多分类样本不均衡--SMOTE方法

SMOTE(合成少数类过采样技术)是一种用于解决样本不平衡问题的算法,通过合成新样本来增加少数类样本数量。它避免了随机过采样的过拟合风险,通过计算少数类样本的k近邻并构造新样本。SMOTE算法包括计算距离、选择近邻和构建新样本等步骤,但存在近邻选择的盲目性和边缘化问题。尽管如此,SMOTE在实践中仍能有效改善模型泛化能力,常与正则化配合使用,适用于随机森林、L2正则化等防止过拟合的模型。
摘要由CSDN通过智能技术生成

(一)过采样算法之SMOTE

https://www.cnblogs.com/wqbin/p/11117616.html

SMOTE(Synthetic Minority Oversampling Technique),合成少数类过采样技术.它是基于随机过采样算法的一种改进方案,由于随机过采样采取简单复制样本的策略来增加少数类样本,这样容易产生模型过拟合的问题,即使得模型学习到的信息过于特别(Specific)而不够泛化(General),SMOTE算法的基本思想是对少数类样本进行分析并根据少数类样本人工合成新样本添加到数据集中,具体如下图所示,算法流程如下。

  • (1)对于少数类中每一个样本x,以欧氏距离为标准计算它到少数类样本集中所有样本的距离,得到其k近邻。 
  • (2)根据样本不平衡比例设置一个采样比例以确定采样倍率N,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为o。 
  • (3)对于每一个随机选出的近邻o,分别与原样本按照公式o(new)=o+rand(0,1)*(x-o)构建新的样本。

或者:

Smote算法的思想其实很简单,先随机选定n个少类的样本,如下图

找出初始扩展的少类样本

再找出最靠近它的m个少类样本,如下图

再任选最临近的m个少类样本中的任意一点,

在这两点上任选一点,这点就是新增的数据样本

复制代码

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from numpy import *
import matplotlib.pyplot as plt
 
#读数据
data = pd.read_table('supermarket_second_man_clothes_train.txt', low_memory=False)

#简单的预处理
test_date = pd.concat([data['label'], data.iloc[:, 7:10]], axis=1)
test_date = test_date.dropna(how='any')

复制代码

结果:

复制代码

test_date.head()
Out[1]: 
   label  max_date_diff  max_pay  cnt_time
0      0           23.0  43068.0        15
1      0           10.0   1899.0         2
2      0          146.0   3299.0        21
3      0           30.0  31959.0        35
4      0            3.0  24165.0        98
test_date['label'][test_date['label']==0].count()/test_date['label'][test_date['label']==1].count()
Out[2]: 67

复制代码

label是样本类别判别标签,0:1=67:1,需要对label=1的数据进行扩充

复制代码

# 筛选目标变量
aimed_date = test_date[test_date['label'] == 1]
# 随机筛选少类扩充中心
index = pd.DataFrame(aimed_date.index).sample(frac=0.1, random_state=1)
index.columns = ['id']
number = len(index)
# 生成array格式
aimed_date_new = aimed_date.ix[index.values.ravel(), :]

复制代码

随机选取了全量少数样本的10%作为数据扩充的中心点

复制代码

# 自变量标准化
sc = StandardScaler().fit(aimed_date_new)
aimed_date_new = pd.DataFrame(sc.transform(aimed_date_new))
sc1 = StandardScaler().fit(aimed_date)
aimed_date = pd.DataFrame(sc1.transform(aimed_date))

# 定义欧式距离计算
def dist(a, b):
    a = array(a)
    b = array(b)
    d = ((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2 + (a[3] - b[3]) ** 2) ** 0.5
    return d

复制代码

下面定义距离计算的方式,所有算法中,涉及到距离的地方都需要标准化去除冈量,也同时加快了计算的速度
这边采取了欧式距离的方式

复制代码

# 统计所有检验距离样本个数
row_l1 = aimed_date_new.iloc[:, 0].count()
row_l2 = aimed_date.iloc[:, 0].count()
a = zeros((row_l1, row_l2))
a = pd.DataFrame(a)
# 计算距离矩阵
for i in range(row_l1):
    for j in range(row_l2):
        d = dist(aimed_date_new.iloc[i, :], aimed_date.iloc[j, :])
        a.ix[i, j] = d
b = a.T.apply(lambda x: x.min())

复制代码

调用上面的计算距离的函数,形成一个距离矩阵

复制代码

# 找到同类点位置
h = []
z = []
for i in range(number):
    for j in range(len(a.iloc[i, :])):
        ai = a.iloc[i, j]
        bi = b[i]
        if ai == bi:
            h.append(i)
            z.append(j)
        else:
            continue
new_point = [0, 0, 0, 0]
new_point = pd.DataFrame(new_point)
for i in range(len(h)):
    index_a = z[i]
    new = aimed_date.iloc[index_a, :]
    new_point = pd.concat([new, new_point], axis=1)

new_point = new_point.iloc[:, range(len(new_point.columns) - 1)]

复制代码

再找到位置的情况下,再去原始的数据集中根据位置查找具体的数据

复制代码

import random
r1 = []
for i in range(len(new_point.columns)):
    r1.append(random.uniform(0, 1))
new_point_last = []
new_point_last = pd.DataFrame(new_point_last)
# 求新点 new_x=old_x+rand()*(append_x-old_x)
for i in range(len(new_point.columns)):
    new_x = (new_point.iloc[1:4, i] - aimed_date_new.iloc[number - 1 - i, 1:4]) * r1[i] + aimed_date_new.iloc[number - 1 - i, 1:4]
    new_point_last = pd.concat([new_point_last, new_x], axis=1)
print new_point_last

复制代码

最后,再根据smote的计算公式new_x=old_x+rand()*(append_x-old_x),计算出新的点即可。

smote算法的伪代码如下:

复制代码

import random
from sklearn.neighbors import NearestNeighbors
import numpy as np


class Smote:
    def __init__(self,samples,N=1,k=5):
        self.n_samples,self.n_attrs=samples.shape
        self.N=N
        self.k=k
        self.samples=samples
        self.newindex=0
       # self.synthetic=np.zeros((self.n_samples*N,self.n_attrs))


    def over_sampling(self):
        N=int(self.N)
        self.synthetic = np.zeros((self.n_samples * N, self.n_attrs))
        neighbors=NearestNeighbors(n_neighbors=self.k).fit(self.samples)
        print('neighbors',neighbors)
        for i in range(len(self.samples)):
            nnarray=neighbors.kneighbors(self.samples[i].reshape(1,-1),return_distance=False)[0]
            #print nnarray
            self._populate(N,i,nnarray)
        return self.synthetic
    
    # for each minority class samples,choose N of the k nearest neighbors and generate N synthetic samples.
    def _populate(self,N,i,nnarray):
        for j in range(N):
            nn=random.randint(0,self.k-1)
            dif=self.samples[nnarray[nn]]-self.samples[i]
            gap=random.random()
            self.synthetic[self.newindex]=self.samples[i]+gap*dif
            self.newindex+=1
a=np.array([[1,2,3],[4,5,6],[2,3,1],[2,1,2],[2,3,4],[2,3,4]])
s=Smote(a,N=2)              #a为少数数据集,N为倍率,即从k-邻居中取出几个样本点
print(s.over_sampling())

复制代码

SMOTE算法的缺陷

该算法主要存在两方面的问题:一是在近邻选择时,存在一定的盲目性。从上面的算法流程可以看出,在算法执行过程中,需要确定K值,即选择多少个近邻样本,这需要用户自行解决。从K值的定义可以看出,K值的下限是M值(M值为从K个近邻中随机挑选出的近邻样本的个数,且有M< K),M的大小可以根据负类样本数量、正类样本数量和数据集最后需要达到的平衡率决定。但K值的上限没有办法确定,只能根据具体的数据集去反复测试。因此如何确定K值,才能使算法达到最优这是未知的。

另外,该算法无法克服非平衡数据集的数据分布问题,容易产生分布边缘化问题。由于负类样本的分布决定了其可选择的近邻,如果一个负类样本处在负类样本集的分布边缘,则由此负类样本和相邻样本产生的“人造”样本也会处在这个边缘,且会越来越边缘化,从而模糊了正类样本和负类样本的边界,而且使边界变得越来越模糊。这种边界模糊性,虽然使数据集的平衡性得到了改善,但加大了分类算法进行分类的难度.

针对SMOTE算法的进一步改进

针对SMOTE算法存在的边缘化和盲目性等问题,很多人纷纷提出了新的改进办法,在一定程度上改进了算法的性能,但还存在许多需要解决的问题。

Han等人Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning在SMOTE算法基础上进行了改进,提出了Borderhne.SMOTE算法,解决了生成样本重叠(Overlapping)的问题该算法在运行的过程中,查找一个适当的区域,该区域可以较好地反应数据集的性质,然后在该区域内进行插值,以使新增加的“人造”样本更有效。这个适当的区域一般由经验给定,因此算法在执行的过程中有一定的局限性。

(二)SMOTE算法解决样本不平衡

https://www.cnblogs.com/limingqi/p/12576875.html

首先,看下Smote算法之前,我们先看下当正负样本不均衡的时候,我们通常用的方法:

  • 抽样
    常规的包含过抽样、欠抽样、组合抽样
    过抽样:将样本较少的一类sample补齐
    欠抽样:将样本较多的一类sample压缩
    组合抽样:约定一个量级N,同时进行过抽样和欠抽样,使得正负样本量和等于约定量级N

这种方法要么丢失数据信息,要么会导致较少样本共线性,存在明显缺陷

  • 权重调整
    常规的包括算法中的weight,weight matrix
    改变入参的权重比,比如boosting中的全量迭代方式、逻辑回归中的前置的权重设置

这种方式的弊端在于无法控制合适的权重比,需要多次尝试

  • 核函数修正
    通过核函数的改变,来抵消样本不平衡带来的问题

这种使用场景局限,前置的知识学习代价高,核函数调整代价高,黑盒优化

  • 模型修正
    通过现有的较少的样本类别的数据,用算法去探查数据之间的特征,判读数据是否满足一定的规律
    比如,通过线性拟合,发现少类样本成线性关系,可以新增线性拟合模型下的新点

实际规律比较难发现,难度较高

算法基本原理:

(1)对于少数类中每一个样本x,以欧氏距离为标准计算它到少数类样本集中所有样本的距离,得到其k近邻。

(2)根据样本不平衡比例设置一个采样比例以确定采样倍率N,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为xn。

(3)对于每一个随机选出的近邻xn,分别与原样本按照如下的公式构建新的样本。


 

Smote算法的思想其实很简单,先随机选定n个少类的样本,如下图

 再找出最靠近它的m个少类样本,如下图

再任选最临近的m个少类样本中的任意一点,

 

 伪代码:

function SMOTE(T, N, k)

  Input: T; N; k # T:少数类样本数目

                 # N:过采样的数目

                 # K:最近邻的数量

  Output: (N/100) * T  # 合成的少数类样本

  Variables: Sample[][] # 存放原始少数类样本

             newindex #控制合成数量

initialized to 0;

             Synthetic[][]

    if N < 100 then

        Randomize the T minority class samples

        T = (N/100)*T

        N = 100

    end if

    N = (int)N/100 # 假定SMOTE的数量是100的整数倍

    for i = 1 to T do

        Compute k nearest neighbors for i, and save the indices in the nnarray

       POPULATE(N, i, nnarray)

   end for

 end function

Algorithm 2 Function to generate synthetic samples

 function POPULATE(N, i, nnarray)

  Input: N; i; nnarray  # N:生成的样本数量

                        # i:原始样本下标

                        # nnarray:存放最近邻的数组

  Output: N new synthetic samples in Synthetic array

     while N != 0 do

         nn = random(1,k)

         for attr = 1 to numattrs do # numattrs:属性的数量

             Compute: dif = Sample[nnarray[nn]][attr] − Sample[i][attr]

             Compute: gap = random(0, 1)

             Synthetic[newindex][attr] = Sample[i][attr] + gap * dif

         end for

         newindex + +

        N − −

    end while

 end function

项目经验:

1.采样方法比直接调整阈值的方法要好。

2.使用采样方法可以提高模型的泛化能力,存在过拟合风险,最好联合正则化同时使用。

3.过采样结果比欠采样多数时间稳定,但是还是要具体问题具体分析,主要还是看数据的分布,SMOTE效果还是不错的。

4.和SMOTE算法使用的模型最好是可以防止过拟合的模型,随机森林,L2正则+逻辑回归,XGBoost等模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值