提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
- 一、介绍
- 1、过采样
- 2、欠采样
- 二、过采样
- 1、SMOTE(常用)
- 1、算法流程
- 2、算法实现
- 3、参数介绍
- 2、ADASYN(不常用)
- 1、算法流程:
- 2、代码实现
- 三、欠采样
- 1、Random under-sampling
- 2、Cleaning under-sampling
- 1、TomekLinks
- 2、Edited nearest neighbours
- 四、联合采样
- 1、SMOTE叠加TomekLink
- 2、SMOTE叠加ENN
- 五、实例
- 1、加载数据
- 2、实现过采样
- 3、问题
- 4、SMOTENC
一、介绍
研究算法时均认为数据是对称分布的,即正负样本数据相当。现实数据中少数类占比20%,甚至10%都不到,容易对模型算法产生影响。
1、过采样
过采样会随机复制少数样本以增大它们的规模。
2、欠采样
通过抽样方法,将多数样本变少。解决二分类问题时,可以使用随机抽样方法降低样本数量;解决多分类问题时,使用分层抽样方法。
二、过采样
1、SMOTE(常用)
- 错误说法
先设置超参数,如4,黑色样本为少数类,每个黑色样本中找到黑色附近的4个黑色样本,在该4个样本的连线中间取一个随机差值,记为新增样本。
- 正确说法
先设置超参数,如4,黑色样本为少数类,每个黑色样本中找到黑色附近的4个黑色样本,对这四个样本做有放回的随机抽样,然后找到黑色样本与随机抽中样本,并随机取差值,生成一个样本。
1、算法流程
1、计算采样比例N,(默认N= 多数样本量/少数样本量)
2、 对每一个少数类样本
x
i
x_i
xi,从其k近邻个少数类样本中有放回地随机抽样N-1个,记为
x
i
k
x_{ik}
xik。
3、分别与原样本
x
i
x_i
xi按照如下的公式生成新的插值样本
x
n
e
w
=
x
i
+
r
a
n
d
(
0
,
1
)
∗
(
x
i
−
x
i
k
)
x_{new}=x_i+rand(0,1)*(x_i-x_{ik})
xnew=xi+rand(0,1)∗(xi−xik)
4、重复进行,直到满足停止条件。
2、算法实现
- 创建不平衡数据集
- 实现SMOTE
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state = 42)
X_sm, Y_sm = smote.fit_resample(X, y)
# 查看采样后的比例
pd.DataFrame(Y_sm).value_counts()
# 画图看过采样后的数据集
plt.scatter(X_sm[:, 0], X_sm[:, 1], c = Y_sm, cmap = "rainbow",s = 10)
- 调整采样比例
smote = SMOTE(sampling_strategy = 0.5, random_state = 42)
X_sm, Y_sm = smote.fit_resample(X, y)
3、参数介绍
sampling_strategy: default= "auto" 过采样策略,可以为0.5,即调整样本比例为0.5倍;minority:只过采样类别比例最少的样本,多分类时用;not_minority出了最少、最多的样本,其他类别过采样,过采样到和最多类别的样本数量一样多;not_majority:除了最多的样本,其他都进行过采样;all:所有都过采样;也可以传入字典控制过采样的key(类别)、value(数量).
k_neighbors: int = 5, k值
优点:简单清晰,用差值做出的结果,比较随机;缺点:如果一个分类器在原始的少数类数据集上做出了一个错误的负面错误,那么数据复制几次后,分类器就会在新的数据集上出现多次错误。由于生成的是虚拟数据,在实际中,不容易解释。
2、ADASYN(不常用)
1、算法流程:
1、对于每一个少数类样本
x
i
x_i
xi,选择其k个近邻的样本
x
i
k
x_{ik}
xik。注意这里的k个近邻样本的选择不区分多数与少数类。
2、计算这K个近邻样本中多数类的比例
r
i
r_i
ri
3、标准化
r
i
r_i
ri得到采样比例,公式为
r
i
^
=
r
i
/
∑
i
=
1
m
r
i
\hat{r_i}=r_i/\sum_{i=1}^m{r_i}
ri^=ri/∑i=1mri。标准化后使
∑
i
r
i
^
=
1
\sum_i\hat{r_i}=1
∑iri^=1。
4、 对每个少数类样本,依照采样比例
r
i
^
\hat{r_i}
ri^使用SMOTE方法得到插值样本。可以发现,ADASYN方法更倾向于在多数类样本的附近生成新的插值样本。
2、代码实现
from imblearn.over_sampling import ADASYN
adasyn = ADASYN(random_state = 42)
X_ada, Y_ada = adasyn.fit_resample(X, y)
pd.DataFrame(Y_ada).value_counts()
plt.scatter(X_ada[:, 0], X_ada[:, 1], c = Y_ada, cmap = "rainbow",s = 10)
三、欠采样
1、Random under-sampling
常用的是Random under-sampling,即随机删除多数类样本。这是唯一一种可以精确设置欠采样样本数量的常用欠采样算法,因此冠以Controlled名号,即Controlled under-sampling(控制过采样)。
from imblearn.under_sampling import RandomUnderSampler
rus= RandomUnderSampler(sampling_strategy= 0.5, random_state= 42) # sampling_strategy=1,控制采样比例
X_rus, Y_rus = rus.fit_resample(X, y)
# 查看采样后的比例
pd.DataFrame(Y_rus).value_counts()
# 画图看过采样后的数据集
plt.scatter(X_rus[:, 0], X_rus[:, 1], c = Y_rus, cmap = "rainbow",s = 10)
2、Cleaning under-sampling
Cleaning类的欠采样算法无法精确控制欠采样样本的数量
1、TomekLinks
- 算法过程
当多数类的某样本y和少数类的某样本x,与另外任意样本z,之间的距离d()存在以下关系
d
(
x
,
y
)
<
d
(
x
,
z
)
a
n
d
d
(
x
,
y
)
<
d
(
y
,
z
)
d(x,y)<d(x,z)\qquad and\qquad d(x,y)<d(y,z)
d(x,y)<d(x,z)andd(x,y)<d(y,z)
则称x与y之间存在Tomek Link,此时算法可删除多数类样本y以实现欠采样。
可以发现,TomekLinks算法不适合对有清晰边界的两类样本做欠采样。
- 代码实现
from imblearn.under_sampling import TomekLinks
tl= TomekLinks()
X_tl, Y_tl= tl.fit_resample(X, y)
# 查看采样后的比例
pd.DataFrame(Y_tl).value_counts()
# 画图看过采样后的数据集
plt.scatter(X_tl[:, 0], X_tl[:, 1], c = Y_tl, cmap = "rainbow",s = 10)
2、Edited nearest neighbours
- 算法过程
应用最近邻算法来编辑(edit)数据集, 找出那些与邻居不太友好的样本然后移除。
具体来说,对每一个多数类样本 x i x_i xi,若其k近邻的全部/大多数样本是多数类,则该多数类样本 x i x_i xi会被保留,否则会被移除。 - 代码实现
from imblearn.under_sampling import EditedNearestNeighbours
enn= EditedNearestNeighbours(n_neighbors= 42) # 改变n_neighbors=3 参数以观察欠采样结果
X_enn, Y_enn= enn.fit_resample(X, y)
# 查看采样后的比例
pd.DataFrame(Y_enn).value_counts()
# 画图看过采样后的数据集
plt.scatter(X_enn[:, 0], X_enn[:, 1], c = Y_enn, cmap = "rainbow",s = 10)
四、联合采样
联合采样(Combination sampling)如果多数类样本有离群值,SMOTE等过采样方法容易在多数类样本中间错误地插入少数类插值,导致类别重叠。这些问题插值可以通过联合使用欠采样方法来解决。 这里我们分别将Tomeks link和Edited nearest-neighbours联接在SMOTE之后,来清除过采样生成的问题插值,以得到一个尽量干净的数据空间。
数据处理方法一直是先过采样再欠采样,顺序不可反。
1、SMOTE叠加TomekLink
代码实现:
from imblearn.combine import SMOTETomek
smote_tomek = SMOTETomek(random_state= 42)
X_smote_tomek, Y_smote_tomek= smote_tomek.fit_resample(X, y)
# 查看采样后的比例
pd.DataFrame(Y_smote_tomek).value_counts()
# 画图看过采样后的数据集
plt.scatter(X_smote_tomek[:, 0], X_smote_tomek[:, 1], c = Y_smote_tomek, cmap = "rainbow",s = 10)
2、SMOTE叠加ENN
代码实现
from imblearn.combine import SMOTEENN
smote_enn = SMOTEENN(random_state= 42)
X_smote_enn, Y_smote_enn= smote_enn.fit_resample(X, y)
# 查看采样后的比例
pd.DataFrame(Y_smote_enn).value_counts()
# 画图看过采样后的数据集
plt.scatter(X_smote_enn[:, 0], X_smote_enn[:, 1], c = Y_smote_enn, cmap = "rainbow",s = 10)
五、实例
1、加载数据
df=pd.read_csv('保险数据_全部数据.csv')
df1= df.dropna()
labels= df1.pop("resp_flag")
# 数据类型区分
cat_cols= df1.select_dtypes(include=["object"]) # 分类型变量
num_cols= df1.select_dtypes(include=["int", "float"]) # 数值型变量
# 数据编码
from sklearn.preprocessing import OrdinalEncoder
cat_encode= OrdinalEncoder()
cat_trans= cat_encode.fit_transform(cat_cols)
df_cat= pd.DataFrame(cat_trans, columns= cat_cols.columns)
# 数据标准化
from sklearn.preprocessing import StandardScaler
num_std= StandardScaler()
num_trans= num_std.fit_transform(num_cols)
df_num= pd.DataFrame(num_trans, columns= num_cols.columns)
# 数据合并
data= pd.concat([df_cat, df_num], axis= 1)
2、实现过采样
实现过采样,并查看原数据比例
# SMOTE
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state = 42)
X_sm, Y_sm = smote.fit_resample(data, labels)
# 查看采样后的比例
pd.DataFrame(Y_sm).value_counts()
3、问题
第一列性别分类数据,0、1之外出现小数,这是因为SMOTE做差值导致。如果模型做预测,不需要可解释性,则可以继续使用该方法;如果需要满足可解释性,数据中存在连续型数据时无问题,如果是分类数据,则出现的新增样本无法解释。
4、SMOTENC
SMOTENC会自动区分数值型、分类型数据,避免了SMOTE数据无法解释的问题。
from imblearn.over_sampling import SMOTENC
smote_nc = SMOTENC(categorical_features= cat_cols.columns) # categorical_features参数传入分类型数据集的列名
X_resampled, y_resampled = smote_nc.fit_resample(data.values, labels)
新问题:大部分电脑运行这部分代码会保错,原因是Python版本间更新出现重大变动时,第三方库未同时更新导致。