【机器学习】(20)—— 类别不平衡 文章目录1. 平衡与不平衡差的是标签比例2. 准确率为什么会骗人3. 训练难在哪小批量里看不见少数类4. 对策多数类降采样再升权4.1 第一步降采样多数类4.2 第二步给多数类升权4.3 工程上更常见的等价做法5. 汽车示例1000 辆里只有 20 辆高效6. 动手看清准确率陷阱与 class_weight7. 降采样升权的手算直觉8. 能力边界与常见误区8.1 适用边界8.2 常见误区9. 关键术语速查10. 延伸阅读11. 小结摘要真实分类很少「正负对半」。欺诈、故障、罕见病、高效车里的极少数样本都会让数据集变成类别不平衡Class-imbalanced。这时「永远猜多数类」也能刷出极高准确率模型却什么都没学会。本文讲清平衡与不平衡的差别、训练难在哪、为何不能只看准确率以及多数类降采样 升权等常用对策。延续汽车高效分类并衔接到第 10、11 篇的 Precision / Recall / AUC。1. 平衡与不平衡差的是标签比例二分类里标签通常是正类 / 负类。类型含义例子类别平衡正负数量大致相当235 辆高效 vs 247 辆非高效类别不平衡某一类远多于另一类990 辆非高效 vs 10 辆高效现实中不平衡远比平衡常见信用卡欺诈可能不到 0.1%罕见病检测更极端。约定两个名字多数类Majority class出现更多的那一类少数类Minority class出现更少、往往更「值钱」的那一类专栏前几篇用 7 辆车演示时高效 / 非高效大致对半属于轻度场景。本篇故意把比例拉极端才能看清问题。2. 准确率为什么会骗人设 1000 辆车里非高效 990高效 10。有一个「笨模型」永远预测非高效。Accuracy 990 1000 99 % \text{Accuracy} \frac{990}{1000} 99\%Accuracy1000990​99%看起来很强但对业务关键的高效车Recall efficient T P T P F N 0 0 10 0 \text{Recall}_{\text{efficient}} \frac{TP}{TPFN} \frac{0}{010} 0Recallefficient​TPFNTP​0100​0一辆高效车都没找回来。第 10 篇已强调「不能只看准确率」不平衡只是把这句话放大到刺眼。评估时应优先看指标不平衡时的角色Precision报「高效」时有多准Recall真高效找回了多少F1二者折中AUC / PR 曲线看排序能力第 11 篇Accuracy仅作参考单独汇报很危险3. 训练难在哪小批量里看不见少数类训练时模型要同时学两件事每一类长什么样特征与标签的对应每一类有多常见真实比例普通训练把两件事缠在一起。轻度不平衡时每个 batch 里通常还能碰到少数类重度不平衡时就不行了。例如多数类 200少数类 2。若batch_size20很多个 batch一个少数类都没有即便batch_size100平均也只有约 1 个少数类信号极弱。结果是模型很快学会「猜多数类就好」梯度几乎不被少数类拉动。4. 对策多数类降采样再升权一种经典、略反直觉的两步法对多数类降采样Downsampling再对多数类升权Upweighting把「长什么样」和「有多常见」拆开学。4.1 第一步降采样多数类故意少用一部分多数类样本让训练集比例不那么悬殊。例原始 99% 多数类、1% 少数类。对多数类按因子 25 降采样后训练集约变成 80% / 20%。每个 batch 更容易见到少数类模型能更快搞清「少数类长什么样」收敛也往往更快。注意验证集、测试集不要为了好看去人为平衡——评估必须反映真实世界比例。4.2 第二步给多数类升权降采样制造了一个「假世界」少数类看起来比真实更常见容易引入预测偏差Prediction Bias第 12 篇相关。矫正办法把多数类样本的损失乘上同一个因子。若降采样因子是 25则多数类损失 ×25——错分一个多数类等价于错了 25 次。这样模型在学完「长什么样」之后仍能感到「多数类其实更常见」。降采样 / 升权因子当作超参数像学习率一样在验证集上试。4.3 工程上更常见的等价做法做法直觉class_weightbalanced按频率自动给少数类更大损失权重少数类过采样 / SMOTE复制或合成少数类注意过拟合调分类阈值概率模型输出后不机械用 0.5第 09 篇换评估与业务阈值先定「更怕漏报还是误报」sklearn 里对逻辑回归直接设class_weight往往比手写降采样更省事原理与「改损失权重」同一家族。和专栏前文怎么拼第 0507 篇的逻辑回归仍然适用只是损失从「每条样本一视同仁」变成「少数类喊得更响」第 09 篇的阈值T TT在不平衡时更要当超参调而不是默认 0.5。5. 汽车示例1000 辆里只有 20 辆高效构造一个轻度中度不平衡玩具集真实欺诈会更极端集合非高效高效比例全体980202% 正类若乱报准确率永远猜 0 → 98%—虚高业务若关心「尽可能找出高效车」应盯Recall并接受 Precision 可能下降若关心「推荐列表别掺水」则更盯 Precision。没有唯一正确指标只有与代价匹配的指标。再极端一点若 10000 辆里只有 5 辆高效任何复杂模型都可能先输给「规则 人工复核」。机器学习不是不平衡问题的唯一解有时先把少数类样本采够比调参更值钱——这和第 19 篇「数量够不够」是同一条线。推荐闭环统计正负比例 → 选定 Precision/Recall/AUC → 仅在训练集做加权或降采样 → 用验证集调权重因子与阈值 → 在原始比例的测试集上验收6. 动手看清准确率陷阱与 class_weightimportnumpyasnpfromsklearn.datasetsimportmake_classificationfromsklearn.model_selectionimporttrain_test_splitfromsklearn.linear_modelimportLogisticRegressionfromsklearn.metricsimportaccuracy_score,precision_score,recall_score,f1_score,roc_auc_score# 合成不平衡数据约 5% 正类X,ymake_classification(n_samples4000,n_features8,n_informative5,weights[0.95,0.05],flip_y0.01,random_state42,)X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.25,stratifyy,random_state0)print(pos rate:,y.mean())# A) 无加权clf_plainLogisticRegression(max_iter1000)clf_plain.fit(X_train,y_train)pclf_plain.predict(X_test)probaclf_plain.predict_proba(X_test)[:,1]# B) 类别加权少数类损失更大clf_balLogisticRegression(max_iter1000,class_weightbalanced)clf_bal.fit(X_train,y_train)p_bclf_bal.predict(X_test)proba_bclf_bal.predict_proba(X_test)[:,1]defreport(name,y_true,y_pred,y_proba):print(name,{acc:round(accuracy_score(y_true,y_pred),3),precision:round(precision_score(y_true,y_pred,zero_division0),3),recall:round(recall_score(y_true,y_pred,zero_division0),3),f1:round(f1_score(y_true,y_pred,zero_division0),3),auc:round(roc_auc_score(y_true,y_proba),3),})report(plain,y_test,p,proba)report(balanced,y_test,p_b,proba_b)# C) 「永远猜多数类」基线baselinenp.zeros_like(y_test)print(always_neg acc,accuracy_score(y_test,baseline))print(always_neg recall,recall_score(y_test,baseline,zero_division0))你会看到always_neg的准确率接近正类稀有度的「镜像」约 95%召回却是 0class_weightbalanced通常会抬高召回精确率可能下降——这是权衡不是实现写错了。stratifyy保证划分后各集合正类比例接近避免「测试集碰巧几乎没有正类」导致指标乱跳与第 18、19 篇的同分布要求一致。若你想手动模拟「降采样 升权」可以先对多数类随机抽样再用sample_weight把多数类权重乘回因子k kk。class_weightbalanced是同一思想的快捷封装按训练集频率自动算权重少写几行样板代码。7. 降采样升权的手算直觉设多数类降采样因子k 25 k25k25训练集中多数类条数变为原来的1 / 25 1/251/25每条保留的多数类样本损失乘k 25 k25k25则「见到少数类的频率」上升同时「多数类在损失里的总分量」大致回到真实世界的稀缺关系。不必死记公式记住目标即可batch 里要能看见少数类损失里要记得多数类其实更多。也可以从预测偏差角度理解只降采样、不升权模型会以为正类没那么稀有输出概率整体偏高或偏低取决于你把哪一类当正类。升权就是把被拿掉的多数类「在损失里补回来」减轻第 12 篇那种系统性偏差。若k kk太大多数类剩太少方差变大模型可能不稳定若k kk太小batch 里仍很难见到少数类。所以k kk要和验证集上的 Recall / F1 一起选而不是拍脑袋。8. 能力边界与常见误区8.1 适用边界轻度不平衡如 40/60有时只需换指标 调阈值不必强行重采样。极端稀有事件还可能需要异常检测、规则系统或更多少数类采集而不是只调class_weight。多分类不平衡同理但要按类分别看召回不能只看总准确率。8.2 常见误区误区正解准确率 99% 模型很好先看多数类基线与少数类召回把测试集也采样成 50/50评估失真只改训练集过采样少数类到爆炸易过拟合重复样本阈值永远 0.5不平衡时往往要按验证集重选T TT只优化 AUC 不管业务AUC 好仍可能在工作点上 Recall 不够加权后 Precision 下降就回退先看业务更怕哪类错误正类太少就放弃分层划分越不平衡越要用stratify否则某折可能零正类「指标好看」和「业务可用」之间不平衡问题最容易制造错觉同一模型你把正类定义翻过来准确率几乎不变但 Precision/Recall 的含义完全对调。汇报时务必写清正类是谁、代价是什么。9. 关键术语速查术语一句话解释类别不平衡各类样本数量差距显著多数类 / 少数类更多 / 更少的那一类降采样训练时少用部分多数类样本升权增大某类样本在损失中的权重类别权重按类设置损失乘数如class_weight分层划分stratify保持各集合类比例接近10. 延伸阅读资源适合看什么sklearn class_weightLogisticRegression(..., class_weightbalanced)sklearn 分类指标precision / recall / F1 / ROC AUC专栏第 10 篇为何不能只看准确率专栏第 11 篇排序能力与阈值无关的视角专栏第 12 篇预测偏差与校准直觉11. 小结类别不平衡时准确率很容易被多数类抬上天。先认清多数类 / 少数类再用 Precision、Recall、F1、AUC 评估训练上通过降采样、升权或class_weight让模型既见得到少数类又不忘记真实比例。验证与测试保持原始分布只在训练集上做重平衡。看比例 → 选对指标 → 训练集加权/降采样 → 验证集调阈值 → 原比例测试集验收下一篇回到本单元的模型侧讲模型复杂度、过拟合与损失曲线怎么读并把第 07 篇的 L2、早停放回完整图景里。复杂度过高会放大不平衡场景下的「背少数噪声样本」风险和本篇的加权策略需要一起看。系列导航上一篇【机器学习】19—— 数据特性与标签下一篇预告模型复杂度、L2 与损失曲线解读如果本篇对你有帮助欢迎点赞、收藏、关注博主机器学习专栏持续更新中下次更新不迷路。