正負樣本比率失衡SMOTE
背景
這幾天測試天池的優惠券預測數據在dnn上面會不會比集成樹有較好的效果,但是正負樣本差距太大,而處理這種情況的一般有欠抽樣和過抽樣,這里主要講過抽樣,過抽樣有一種簡單的方法叫隨機過抽樣,但是隨機過抽樣只是隨機的復制,很容易過擬合,所以SMOTE比較好,SMOTE還有一些改進版本,更好用,這里講一般的SMOTE即可
公式
\(x\) : 任意一個樣本
\(\tilde{x}\) : \(x\)最臨近的\(K\)個樣本的隨機一個,\(x\not= \tilde{x}\)
\[x_{new}=x+rand(0,1)\times(\tilde{x}-x) \]
python實現
import random
from sklearn.neighbors import NearestNeighbors
import numpy as np
class Smote:
def __init__(self,N=1,k=5):
self.__shape=None
self.__N=N
self.__k=k
def fit(self, samples):
self.__shape=samples.shape #源樣本的shape
# 塑形為兩位度才可以用KNN
self.__samples=samples.reshape((self.__shape[0],-1))
self.__tmp_shape=self.__samples.shape
# 返回值的維度
self.__ret_shape=(self.__shape[0]*self.__N,)+self.__shape[1:]
def transform(self):
# 如果沒有喂給數據,則直接返回None
if self.__shape == None:
return None
self.__index=0 # 清零新增數據的索引
self.__X = np.zeros((self.__tmp_shape[0] * self.__N, self.__tmp_shape[1])) # 構造返回的數據,具體數據待填充
neighbors=NearestNeighbors(n_neighbors=self.__k).fit(self.__samples)
for i in range(self.__shape[0]): # 根據每一個樣本產生一個新樣本
# nnarray當前樣本最近k個的樣本的索引
nnarray=neighbors.kneighbors(self.__samples[i].reshape(1,-1),return_distance=False)[0]
# 根據當前樣本索引和,最近k和樣本生成一個新樣本
self.__new_one_sample(i,nnarray)
return self.__X.reshape(self.__ret_shape) # 重新塑形並返回
def fit_transform(self, samples):
self.fit(samples)
return self.transform()
# 根據當前樣本索引和,最近k和樣本生成一個新樣本
def __new_one_sample(self,i,nnarray):
for _ in range(self.__N):
#從K個最近的樣本隨機挑選不同於當前樣本的一個樣本
nn_idx=random.choice(nnarray)
while (nn_idx==i):
nn_idx=random.choice(nnarray)
gap=self.__samples[nn_idx]-self.__samples[i]
prob=random.random()
# 根據公式生成新樣本
self.__X[self.__index]=self.__samples[i]+prob*gap
self.__index+=1
if __name__ == '__main__':
a=np.array([[1,3,4],[2,5,6],[4,1,2],[5,1,4],[3,2,4],[5,3,5]])
print("\n"*2, "測試維度為" , a.shape)
print("*"*100)
s=Smote()
s.fit(a)
print (s.transform())
# 測試多維度支持
b=np.zeros((10,)+a.shape)
print("\n"*2, "測試維度為" , b.shape)
print("*"*100)
for i in range(10):
b[i,:]=s.fit_transform(a)
print (s.fit_transform(b))
代碼的使用方法
假設你已經有label很少的數據 data (不包括label列)
s=Smote()
s.fit(data)
s.transform()
上面的實例是默認參數,可根據情況選擇參數N和k
數據擴增N被,從最近的k個樣本選擇一個樣本參考(這里 參考 這個詞可能不太准確,想不出其他詞)來生成樣本
s=Smote(N=2, k=4)
s.fit(data)
s.transform()
