数据平衡:别让模型“偏心”!少数类样本的“生存指南”

你有没有遇到过这种情况:训练一个“欺诈检测模型”,99%的样本是正常交易,1%是欺诈交易。模型看似准确率99%,但实际上对所有样本都预测“正常”——因为只要讨好多数类,就能轻松拿高分,却完全漏掉了致命的欺诈样本。

这就是数据不平衡在搞鬼。当数据集中某些类别的样本数量远远少于其他类别时,模型会下意识“偏袒”多数类,对少数类视而不见。今天就来讲透数据平衡的核心方法,从过采样到加权损失,附公式推导和代码实战,帮你的模型“一碗水端平”!

一、数据不平衡:模型的“偏见之源”

先看一个扎心的例子:

  • 二分类任务:预测“癌症患者”(少数类)和“健康人”(多数类);
  • 数据分布:1000个样本中,只有50个癌症患者(5%),950个健康人(95%);
  • 模型“偷懒”:直接预测所有样本为“健康人”,准确率也能达到95%,但对癌症患者的识别率为0——这样的模型毫无价值。

数据不平衡的危害

  1. 模型偏向多数类,少数类识别率极低(如欺诈检测漏检、疾病诊断误诊);
  2. 评估指标“失真”:高准确率掩盖了少数类的预测失败;
  3. 模型学到的特征有偏差:只记住多数类的规律,忽略少数类的特点。

判断数据是否不平衡的简单标准:少数类样本占比低于20%,就需要做平衡处理。

二、数据平衡3大核心方法:从“偏袒”到“公平”

1. 过采样(Oversampling):给少数类“扩军”

原理:增加少数类样本数量,使其与多数类接近。就像给少数派“增加话语权”,让模型不得不关注它们。

经典算法:SMOTE( Synthetic Minority Oversampling Technique)
SMOTE不是简单复制少数类样本(会导致过拟合),而是生成“虚拟少数类样本”

  1. 对每个少数类样本,找它最近的k个少数类邻居;
  2. 在样本与邻居之间随机选一点,生成新样本(如样本A(2,3),邻居B(4,5),新样本可能是(3,4))。

这样既增加了少数类数量,又保留了特征分布,避免过拟合。

2. 欠采样(Undersampling):给多数类“裁员”

原理:减少多数类样本数量,使其与少数类接近。就像给多数派“限权”,降低其在训练中的主导地位。

常用策略

  • 随机欠采样:随机删除多数类样本(简单但可能删错关键样本);
  • 聚类欠采样:对多数类聚类,保留每个簇的中心样本(保留重要信息)。

缺点:可能丢失多数类的重要特征(比如删除了包含关键模式的样本),因此更适合多数类样本极多的场景(如10万+样本)。

3. 加权损失(Weighted Loss):让模型“更关注”少数类

原理:不改变样本数量,而是在计算损失时给少数类样本更高的权重——少数类样本的预测错误会“惩罚”模型更重,迫使模型重视它们。

核心公式:加权交叉熵损失
对于二分类任务,加权交叉熵损失为:

L=−1N∑i=1Nwyi(yilog⁡y^i+(1−yi)log⁡(1−y^i))L = -\frac{1}{N} \sum_{i=1}^N w_{y_i} \left( y_i \log \hat{y}_i + (1-y_i) \log (1-\hat{y}_i) \right)L=N1i=1Nwyi(yilogy^i+(1yi)log(1y^i))

其中:

  • wyiw_{y_i}wyi 是类别yiy_iyi的权重(yi=1y_i=1yi=1为少数类,yi=0y_i=0yi=0为多数类);
  • 权重通常按“样本总数/类别样本数”计算:w1=NN1w_1 = \frac{N}{N_1}w1=N1Nw0=NN0w_0 = \frac{N}{N_0}w0=N0NN1N_1N1是少数类样本数,N0N_0N0是多数类样本数)。

举例:若少数类占比10%(N1=100N_1=100N1=100N0=900N_0=900N0=900N=1000N=1000N=1000),则w1=1000/100=10w_1=1000/100=10w1=1000/100=10w0=1000/900≈1.1w_0=1000/900≈1.1w0=1000/9001.1。少数类的损失权重是多数类的9倍,模型会更努力学习少数类的特征。

三、方法对比:该选过采样、欠采样还是加权损失?

方法原理优点缺点适用场景
过采样(SMOTE)增加少数类样本保留多数类信息,适合样本少的场景可能生成不合理样本,导致过拟合少数类样本极少(<10%)
欠采样减少多数类样本训练快,适合大数据集可能丢失多数类关键信息多数类样本极多(如10万+)
加权损失调整损失权重不改变数据分布,灵活需调权重参数,对极端不平衡效果有限中等不平衡(10%-20%)

一句话总结

  • 少数类样本极少(如5%以下)→ 优先用SMOTE过采样;
  • 多数类样本爆炸(如100万+)→ 试试欠采样;
  • 怕改变数据分布或需在线学习 → 用加权损失。

四、代码实战:用SMOTE拯救“少数类”(附可视化)

我们用一个极度不平衡的二分类数据集(10%少数类),演示SMOTE处理过程,对比平衡前后的模型效果:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
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
from imblearn.over_sampling import SMOTE  # 需安装:pip install imbalanced-learn

# ----------------------
# 1. 生成不平衡数据(修正参数冲突)
# ----------------------
np.random.seed(42)
X, y = make_classification(
    n_classes=2,  # 2个类别
    class_sep=2,  # 类别分离度
    weights=[0.9, 0.1],  # 多数类90%,少数类10%
    n_features=2,  # 总特征数=2
    n_informative=2,  # 信息特征数=2(关键修正:2^2=4)
    n_redundant=0,    # 冗余特征数=0
    n_repeated=0,     # 重复特征数=0
    n_clusters_per_class=2,  # 每个类别2个簇(默认值)
    n_samples=1000,   # 1000个样本
    random_state=42
)
# 此时:2(类别数)* 2(每个类别簇数)=4 ≤ 2^2(信息特征数的2次方)=4,满足条件

# ----------------------
# 2. 可视化原始数据分布(解决中文显示)
# ----------------------
plt.figure(figsize=(12, 6))
plt.rcParams["font.family"] = ["SimHei"]  # 中文显示
plt.rcParams["axes.unicode_minus"] = False  # 负号显示

# 子图1:原始数据分布
plt.subplot(1, 2, 1)
plt.title("原始数据分布(少数类仅10%)", fontsize=14)
plt.scatter(X[y==0, 0], X[y==0, 1], c='blue', label='多数类(y=0)', alpha=0.6, edgecolor='k')
plt.scatter(X[y==1, 0], X[y==1, 1], c='yellow', label='少数类(y=1)', alpha=0.6, edgecolor='k')
plt.legend()
plt.xlabel("特征1")
plt.ylabel("特征2")

# ----------------------
# 3. SMOTE过采样平衡数据
# ----------------------
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=42
)

smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)

# ----------------------
# 4. 可视化平衡后的数据
# ----------------------
plt.subplot(1, 2, 2)
plt.title("SMOTE平衡后的数据分布(1:1)", fontsize=14)
plt.scatter(X_resampled[y_resampled==0, 0], X_resampled[y_resampled==0, 1], 
           c='blue', label='多数类(y=0)', alpha=0.6, edgecolor='k')
plt.scatter(X_resampled[y_resampled==1, 0], X_resampled[y_resampled==1, 1], 
           c='yellow', label='少数类(y=1)', alpha=0.6, edgecolor='k')
plt.legend()
plt.xlabel("特征1")
plt.ylabel("特征2")

plt.tight_layout()
plt.show()

# ----------------------
# 5. 模型评估
# ----------------------
clf = RandomForestClassifier(random_state=42)
clf.fit(X_resampled, y_resampled)
y_pred = clf.predict(X_test)

print("=== 平衡后模型评估报告 ===")
print(classification_report(y_test, y_pred))

# ----------------------
# 6. 可视化决策边界
# ----------------------
xx, yy = np.meshgrid(
    np.linspace(X[:, 0].min()-1, X[:, 0].max()+1, 200),
    np.linspace(X[:, 1].min()-1, X[:, 1].max()+1, 200)
)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

plt.figure(figsize=(10, 6))
plt.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
plt.scatter(X_test[y_test==0, 0], X_test[y_test==0, 1], c='blue', label='多数类(y=0)', edgecolor='k')
plt.scatter(X_test[y_test==1, 0], X_test[y_test==1, 1], c='yellow', label='少数类(y=1)', edgecolor='k')
plt.title("平衡后模型的决策边界", fontsize=14)
plt.xlabel("特征1")
plt.ylabel("特征2")
plt.legend()
plt.show()

在这里插入图片描述

== 平衡后模型评估报告 ==
precision recall f1-score support

       0       0.99      0.99      0.99       268
       1       0.93      0.88      0.90        32

accuracy                           0.98       300

macro avg 0.96 0.93 0.95 300
weighted avg 0.98 0.98 0.98 300

在这里插入图片描述

五、结果解读:SMOTE如何让模型“公平”?

  1. 原始数据分布(左图)
    黄色点(少数类)稀疏,蓝色点(多数类)密集,模型很容易“只看到蓝色”,忽略黄色。

  2. SMOTE平衡后(右图)
    黄色点数量翻倍(生成了合理的虚拟样本),与蓝色点数量接近,模型不得不“认真学习”黄色点的特征。

  3. 评估报告
    重点看“少数类(y=1)”的recall(召回率)——平衡后通常能从30%以下提升到80%以上,意味着更多少数类样本被正确识别(如欺诈交易、癌症患者)。

  4. 决策边界(右图)
    平衡后的模型会给少数类样本“划分出合理的决策区域”(冷色区域),而不是把所有区域都归为多数类。

六、避坑指南:数据平衡的3个“雷区”

  1. 只平衡训练集,别动测试集
    测试集必须保持真实分布(比如现实中欺诈就是1%),否则评估结果失真。SMOTE等操作只用于训练集

  2. 过采样别“过度复制”
    简单复制少数类样本(而不是SMOTE生成新样本)会导致模型“记住”这些样本,泛化差。优先用SMOTE等智能过采样。

  3. 别盲目追求“绝对平衡”
    某些场景下,完全1:1可能引入噪声(如少数类本身就稀有)。可根据业务需求调整比例(如2:1或3:1)。

总结:数据平衡的“终极目标”

数据平衡不是为了“让样本数量绝对相等”,而是让模型对每个类别都给予足够的关注。无论是过采样、欠采样还是加权损失,核心都是纠正模型的“偏见”,让少数类的重要特征不被忽略。

记住:在欺诈检测、疾病诊断等场景中,“漏检一个少数类样本”的代价可能远高于“错检一个多数类样本”。数据平衡,本质是在为这些“关键少数”争取应有的重视。

你在处理不平衡数据时踩过哪些坑?评论区聊聊你的解决方案~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值