在真实世界的机器学习应用中,我们经常会遇到类别分布严重不均衡的数据集。比如信用卡欺诈检测中,正常交易可能占99.9%,而欺诈交易只有0.1%;在医疗诊断中,健康样本可能远多于患病样本。这种类别不平衡会带来几个关键问题:
准确率陷阱:模型可能简单地预测多数类就能获得高准确率,但对实际业务毫无价值。比如一个欺诈检测模型如果总是预测"非欺诈",可以达到99.9%的准确率,但完全检测不出任何欺诈案例。
评估指标失真:传统指标如准确率在不平衡数据上会严重误导。我们需要更细致的评估方式,如精确率-召回率曲线、F1分数或AUC-ROC。
模型偏差:大多数算法默认假设类别分布均衡,在训练时会倾向于偏好多数类,导致对少数类的识别能力下降。
提示:在处理不平衡数据前,务必先明确业务目标。是要尽可能捕捉所有少数类样本(高召回)?还是要确保预测为少数类的样本尽可能准确(高精确)?不同目标对应不同的处理策略。
SMOTE(Synthetic Minority Over-sampling Technique)是最经典的过采样方法,其核心思想是在少数类样本的特征空间中进行插值,生成新的合成样本。具体实现:
python复制from imblearn.over_sampling import SMOTE
smote = SMOTE(sampling_strategy='minority', random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)
关键参数解析:
k_neighbors:默认为5,控制生成新样本时参考的最近邻数量sampling_strategy:可指定希望达到的少数类比例注意事项:
随机欠采样虽然简单,但可能丢失重要信息。更智能的方法是:
python复制from imblearn.under_sampling import TomekLinks
tl = TomekLinks()
X_resampled, y_resampled = tl.fit_resample(X, y)
python复制from imblearn.under_sampling import ClusterCentroids
cc = ClusterCentroids(random_state=42)
X_resampled, y_resampled = cc.fit_resample(X, y)
实测心得:在金融风控项目中,组合使用SMOTE和Tomek Links能使AUC-ROC提升约15%,同时保持合理的运行效率。
大多数分类器支持类别权重参数。以逻辑回归为例:
python复制from sklearn.linear_model import LogisticRegression
# 权重与类别频率成反比
model = LogisticRegression(class_weight='balanced')
更精细的控制方式:
python复制# 手动指定类别权重
weights = {0: 1, 1: 10} # 少数类权重是多数类的10倍
model = LogisticRegression(class_weight=weights)
在某些场景下,不同类型的误分类代价不同。例如:
python复制from sklearn.svm import SVC
# 自定义代价矩阵
cost_matrix = {
'FN_cost': 100, # False Negative代价
'FP_cost': 1 # False Positive代价
}
# 需要自定义损失函数实现代价敏感学习
class CostSensitiveSVC(SVC):
def _decision_function(self, X):
# 根据cost_matrix调整决策阈值
...
行业案例:在某电商异常订单检测中,通过调整代价矩阵使召回率从60%提升至85%,虽然精确率下降10%,但整体业务损失减少了35%。
python复制from imblearn.ensemble import BalancedRandomForestClassifier
brf = BalancedRandomForestClassifier(
n_estimators=100,
sampling_strategy='auto',
replacement=True,
random_state=42
)
brf.fit(X_train, y_train)
通过多次欠采样多数类并集成结果:
python复制from imblearn.ensemble import EasyEnsembleClassifier
ee = EasyEnsembleClassifier(
n_estimators=10,
base_estimator=LogisticRegression(),
random_state=42
)
ee.fit(X_train, y_train)
当少数类样本极少时,可将其视为异常检测问题:
python复制from sklearn.svm import OneClassSVM
ocsvm = OneClassSVM(gamma='auto')
ocsvm.fit(X_train[y_train == 0]) # 仅用多数类训练
# 预测时,返回1表示多数类,-1表示少数类
predictions = ocsvm.predict(X_test)
实测对比:在工业设备故障预测中,当异常样本<1%时,One-Class SVM的表现优于常规分类方法约20%的F1分数。
python复制from sklearn.metrics import classification_report
print(classification_report(
y_test,
predictions,
target_names=['多数类', '少数类']
))
默认的0.5决策阈值在不平衡数据中往往不是最优的。可通过以下方法寻找最佳阈值:
python复制from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_test, probas)
python复制J = recalls - (1 - precisions)
optimal_idx = np.argmax(J)
optimal_threshold = thresholds[optimal_idx]
案例:在某医疗诊断系统中,通过阈值调整使召回率从70%提升到90%,虽然精确率从85%降到75%,但挽救了更多患者的生命。
特别适用于极度不平衡的视觉任务:
python复制import tensorflow as tf
def focal_loss(gamma=2., alpha=.25):
def focal_loss_fixed(y_true, y_pred):
pt = tf.where(tf.equal(y_true, 1), y_pred, 1 - y_pred)
return -tf.reduce_mean(alpha * tf.pow(1. - pt, gamma) * tf.math.log(pt))
return focal_loss_fixed
model.compile(optimizer='adam', loss=focal_loss())
逐步增加少数类样本的难度:
python复制# 伪代码示例
for epoch in range(epochs):
if epoch < warmup_epochs:
train_on_easy_samples()
else:
gradually_add_hard_samples()
在处理图数据时,传统方法可能失效。可采用的策略包括:
python复制import torch_geometric
# 使用Node2Vec进行图嵌入后应用常规不平衡处理方法
from torch_geometric.nn import Node2Vec
model = Node2Vec(
edge_index,
embedding_dim=128,
walk_length=20,
context_size=10
)
最新研究显示,在图数据上结合拓扑信息的过采样方法能提升约30%的少数类识别率。
根据项目特点选择策略组合的决策树:
数据量级评估
不平衡程度
业务需求
计算资源
典型组合方案示例:
python复制from imblearn.pipeline import make_pipeline
from sklearn.ensemble import GradientBoostingClassifier
pipeline = make_pipeline(
SMOTE(sampling_strategy=0.3),
TomekLinks(),
GradientBoostingClassifier(
n_estimators=200,
learning_rate=0.05,
max_depth=5
)
)
在电信客户流失预测项目中,这套组合方案将流失客户的召回率从50%提升到78%,同时保持整体准确率在92%以上。