SMOTE算法深度解析及代码实现

SMOTE算法介绍

 SMOTE算法是较为常用的数据增广算法,其核心思路是在少数类别样本内部进行数据合成,更具体的说,其后隐藏的猜想是假定样本 x 0 , x 1 , . . , x N x_0,x_1,..,x_N x0,x1,..,xN都为同一类别,那么他们的线性组合 x 0 + a 1 ∗ x 1 + . . . + a N ∗ x N x_0+a_1*x_1+...+a_N*x_N x0+a1x1+...+aNxN也应该和它们属于同一类别。 给出SMOTE原论文中的算法伪代码如下:
在这里插入图片描述
其基本思路和刚刚所介绍的一致,但是引入了基于KNN检索的数据合成方案。让我们来一步步拆解。

SMOTE函数

 函数SMOTE(T,N,K)三个参数含义为:
N:int(N/100)生成数据的倍数,为100的倍数。距离而言,如果设置N=200,那么少数类别样本的数目就会增加2倍,变为原来的3倍;
T:少数类别样本的数目
K:用于合成的最近邻样本的数目

 接下来我们分析一下SMOTE的运行逻辑:

  1. 首先会判断要增广的倍数是否大于1(即N是否大于100),基于此计算出增广倍数N=N/100
  2. 遍历每个少数类别样本 x i , i ∈ 1 , . . , T x_i,i\in {1,..,T} xi,i1,..,T,对其进行增广,增广步骤如下:
    1. 计算出 x i x_i xi同类样本(即少数类别样本) 的k个最近邻样本,记录这k个样本在样本集中索引,构成nnarray
    2. 调用Populate()函数进行增广。

Populate函数

 函数Populate(N,i,nnarray)的三个参数的含义为:
N: 增广倍数
i: 要进行增广的样本的索引
nnarray:样本 x i x_i xi的最近邻样本的索引集合

 Popluate函数会按照我们之前提过的SMOTE核心思路–同类样本的线性组合来进行样本的生成。具体而言,总共会执行N次生成过程,每一次生成过程中,我们都会从其最近邻样本中挑选一个,然后对特征进行线性组合作为合成样本。值得注意的是,在合成新样本过程中,不同特征的线性系数都是不同的,特征向量中含numattrs个特征就会包含numattrs个合成系数gap。

小结

 SMOTE函数总结起来就是对每个少数类别样本都寻找其同类最相似的K个样本,然后进行N次样本合成,每次合成过程中都会随机从K个样本中采样一个,然后用采样的近邻样本和当前样本的特征进行线性组合得到新样本,最终得到T*K个增广样本。

代码实现

 实际应用SMOTE算法时,调用imbalanced-learn中的库即可实现,如果想要手动实现的话,可以参见笔者给出的实现方案如下:

def smote(feature,labels,K):
    '''
    这份代码实现的是最为简单的smote算法,也就是核心是同类样本特征的线性组合,
    :param feature: 特征matrix
    :param labels:  标签向量
    :param K: SMOTE算法中样本考量的近邻个数
    :return: new_features-生成的样本特征;new_labels-生成样本的标签
    '''
    from sklearn.neighbors import NearestNeighbors
    from collections import Counter



    value_cnts=dict(Counter(labels))
    max_sampels=max(value_cnts.values())


    new_features=[]
    new_labels=[]
    for label,cnt in value_cnts.items():
        if cnt<=1:
            continue

        # 提取当前label对应的feature

        cur_feats=feature[np.where(labels==label)]

        if(cur_feats.shape[0]>K):
            Neighbors = NearestNeighbors(n_neighbors=K)
            Neighbors.fit(cur_feats)
            neighbor_index=Neighbors.kneighbors()[1]
        else:
            Neighbors = NearestNeighbors(n_neighbors=cur_feats.shape[0]-1)
            Neighbors.fit(cur_feats)
            neighbor_index=Neighbors.kneighbors()[1]

        # 采样max_samples-cnt个样本来进行增广
        base_sampe_idxs=np.random.choice(np.arange(cur_feats.shape[0]),max_sampels-cnt)
        for idx in base_sampe_idxs:
            base_sample=cur_feats[idx]
            # 随机采样一个邻居
            random_neighbor_idx=np.random.choice(neighbor_index[idx])
            neighbor=cur_feats[random_neighbor_idx]
            coff=np.random.rand(feature.shape[1]) # n_dim个位于0-1间的特征系数
            dif=base_sample-neighbor
            new_features.append((base_sample+coff*dif).reshape(1,-1))
            new_labels.append(label)
    new_features=np.vstack(new_features)
    new_labels=np.array(new_labels)
    return new_features,new_labels

需要注意的是,笔者实现的方案和原始SMOTE算法并不完全一致。具体而言,这份实现中舍弃了对增广倍数的控制,改为了强制要求所有类别样本数目相同,逐类别的进行样本合成,每次合成过程中都随机选取一个当前类的样本,然后再基于SMOTE的思路在类别内采样、线性合成。

参考

探索SMOTE算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值