一、问题背景:类别不平衡

从模型的训练过程来看

​ 从训练模型的角度来说,如果某类的样本数量很少,那么这个类别所提供的“信息”就太少。
​ 使用经验风险(模型在训练集上的平均损失)最小化作为模型的学习准则。设损失函数为0-1 loss(这是一种典型的均等代价的损失函数),那么优化目标就等价于错误率最小化(也就是accuracy最大化)。考虑极端情况:1000个训练样本中,正类样本999个,负类样本1个。训练过程中在某次迭代结束后,模型把所有的样本都分为正类,虽然分错了这个负类,但是所带来的损失实在微不足道,accuracy已经是99.9%,于是满足停机条件或者达到最大迭代次数之后自然没必要再优化下去,ok,到此为止,训练结束!
​ 于是这个模型没有学习到如何去判别出少数类。

从模型的预测过程来看

​ 考虑二项Logistic回归模型。输入一个样本 x ,模型输出的是其属于正类的概率 y’ 。当 y’>0.5时,模型判定该样本属于正类,否则就是属于反类。
​ 为什么是0.5呢?可以认为模型是出于最大后验概率决策的角度考虑的,选择了0.5意味着当模型估计的样本属于正类的后验概率要大于样本属于负类的后验概率时就将样本判为正类。但实际上,这个后验概率的估计值是否准确呢?
​ 从几率(odds)的角度考虑:几率表达的是样本属于正类的可能性与属于负类的可能性的比值。模型对于样本的预测几率为 y’/(1-y’) 。
​ 模型在做出决策时,当然希望能够遵循真实样本总体的正负类样本分布:设 θ 等于正类样本数除以全部样本数,那么样本的真实几率为 θ/(1−θ) 。当观测几率大于真实几率时,也就是 y’>θ 时,那么就判定这个样本属于正类。
​ 虽然我们无法获悉真实样本总体,但之于训练集,存在这样一个假设:训练集是真实样本总体的无偏采样。正是因为这个假设,所以认为训练集的观测几率 θ/(1−θ) 就代表了真实几率 θ/(1−θ) 。
​ 所以,在这个假设下,当一个样本的预测几率大于观测几率时,就应该将样本判断为正类。

二、解决方法

目前主要有三种办法:

  1. 调整 θ值(也叫再缩放、再平衡、阈值移动、是代价敏感学习的基础)

根据训练集的正负样本比例,调整 θ 值。

这样做的依据是上面所述的对训练集的假设。但在给定任务中,这个假设是否成立,还有待讨论。

  1. 过采样

对训练集里面样本数量较少的类别(少数类)进行过采样,合成新的样本来缓解类不平衡。

下面将介绍一种经典的过采样算法:SMOTE

  1. 欠采样

对训练集里面样本数量较多的类别(多数类)进行欠采样,抛弃一些样本来缓解类不平衡。

三、SMOTE过采样算法

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

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

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

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

										xnew=x+rand(0,1)∗|x−xn|
img

smote算法的伪代码如下:

img

因此,smote算法的思想是合成新的少数类样本,合成的策略是对每个少数类样本a,从它的最近邻中随机选一个样本b,然后在a、b之间的连线上随机选一点作为新合成的少数类样本。

下面具体介绍如何合成新的样本

​ 设训练集的一个少数类的样本数为 T ,那么SMOTE算法将为这个少数类合成 NT 个新样本。这里要求 N 必须是正整数,如果给定的 N<1 那么算法将“认为”少数类的样本数 T=NT ,并将强制 N=1 。 考虑该少数类的一个样本 ii ,其特征向量为 xi,i∈{1,…,T}:

1.首先从该少数类的全部 T 个样本中找到样本 xi 的 k个近邻(例如用欧氏距离),记为 xi(near),near∈{1,…,k};

2.然后从这 k 个近邻中随机选择一个样本 xi(nn) ,再生成一个0 到 1 之间的随机数 ζ1 ,从而合成一个新样本 xi1 :
xi1=xi+ζ1⋅(xi(nn)−xi)

3.将步骤2重复进行 N 次,从而可以合成 NN 个新样本:xinew,new∈1,…,N。

那么,对全部的 T 个少数类样本进行上述操作,便可为该少数类合成 NT个新样本。

如果样本的特征维数是 2维,那么每个样本都可以用二维平面上的一个点来表示。SMOTE算法所合成出的一个新样本 xi1 相当于是表示样本 xi的点和表示样本 xi(nn) 的点之间所连线段上的一个点。所以说该算法是基于“插值”来合成新样本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#SMOTE算法及其python实现
import random
from sklearn.neighbors import NearestNeighbors
import numpy as np
class Smote:
def __init__(self,samples,N=10,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/100)
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=100)
print s.over_sampling()

SMOTE算法的缺陷

​ 该算法主要存在两方面的问题:一是在近邻选择时,存在一定的盲目性。从上面的算法流程可以看出,在算法执行过程中,需要确定K值,即选择多少个近邻样本,这需要用户自行解决。从K值的定义可以看出,K值的下限是M值(M值为从K个近邻中随机挑选出的近邻样本的个数,且有M< K),M的大小可以根据负类样本数量、正类样本数量和数据集最后需要达到的平衡率决定。但K值的上限没有办法确定,只能根据具体的数据集去反复测试。因此如何确定K值,才能使算法达到最优这是未知的。
​ 另外,该算法无法克服非平衡数据集的数据分布问题,容易产生分布边缘化问题。由于负类样本的分布决定了其可选择的近邻,如果一个负类样本处在负类样本集的分布边缘,则由此负类样本和相邻样本产生的“人造”样本也会处在这个边缘,且会越来越边缘化,从而模糊了正类样本和负类样本的边界,而且使边界变得越来越模糊。这种边界模糊性,虽然使数据集的平衡性得到了改善,但加大了分类算法进行分类的难度.

推荐阅读:

解决真实世界问题:如何在不平衡类上使用机器学习?

从重采样到数据合成:如何处理机器学习中的不平衡分类问题?