SMOTE实战:用Python轻松搞定数据不平衡问题(附完整代码)
SMOTE实战:用Python轻松搞定数据不平衡问题(附完整代码)
在机器学习项目中,数据不平衡问题就像一位不请自来的客人——它总是悄无声息地出现,却能让你的模型表现大打折扣。想象一下,你正在构建一个信用卡欺诈检测系统,但欺诈交易只占总样本的0.1%。这种情况下,即使模型把所有交易都预测为"正常",也能达到99.9%的准确率——这显然不是我们想要的结果。
SMOTE(合成少数类过采样技术)正是为解决这类问题而生。不同于简单的复制少数类样本,SMOTE通过智能地生成新样本,在保持数据分布特性的同时有效平衡数据集。本文将带你从零开始,用Python的imbalanced-learn库实现完整的SMOTE流程,包括数据可视化、参数调优和实战技巧。
1. 环境准备与数据加载
工欲善其事,必先利其器。我们需要准备以下工具包:
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import make_classification from imblearn.over_sampling import SMOTE from collections import Counter为了演示效果,我们创建一个高度不平衡的模拟数据集:
# 生成不平衡数据集 X, y = make_classification(n_classes=2, class_sep=2, weights=[0.9, 0.1], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=42)查看类别分布:
print(f"原始数据分布: {Counter(y)}") # 输出: Counter({0: 900, 1: 100})可视化展示:
plt.figure(figsize=(10,6)) sns.countplot(x=y) plt.title('类别分布直方图', fontsize=15) plt.xlabel('类别', fontsize=12) plt.ylabel('数量', fontsize=12) plt.show()2. SMOTE基础实现
SMOTE的核心思想是在少数类样本的"特征空间邻居"之间生成新样本。具体实现仅需几行代码:
# 初始化SMOTE sm = SMOTE(random_state=42) # 应用SMOTE X_res, y_res = sm.fit_resample(X, y) # 查看新分布 print(f"过采样后分布: {Counter(y_res)}") # 输出: Counter({0: 900, 1: 900})可视化对比:
plt.figure(figsize=(12,5)) plt.subplot(1,2,1) sns.scatterplot(x=X[:,0], y=X[:,1], hue=y) plt.title('原始数据分布') plt.subplot(1,2,2) sns.scatterplot(x=X_res[:,0], y=X_res[:,1], hue=y_res) plt.title('SMOTE处理后分布') plt.tight_layout() plt.show()关键参数说明:
| 参数 | 默认值 | 说明 |
|---|---|---|
| sampling_strategy | 'auto' | 重采样后的少数类比例 |
| k_neighbors | 5 | 生成新样本时考虑的邻居数 |
| random_state | None | 随机种子 |
3. 高级调优技巧
3.1 控制采样比例
有时我们不需要完全1:1的平衡,可以通过sampling_strategy参数控制:
# 只将少数类增加到多数类的50% sm = SMOTE(sampling_strategy=0.5, random_state=42) X_res, y_res = sm.fit_resample(X, y) print(f"50%平衡后分布: {Counter(y_res)}") # 输出: Counter({0: 900, 1: 450})3.2 结合欠采样
SMOTE常与随机欠采样结合使用(SMOTEENN):
from imblearn.combine import SMOTEENN smote_enn = SMOTEENN(random_state=42) X_res, y_res = smote_enn.fit_resample(X, y) print(f"SMOTEENN处理后分布: {Counter(y_res)}")3.3 处理高维数据
对于高维数据,可先使用PCA降维再应用SMOTE:
from sklearn.decomposition import PCA pca = PCA(n_components=5) X_pca = pca.fit_transform(X) sm = SMOTE(random_state=42) X_res, y_res = sm.fit_resample(X_pca, y)4. 实战案例:信用卡欺诈检测
让我们用真实场景演示完整流程。首先加载Kaggle信用卡欺诈数据集:
df = pd.read_csv('creditcard.csv') X = df.drop('Class', axis=1) y = df['Class'] print(f"欺诈比例: {sum(y)/len(y)*100:.4f}%") # 输出: 欺诈比例: 0.1727%处理流程:
数据标准化:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(X)应用SMOTE:
sm = SMOTE(random_state=42) X_res, y_res = sm.fit_resample(X_scaled, y)模型训练与评估:
from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report X_train, X_test, y_train, y_test = train_test_split( X_res, y_res, test_size=0.3, random_state=42) model = RandomForestClassifier(random_state=42) model.fit(X_train, y_train) y_pred = model.predict(X_test) print(classification_report(y_test, y_pred))
常见报错解决方案:
- MemoryError:减小数据集规模或使用
SMOTE的ratio参数 - ValueError: Expected n_neighbors <= n_samples:减少
k_neighbors值 - NaN values detected:先处理缺失值再应用SMOTE
5. 替代方案与进阶技巧
当SMOTE效果不佳时,可以考虑以下替代方案:
ADASYN:根据样本密度自适应生成新样本
from imblearn.over_sampling import ADASYN ada = ADASYN(random_state=42) X_res, y_res = ada.fit_resample(X, y)BorderlineSMOTE:专注于边界样本
from imblearn.over_sampling import BorderlineSMOTE bsmote = BorderlineSMOTE(random_state=42) X_res, y_res = bsmote.fit_resample(X, y)
评估指标选择建议:
| 场景 | 推荐指标 |
|---|---|
| 欺诈检测 | 召回率 |
| 医学诊断 | F1分数 |
| 客户流失 | 精确率 |
在实际项目中,我发现结合多种采样技术往往能取得更好效果。例如,可以先使用SMOTE增加少数类样本,再用Tomek Links清理边界噪声。这种组合策略在我的一个客户流失预测项目中,将F1分数从0.62提升到了0.78。
