SMOTE详解
一、什么是SMOTE?为什么要使用它?
1、核心问题:类别不平衡
SMOTE(Synthetic Minority Over-sampling Technique,合成少数类过采样技术),在机器学习中,我们经常会遇到“类别不平衡”的数据集。例如:
- 金融风控:绝大多数交易是正常的,只有极少数是欺诈交易。
- 医疗诊断:绝大多数样本是健康的,只有少数是患病个体。
- 工业质检:绝大部分产品是良品,只有少量是次品。
在这些场景下,如果直接用原始数据训练模型,模型会倾向于“讨好”多数类(即把所有样本都预测为多数类),因为这样也能获得很高的准确率。但对于我们真正关心的少数类,模型的预测效果会非常差。
2、传统方法的局限性
在SMOTE提出之前,解决类别不平衡的简单方法主要是:
- 过采样:随机复制少数类样本。
- 缺点:容易导致模型过拟合。因为只是简单复制样本,模型学到的信息没有增加,只是反复看了几遍相同的样本,对噪声和异常值会更敏感。
- 欠采样:随机删除多数类样本。
- 缺点:会丢失多数类中包含的重要信息,可能影响模型的性能。
3、SMOTE的诞生
SMOTE由Nitesh Chawla等人于2002年提出。它的核心思想非常巧妙:不是简单地复制少数类样本,而是通过“插值”来合成新的、人工的少数类样本。 这种方法可以有效地扩大少数类的决策区域,从而让模型学习到更鲁棒、更泛化的规律。
二、 SMOTE算法原理详解
SMOTE的核心是“插值”。想象一下在二维平面上,我们有两个很接近的少数类样本点,那么在这两个点之间的连线上,新生成的点也很有可能是合理的少数类样本。
1、 算法步骤(假设针对少数类S_min):
- 对于少数类中的每一个样本x_i:
- a. 计算x_i在少数类样本集合中的k个最近邻(通常使用欧氏距离,k通常取5)。
- b. 从这k个最近邻中,随机选择一个样本,记为x_zi。
- 合成新样本:
- 在x_i和x_zi之间的连线上,随机选择一个点作为新样本。
- 具体公式为:
x_new = x_i + λ * (x_zi - x_i) - 其中,λ是一个在[0, 1]区间内随机生成的数。
- 当λ=0时,x_new就是x_i。
- 当λ=1时,x_new就是x_zi。
- 当λ=0.5时,x_new就是x_i和x_zi的中点。
- 重复步骤1和2,直到少数类的样本数量达到预期(例如,与多数类样本数量相等)。
三、在Python中的实现
使用imbalanced-learn这个专门的库可以非常方便地实现SMOTE。
1、安装与导入、语法
pip install imbalanced-learn
from imblearn.over_sampling import SMOTE
smote = SMOTE(sampling_strategy='auto',random_state=None,k_neighbors=5,n_jobs=None
)
2、参数详解
| 参数/方法 | 说明 | 常用值 |
|---|---|---|
sampling_strategy | 核心,控制重采样目标和程度 | 'auto'(默认), float(如0.8), dict(最灵活) |
k_neighbors | 控制生成新样本时考虑的近邻数 | 5(默认),样本少时可调小,样本多噪声少时可调大 |
random_state | 确保结果可重现 | 42, None |
n_jobs | 并行计算,加速k近邻搜索 | -1(用全部核心),大数据集时有用 |
fit_resample(X, y) | 最常用方法,一次性完成拟合和重采样 | 返回 X_resampled, y_resampled |
3、方法详解
fit(X, y)
- 作用:仅“拟合”重采样器到数据,计算生成样本所需的统计信息(如每个类需要生成多少样本),但并不实际重采样。
- 返回值:返回拟合后的重采样器对象本身。
- 用途:通常不单独使用,而是与 resample方法联用。fit_resample方法等价于先调用 fit再调用 resample。
resample(X, y)
- 作用:根据 fit方法计算出的信息,执行实际的重采样。
- 前提:必须先调用 fit方法。
- 返回值:返回重采样后的 (X_resampled, y_resampled)。
fit_resample(X, y)
这是最核心、最常用的方法。它一次性完成“拟合”和“重采样”两个步骤。
参数:
- X:特征矩阵,形状为 (n_samples, n_features)的类数组(如 numpy array, pandas DataFrame)。
- y:目标向量,形状为 (n_samples,)的类数组(如 numpy array, pandas Series)。
返回值:
- X_resampled:重采样后的特征矩阵。
- y_resampled:重采样后的目标向量。
内部工作流程:
- 识别类别:分析 y,确定多数类和少数类。
- 确定目标:根据 sampling_strategy参数,计算每个少数类需要生成的新样本数量。
- 生成样本:对于每个需要过采样的少数类:
- 对于该类中的每个样本,找到其 k_neighbors个最近邻。
- 根据所需的新样本数量,循环执行:随机选择一个样本,再从其k近邻中随机选择一个近邻,然后在两者连线上随机插值生成一个新样本。
- 返回结果:将新生成的合成样本与原始数据(包括所有多数类样本和未重采样的少数类)合并,然后返回。
smote = SMOTE(random_state=42)
smote.fit(X_train, y_train) # 先拟合
X_res, y_res = smote.resample(X_train, y_train) # 再重采样# 这完全等价于:
X_res, y_res = smote.fit_resample(X_train, y_train)
4、实例
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
from imblearn.over_sampling import SMOTE # 注意导入路径是 imblearn# 1. 创建一个高度不平衡的数据集(仅用于演示)
X, y = make_classification(n_samples=1000, n_features=20, n_informative=2,n_redundant=10, n_clusters_per_class=1,weights=[0.99], flip_y=0, random_state=1)# 查看原始数据分布
print("原始数据集类别分布:", pd.Series(y).value_counts()) # 大概是 990:10# 2. 分割数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)# 3. 应用SMOTE(仅在训练集上应用!绝对不要在测试集上应用!)
sm = SMOTE(random_state=42)
X_train_resampled, y_train_resampled = sm.fit_resample(X_train, y_train)# 查看过采样后的训练集分布
print("SMOTE后训练集类别分布:", pd.Series(y_train_resampled).value_counts())# 4. 训练模型并评估
# 在原始不平衡数据上训练(作为对比)
model_imbalanced = RandomForestClassifier(random_state=42)
model_imbalanced.fit(X_train, y_train)
y_pred_imb = model_imbalanced.predict(X_test)# 在SMOTE平衡后的数据上训练
model_balanced = RandomForestClassifier(random_state=42)
model_balanced.fit(X_train_resampled, y_train_resampled)
y_pred_bal = model_balanced.predict(X_test)# 5. 比较结果
print("\n--- 在不平衡数据上训练的模型表现 ---")
print(confusion_matrix(y_test, y_pred_imb))
print(classification_report(y_test, y_pred_imb))print("\n--- 在SMOTE平衡数据上训练的模型表现 ---")
print(confusion_matrix(y_test, y_pred_bal))
print(classification_report(y_test, y_pred_bal))
关键注意事项:
- 仅在训练集上应用SMOTE:SMOTE的本质是通过生成新样本来扩充训练集,因此它必须只在训练集上进行。如果在整个数据集(包含测试集)上应用,会导致数据泄露,严重高估模型性能。
fit_resample方法只应使用训练集的数据。 - 先分割,再重采样:这是必须遵守的工作流。
四、 SMOTE的优缺点总结
优点:
- 有效缓解过拟合:相比随机过采样,通过生成“新”样本,降低了过拟合风险。
- 增加决策区域:通过扩充少数类,使得分类器能够学习到更广泛的少数类模式。
- 简单有效:概念清晰,实现简单,在多数情况下效果显著。
缺点和局限:
- 可能生成噪声:如果少数类的分布不连续或存在噪声点,SMOTE可能会在多数类区域内生成不合理的样本,反而干扰分类器。
- 可能放大噪声:如果选择的近邻本身是噪声点,那么新生成的样本也会是噪声。
- 忽略多数类信息:SMOTE只关注少数类,没有利用多数类的分布信息,有时会导致类别重叠区域扩大。
- 对高维数据效果下降:在高维空间中,“最近邻”的概念会变得不稳定(维度灾难)。
五、 最佳实践与建议
- 不要盲目使用:首先尝试在不平衡数据上训练一个模型作为基线。有时,简单的模型(如树模型)或调整类别权重(如
class_weight=‘balanced’)可能就足够了。 - 结合多种技术:可以尝试将SMOTE与欠采样结合(如SMOTE + Tomek Links),或者使用其改进算法(如Borderline-SMOTE, ADASYN)。
- 使用正确的评估指标:在类别不平衡问题中,准确率是无效的。应重点关注:
- 精确率
- 召回率
- F1-Score
- AUC-ROC曲线 和 AUC-PR曲线(尤其是不平衡严重时,PR曲线更敏感)
- 混淆矩阵
- 进行交叉验证:使用SMOTE时,交叉验证的设置要小心。必须确保重采样过程只在训练折叠中进行,而不是在整个数据集上。
imblearn提供了Pipeline来与sklearn的交叉验证无缝结合。
