正負樣本比率失衡SMOTE


正負樣本比率失衡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()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM