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

转载于:https://www.cnblogs.com/paiandlu/p/8081763.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值