Python实战:用SMOTE算法解决数据不平衡问题(附完整代码)
你是否曾在训练一个分类模型时,发现无论怎么调整参数,模型总是倾向于预测占多数的那个类别?比如在欺诈检测中,99%的交易都是正常的,模型为了追求整体准确率,干脆把所有交易都预测为正常,导致欺诈交易一个也抓不到。这就是数据不平衡带来的典型问题。对于需要精准识别少数类别的场景,如疾病诊断、异常检测、客户流失预测等,空有99%的准确率毫无意义。传统的过采样方法简单复制少数类样本,又容易让模型陷入过拟合的泥潭。今天,我们将深入探讨一种更聪明的解决方案——SMOTE算法,并通过完整的Python代码,带你从原理到实践,彻底掌握这一处理数据不平衡的利器。
1. 数据不平衡:不只是数量问题
在机器学习中,数据不平衡指的是数据集中不同类别的样本数量存在显著差异。这种差异并非简单的数量差距,它直接导致模型在训练时对多数类产生偏见,从而在预测时忽略少数类。想象一下,一个包含1000个样本的数据集,其中950个是正常邮件,50个是垃圾邮件。一个模型即使将所有邮件都预测为正常邮件,也能获得95%的准确率,但这显然不是我们想要的结果。
数据不平衡带来的挑战主要有以下几点:
- 模型偏见:模型倾向于学习多数类的特征,而忽略少数类,导致对少数类的识别能力极差。
- 评估指标失真:传统的准确率指标在这种情况下毫无意义,需要引入精确率、召回率、F1分数等更全面的评估指标。
- 训练过程不稳定:少数类样本数量太少,无法提供足够的学习信号,导致模型训练过程波动大,难以收敛。
解决数据不平衡的常用方法包括:
- 欠采样:随机从多数类中删除部分样本,使两类数量接近。缺点是会丢失大量信息。
- 过采样:增加少数类样本的数量,使其与多数类匹配。传统的随机过采样(简单复制)容易导致过拟合。
- 合成采样:通过算法生成新的少数类样本,在增加数量的同时保持多样性,SMOTE 就是其中的代表。
空泛的评估指标毫无意义,我们需要更细致的观察。 下面是一个简单的示例,展示了在数据不平衡情况下,仅使用准确率评估模型是多么危险:
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report
# 创建一个高度不平衡的数据集
X, y = make_classification(
n_samples=1000,
n_features=2,
n_redundant=0,
n_clusters_per_class=1,
n_classes=2,
weights=[0.95, 0.05], # 95%的样本属于类别0,5%属于类别1
random_state=42
)
# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 训练一个简单的逻辑回归模型
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
# 预测并评估
y_pred = model.predict(X_test)
print(f"准确率: {accuracy_score(y_test, y_pred):.4f}")
print("详细分类报告:")
print(classification_report(y_test, y_pred))
运行上述代码,你可能会看到一个高达95%以上的准确率,但仔细查看分类报告,会发现类别1(少数类)的召回率(Recall)和F1分数可能非常低,甚至为零。这表明模型根本没有识别出少数类的能力。
注意:在处理数据不平衡问题时,不要依赖单一指标。至少需要同时关注精确率、召回率、F1分数和混淆矩阵,才能全面评估模型对少数类的识别能力。
2. SMOTE算法原理:从复制到创造
SMOTE(Synthetic Minority Oversampling Technique,合成少数类过采样技术)由Nitesh Chawla等人于2002年提出,其核心思想是创造而非复制。它通过分析少数类样本的特征空间,在相邻样本之间生成新的合成样本,从而增加少数类的数量,同时保持其分布特征,有效避免了随机过采样带来的过拟合问题。
SMOTE算法的核心步骤可以概括为:
- 识别邻居:对于少数类中的每个样本,计算其在特征空间中的欧氏距离,找到其最近的K个邻居(这些邻居也必须是少数类样本)。
- 生成合成样本:对于每个少数类样本,从其K个邻居中随机选择一个或多个邻居。对于每个被选中的邻居,在原始样本和邻居样本之间的连线上随机选择一个点,这个点就是新生成的合成样本。
- 重复生成:根据预设的采样倍率(即希望将少数类样本数量增加到多少倍),重复上述过程,直到达到目标数量。
生成合成样本的数学公式非常简单:
假设原始少数类样本为 x,其邻居样本为 o,生成的新样本为 new_sample,公式如下:
new_sample = x + random(0, 1) * (o - x)
其中 random(0, 1) 表示一个在0到1之间的随机数。这个公式的含义是在从样本 x 指向邻居 o 的向量上随机选取一个点,这个点就是新合成的样本。
SMOTE算法的优势在于:
- 增加样本多样性:生成的样本是全新的,而非简单复制,有助于模型学习更泛化的特征。
- 缓解过拟合:相比随机过采样,SMOTE生成的样本分布更接近原始少数类分布,降低了模型过拟合的风险。
- 易于实现:算法逻辑清晰,易于在多种编程语言中实现,且有成熟的库支持。
SMOTE算法的局限性:
- **盲目选择邻居:需要预先指定邻居数量K,而K值的选择对结果影响较大,通常需要通过实验来确定。
- **可能产生噪声:当少数类样本分布稀疏或存在噪声时,SMOTE可能会在类别边界上生成不合理的合成样本,反而模糊了类别边界,增加分类难度。
- **不适用于所有数据类型:对于类别型特征,欧氏距离的计算可能不适用,需要特殊处理。
下表对比了SMOTE与随机过采样和欠采法的特点:
| 方法 | 核心思想 | 优点 | 缺点 |
|---|---|---|---|
| 随机过采样 | 随机复制少数类样本 | 简单易行,不丢失信息 | 容易导致过拟合 |
| 随机欠采样 | 随机删除多数类样本 | 减少训练时间,降低计算成本 | 丢失大量信息,可能影响模型性能 |
| SMOTE | 在特征空间合成新的少数类样本 | 增加样本多样性,缓解过拟合 | 可能生成噪声样本,参数选择敏感 |
3. 实战:使用imbalanced-learn库实现SMOTE
理论再好,不如动手一试。在Python中,我们可以使用imbalance

2万+

被折叠的 条评论
为什么被折叠?



