原文地址 mp.weixin.qq.com
不均衡数据集是指 类别散布重大偏斜
的数据集,例如少数类与少数类的样本比例为 1:100 或 1:1000。
训练集中的这种偏差会影响许多机器学习算法,甚至导致齐全疏忽少数类,容易导致模型过拟合,泛化能力差。
所以,针对类别散布不平衡的数据集,个别会采取采样的形式,使得类别散布绝对平衡,晋升模型泛化能力。
上面介绍几种常见的采样办法及其原理,均是基于 imbalanced-learn
的实现:
1、奢侈随机采样
随机采样:
- 随机过采样:从少数类中随机抉择示例,并进行替换,而后将它们增加到训练数据集中;
- 随机欠采样:从少数类中随机抉择示例,并将它们从训练数据集中删除;
奢侈重采样,对数据没有任何假如,也没有应用启发式办法
。所以,易于实现且执行速度快,这对于十分大和简单的数据集来说是 ok 的。
需注意的是,对类散布的更改仅实用于训练数据集
,目标是优化模型的拟合;重采样不适用于评估模型性能的测试集。
2、随机过采样
这种技术对于受偏态散布影响并且给定类的多个反复示例会影响模型拟合的机器学习算法十分无效。
这可能包含迭代学习系数的算法,例如应用随机梯度降落的人工神经网络,它还可能影响寻求数据良好拆分的模型,例如反对向量机和决策树。
调整指标类散布可能很有用,但在某些状况下,为重大不均衡的数据集寻求均衡散布(因为它会准确复制少数类示例),可能会导致受影响的算法适度拟合少数类,从而导致泛化误差减少。最好在过采样后监控训练和测试数据集的性能,并将后果与原始数据集上的雷同算法进行比拟。
# define oversampling strategy
oversample = RandomOverSampler(sampling_strategy='minority')
指定一个浮点值,指定转换数据集中少数类与少数类示例的比率。比方,对于二元分类问题,假如对少数类进行过采样,以使大多数类的示例数量缩小一半,如果少数类有 1,000 个示例,而少数类有 100 个,则转换后的数据集将有 500 个少数类的示例。
# define oversampling strategy
oversample = RandomOverSampler(sampling_strategy=0.5)
# fit and apply the transform
X_over, y_over = oversample.fit_resample(X, y)
残缺示例:应用 3 个反复的 10 折穿插验证进行评估,并在每 1 折内别离对训练数据集执行过采样
# example of evaluating a decision tree with random oversamplingfrom numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.tree import DecisionTreeClassifier
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import RandomOverSampler
# define dataset
X, y = make_classification(n_samples=10000, weights=[0.99], flip_y=0)
# define pipeline
steps = [('over', RandomOverSampler()), ('model', DecisionTreeClassifier())]
pipeline = Pipeline(steps=steps)
# evaluate pipeline
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(pipeline, X, y, scoring='f1_micro', cv=cv, n_jobs=-1)
score = mean(scores)
print('F1 Score: %.3f' % score)
3、随机欠采样
随机欠采样波及从少数类中随机抉择示例,并从训练集中删除。
欠采样的一个限度是,删除少数类中可能有用、重要或可能对拟合持重决策边界至关重要的示例(一不小心把重要数据删了~~)。
鉴于示例是随机删除的,因而无奈从少数类中检测或保留好的或蕴含更多信息的示例。数据的失落会使多数和少数实例之间的决策边界更难学习,从而导致分类性能降落。
# define undersample strategy
undersample = RandomUnderSampler(sampling_strategy='majority')
..
# define undersample strategy
undersample = RandomUnderSampler(sampling_strategy=0.5)
# fit and apply the transform
X_over, y_over = undersample.fit_resample(X, y)
4、随机过采样与欠采样的联合
能够对少数类采纳适度的过采样以改善对这些示例的偏差,同时也能够对少数类采纳适度的欠采样以缩小对该类的偏差。与独自执行一种采样相比,这能够进步模型整体性能。
例如,咱们有一个类别散布为 1:100 的数据集,可能首先利用过采样,通过复制少数类的示例来将比例进步到 1:10,而后利用欠采样,通过从少数类中删除示例,将比例进一步提高到 1:2。
# define pipeline
over = RandomOverSampler(sampling_strategy=0.1)
under = RandomUnderSampler(sampling_strategy=0.5)
steps = [('o', over), ('u', under), ('m', DecisionTreeClassifier())]
pipeline = Pipeline(steps=steps)
# example of evaluating a model with random oversampling and undersampling
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.tree import DecisionTreeClassifier
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler
# define dataset
X, y = make_classification(n_samples=10000, weights=[0.99], flip_y=0)
# define pipeline
over = RandomOverSampler(sampling_strategy=0.1)
under = RandomUnderSampler(sampling_strategy=0.5)
steps = [('o', over), ('u', under), ('m', DecisionTreeClassifier())]
pipeline = Pipeline(steps=steps)
# evaluate pipeline
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(pipeline, X, y, scoring='f1_micro', cv=cv, n_jobs=-1)
score = mean(scores)
print('F1 Score: %.3f' % score)
5、其余几类过采样
5.1、SMOTE
RandomOverSampler 通过复制少数类的一些原始样本进行过采样,而 SMOTE 通过插值生成新样本。
从现有示例中合成新示例
,这是少数类 数据加强
的一种类型,被称为合成少数类过采样技术,简称SMOTE。
SMOTE 的工作原理是抉择特色空间中靠近的示例,在特色空间中的示例之间绘制一条线,并在该线的某个点处绘制一个新样本。
- SMOTE 首先随机抉择一个少数类实例 a 并找到它的 k 个最近的少数类街坊
- 而后通过随机抉择 k 个最近邻 b 中的一个并连贯 a 和 b,以在特色空间中造成线段来创立合成实例,合成实例是作为两个选定实例 a 和 b 的凸组合生成的。
倡议首先应用随机欠采样来修剪少数类中的示例数量,而后应用 SMOTE 对少数类进行过采样以均衡类散布。SMOTE 和欠采样的组合比一般欠采样体现更好
。(对于 SMOTE 的原始论文倡议将 SMOTE 与少数类的随机欠采样联合起来)
广泛毛病在于,创立合成示例时没有思考少数类,如果类有很强的重叠,可能会导致示例不明确。
# Oversample and plot imbalanced dataset with SMOTEfrom collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
from matplotlib import pyplot
from numpy import where
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# summarize class distribution
counter = Counter(y)
print(counter)
# transform the dataset
oversample = SMOTE()
X, y = oversample.fit_resample(X, y)
# summarize the new class distribution
counter = Counter(y)
print(counter)
# scatter plot of examples by class label
for label, _ in counter.items():
row_ix = where(y == label)[0]
pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(label))
pyplot.legend()
pyplot.show()
先对少数类 smote 过采样,再样对少数类欠采样:
# Oversample with SMOTE and random undersample for imbalanced datasetfrom collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
from imblearn.pipeline import Pipeline
from matplotlib import pyplot
from numpy import where
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# summarize class distribution
counter = Counter(y)
print(counter)
# define pipeline
over = SMOTE(sampling_strategy=0.1)
under = RandomUnderSampler(sampling_strategy=0.5)
steps = [('o', over), ('u', under)]
pipeline = Pipeline(steps=steps)
# transform the dataset
X, y = pipeline.fit_resample(X, y)
# summarize the new class distribution
counter = Counter(y)
print(counter)
# scatter plot of examples by class label
for label, _ in counter.items():
row_ix = where(y == label)[0]
pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(label))
pyplot.legend()
pyplot.show()
能够比照采样前和采样后的模型性能(这里采样 AUC):
# decision tree on imbalanced dataset with SMOTE oversampling and random undersamplingfrom numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.tree import DecisionTreeClassifier
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# define pipeline
model = DecisionTreeClassifier()
over = SMOTE(sampling_strategy=0.1)
under = RandomUnderSampler(sampling_strategy=0.5)
steps = [('over', over), ('under', under), ('model', model)]
pipeline = Pipeline(steps=steps)
# evaluate pipeline
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(pipeline, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
print('Mean ROC AUC: %.3f' % mean(scores))
还能够通过调整 SMOTE 的 k 最近邻的不同值(默认是 5):
# grid search k value for SMOTE oversampling for imbalanced classificationfrom numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.tree import DecisionTreeClassifier
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# values to evaluate
k_values = [1, 2, 3, 4, 5, 6, 7]
for k in k_values:
# define pipeline
model = DecisionTreeClassifier()
over = SMOTE(sampling_strategy=0.1, k_neighbors=k)
under = RandomUnderSampler(sampling_strategy=0.5)
steps = [('over', over), ('under', under), ('model', model)]
pipeline = Pipeline(steps=steps)
# evaluate pipeline
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(pipeline, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
score = mean(scores)
print('> k=%d, Mean ROC AUC: %.3f' % (k, score))
5.2、Borderline-SMOTE
SMOTE 过于随机了,从少数类中随机抉择一个样本 a,找到 K 近邻后,再从近邻中随机抉择一个样本 b,连贯样本 a,b,抉择 ab 直线上一点 de 作为过采样点。这样很容易生成谬误类样本,生成的样本进入到少数类中去了。
直觉:边界上的示例和左近的示例比远离边界的示例更容易被谬误分类,因而对分类更重要。
这些谬误分类的示例可能是不置可否的,并且位于决策边界的边缘或边界区域中,类别成员可能重叠。因而,这种批改为 SMOTE 的办法称为 Borderline-SMOTE。
Borderline-SMOTE 办法仅在两个类之间的决策边界上创立合成示例,而不是自觉地为少数类生成新的合成示例。
# borderline-SMOTE for imbalanced datasetfrom collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import BorderlineSMOTE
from matplotlib import pyplot
from numpy import where
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# summarize class distribution
counter = Counter(y)
print(counter)
# transform the dataset
oversample = BorderlineSMOTE()
X, y = oversample.fit_resample(X, y)
# summarize the new class distribution
counter = Counter(y)
print(counter)
# scatter plot of examples by class label
for label, _ in counter.items():
row_ix = where(y == label)[0]
pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(label))
pyplot.legend()
pyplot.show()
5.3、Borderline-SMOTE SVM
与 Borderline-SMOTE 不同的是,应用 SVM 算法
而不是 KNN 来辨认决策边界上的谬误分类示例。
# borderline-SMOTE with SVM for imbalanced datasetfrom collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import SVMSMOTE
from matplotlib import pyplot
from numpy import where
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# summarize class distribution
counter = Counter(y)
print(counter)
# transform the dataset
oversample = SVMSMOTE()
X, y = oversample.fit_resample(X, y)
# summarize the new class distribution
counter = Counter(y)
print(counter)
# scatter plot of examples by class label
for label, _ in counter.items():
row_ix = where(y == label)[0]
pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(label))
pyplot.legend()
pyplot.show()
与 Borderline-SMOTE 不同,更多的示例是在远离类重叠区域的中央合成的,例如图的左上角。
6、自适应合成采样 (ADASYN)
通过生成与少数类中示例的密度成反比的合成样本进行过采样
也就是说,在特色空间的多数样本密度低的区域生成更多合成样本,而在密度高的区域生成更少或不生成合成样本。
ADASYN 算法的要害思维是,应用密度散布作为规范,主动决定须要为每个多数数据示例生成的合成样本的数量。
ADASYN 基于依据散布自适应生成多数数据样本的思维:与那些更容易学习的多数样本相比,为更难学习的少数类样本生成更多的合成数据。
# Oversample and plot imbalanced dataset with ADASYNfrom collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import ADASYN
from matplotlib import pyplot
from numpy import where
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# summarize class distribution
counter = Counter(y)
print(counter)
# transform the dataset
oversample = ADASYN()
X, y = oversample.fit_resample(X, y)
# summarize the new class distribution
counter = Counter(y)
print(counter)
# scatter plot of examples by class label
for label, _ in counter.items():
row_ix = where(y == label)[0]
pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(label))
pyplot.legend()
pyplot.show()
值得注意的一点是,在进行采样的时候,要留神数据泄露问题。
在将整个数据集拆分为训练分区和测试分区之前对整个数据集进行重采样,这就会导致数据泄露。
- 该模型不会在类散布相似于实在用例的数据集上进行测试。通过对整个数据集进行从新采样,训练集和测试集都可能是均衡的,然而模型应该在不均衡的数据集上进行测试,以评估模型的潜在偏差;
- 重采样过程可能会应用无关数据集中样本的信息来生成或抉择一些样本。因而,咱们可能会应用样本信息,这些信息将在当前用作测试样本。
References:
- https://imbalanced-learn.org/…
- https://zhuanlan.zhihu.com/p/…
欢送关注集体公众号:DS 数说