做分类项目时间稍长一点,一定会撞上一堵墙:数据不平衡。我昨天在调试一个二分类模型的时候,训练集里正样本只有1.5%,默认参数跑完,准确率高得吓人,可业务方真正关心的那部分样本,几乎一个都没挖掘出来。今天这一篇笔记,就集中梳理一下不平衡数据集问题从诊断到处理的全流程,包括重采样技术、损失函数调整、评估指标的选择,以及一个可以直接照做的对比实验。这次内容对做欺诈检测、风控评分、故障预测、医学诊断这类稀有事件场景的同学尤其有用。
1. 先诊断再动手:什么样的不平衡才真正需要处理
很多人一看到“不平衡”三个字就紧张,立刻想到上采样、下采样、SMOTE,恨不得把工具箱里的东西全倒出来。但我的经验是,先别急着动手,先问三个问题:类别比例到底是多少?少数类在业务上到底值多少钱?模型现在真的不行吗?
1.1 不平衡程度的量化分级
不平衡程度不是非黑即白,业内通常按少数类占比分成几个档位。我用一张表概括一下常见情况。
| 程度 | 多数类:少数类 | 典型场景 | 处理策略 |
|---|---|---|---|
| 轻度不平衡 | ≤ 10:1 | 用户流失、营销响应 | 通常不需要专门处理,调阈值即可 |
| 中度不平衡 | 10:1 ~ 100:1 | 信用违约、设备故障 | 建议尝试类别权重或轻量重采样 |
| 高度不平衡 | 100:1 ~ 1000:1 | 欺诈交易、网络入侵 | 需要系统性方案,重采样+算法级调整+阈值移动 |
| 极度不平衡 | > 1000:1 | 罕见病筛查、超大流量异常过滤 | 需要专门设计,常规方法容易失效 |
这个分级没有绝对标准,但能帮你快速判断复杂度的起点。比如10:1的数据,你用默认逻辑回归跑出来,少数类召回率可能已经能接受;但1000:1的数据,不管用什么模型,直接训练几乎必然把所有样本都判成多数类。
1.2 业务目标决定了少数类的价值
这里有个反直觉的点:即使类别比例很悬殊,有些场景仍然不需要处理。比如一个电商平台上“北京用户”和“非北京用户”的比例可能是1:20,但如果业务目标是对所有用户做个性化推荐,品牌方都希望预测得准确,那这个不平衡就没有那么“致命”。因为少数类本身没有更高的业务价值,误判少数类也不会带来额外损失。
真正需要处理不平衡的场景,通常满足两个条件:
- 少数类代表“事件发生”,比如欺诈、故障、患病,漏掉一个的代价极高。
- 模型默认会牺牲少数类来换取整体准确率。
所以,处理不平衡的第一步不是选算法,而是确认“少数类真的值得重视”。如果你担心漏掉欺诈交易,那么召回率就是你最重要的指标;如果你做的是精准营销,希望降低骚扰用户的比例,那精确率就更重要。这两个不同的侧重,会直接决定你后面用哪种手段。
1.3 用基线模型先判断“问题有多严重”
在动手重采样之前,我习惯先跑一个最简单的基线:用默认参数训练一个逻辑回归或随机森林,然后看混淆矩阵。只要少数类召回率惨不忍睹,那就说明模型确实被多数类带偏了。如果连基线都能把少数类识别得七七八八,说明数据分布或者特征本身已经足够可分,这时候强行加SMOTE反而可能画蛇添足。
基线实验同时也是后续所有方案的“锚点”。没有这个锚点,你后面看到的任何指标提升都是没有参照物的浮动。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据层重采样:过采样、欠采样与SMOTE的真实边界
一旦确认问题存在,首先想到的往往就是改变数据的分布,让样本数量看起来平衡一点。这一章我重点讲三条路线:随机欠采样、随机过采样、以及最常用的SMOTE系列。
2.1 随机欠采样:简单直接,但信息丢弃严重
随机欠采样的逻辑很朴素:从多数类里随机抽出一部分样本,使得多数类和少数类数量接近。比如多数类有9000条,少数类1000条,那就从多数类里抽1000条,组成一个各1000条的新训练集。
它的优点很明显:
- 训练速度快,内存占用小。
- 当多数类数据本身有大量冗余时,减少样本量对模型效果影响不大。
- 在某些高噪声数据集上,去掉一部分多数类反而能提升模型对少数类的敏感度。
但缺点也同样致命——随机丢弃多数类样本,就是随机丢弃信息。如果多数类内部有多个不同的子分布,抽样的随机性可能让某些重要模式全部被丢弃。实际项目中,我在一个故障预测数据集上尝试过随机欠采样,把训练集缩小到原来的20%后,多数类的精确率大幅下降,整体效果反而不如不处理。
所以我的建议是:随机欠采样适合“多数类海量且冗余、训练资源紧张”的场景,但不适合样本总量本来就不大的场景。它最大的价值是作为其他重采样方法的对照,而不是终极方案。
2.2 随机过采样:复制少数类会让模型“背答案”
随机过采样的做法更简单:把少数类样本复制几份,直到数量追上多数类。实现上几乎零成本,一行代码就能搞定。但它的问题在于——模型会过度记忆被重复的样本。
为什么?因为模型训练时的损失函数会在每个样本上计算梯度。如果你把同一个少数类样本复制10遍,相当于这个样本的梯度被放大10倍。模型会努力拟合这些重复样本的具体
