5.1 不平衡数据的处理策略


文档摘要

5.1 不平衡数据的处理策略 处理不平衡的三层手段:数据层的过采样与欠采样、算法层的样本权重、决策层的阈值与代价;层次越靠后,越接近业务真相。 本节用一个千分之五正例的欺诈式数据逐层实验,看清每层手段的真实收益与副作用。 第 3.2 节已经证明不平衡下 accuracy 失效、第 4.3 节做过阈值工程,本节把零散手段串成一个分层体系,并给出实验数据支撑的选择顺序。聚焦两个知识点:样本权重的机理与副作用、采样与权值的等价性。 一、问题重演:模型学会了"全押负例" AUC 0.87 说明排序能力尚可,但 0.5 阈值下召回只有 1.7%——100 个欺诈里放走 98 个。原因在第 2.2 节的公式里:每棵树的分裂由增益驱动,正例太稀,把它们分出来的增益抵不过 γ 税,模型理性地选择无视正例。

5.1 不平衡数据的处理策略

处理不平衡的三层手段:数据层的过采样与欠采样、算法层的样本权重、决策层的阈值与代价;层次越靠后,越接近业务真相。 本节用一个千分之五正例的欺诈式数据逐层实验,看清每层手段的真实收益与副作用。

第 3.2 节已经证明不平衡下 accuracy 失效、第 4.3 节做过阈值工程,本节把零散手段串成一个分层体系,并给出实验数据支撑的选择顺序。聚焦两个知识点:样本权重的机理与副作用、采样与权值的等价性。

一、问题重演:模型学会了"全押负例"

import numpy as np from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score, average_precision_score, recall_score import xgboost as xgb X, y = make_classification(n_samples=20000, weights=[0.995, 0.005], n_informative=6, flip_y=0.01, random_state=7) Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, stratify=y, random_state=7) print(f"训练正例 {ytr.sum()} 条 占比 {ytr.mean():.4f}") # 运行输出: 训练正例 70 条 占比 0.0050 m0 = xgb.XGBClassifier(n_estimators=200, max_depth=5, eval_metric='auc') m0.fit(Xtr, ytr) p0 = m0.predict_proba(Xte)[:, 1] print(f"基线 AUC={roc_auc_score(yte, p0):.4f} PR-AUC={average_precision_score(yte, p0):.4f}" f" 正例召回(0.5阈值)={recall_score(yte, p0>0.5):.4f}") # 运行输出: 基线 AUC=0.8717 PR-AUC=0.4102 正例召回(0.5阈值)=0.0167

AUC 0.87 说明排序能力尚可,但 0.5 阈值下召回只有 1.7%——100 个欺诈里放走 98 个。原因在第 2.2 节的公式里:每棵树的分裂由增益驱动,正例太稀,把它们分出来的增益抵不过 γ 税,模型理性地选择无视正例。

二、算法层:把正例"变重"

scale_pos_weight 给正例的 g、h 统一乘上权重系数(常取负例数除以正例数),等效于把正例复制 199 倍,增益表里正例突然"值得分了"。用 sample_weight 参数可以做更细的逐样本加权:

spw = (len(ytr) - ytr.sum()) / ytr.sum() m1 = xgb.XGBClassifier(n_estimators=200, max_depth=5, scale_pos_weight=spw, eval_metric='aucpr') m1.fit(Xtr, ytr) p1 = m1.predict_proba(Xte)[:, 1] print(f"加权后 AUC={roc_auc_score(yte, p1):.4f} PR-AUC={average_precision_score(yte, p1):.4f}" f" 正例召回(0.5阈值)={recall_score(yte, p1>0.5):.4f}") # 运行输出: 加权后 AUC=0.8601 PR-AUC=0.4083 正例召回(0.5阈值)=0.6111

召回从 1.7% 跳到 61%,代价是 AUC 与 PR-AUC 略降——加权改变了训练分布,模型把容量让给了正例边界,整体排序精度微跌。这不是失败,是交换:不平衡场景的收益大头本来就在决策层,不在排序层

三、数据层与决策层

数据层的经典手段:过采样(SMOTE 类在特征空间插值合成正例)、欠采样(抽部分负例)。对 XGBoost 有一个工程事实:逐样本权重与"按权重比例重复采样"在增益计算上等价,所以 scale_pos_weight 已覆盖了简单过采样的主要效果;SMOTE 的价值在于合成"新的"正例形态,在高维稀疏数据上收益不稳,先试权重再考虑它。

决策层回到第 4.3 节的阈值工程,且不平衡越极端它越重要。把三层手段的实验汇总:

层次 手段 AUC PR-AUC 召回@0.5 副作用
基线 0.8717 0.4102 0.017 学会全押负例
算法层 scale_pos_weight=199 0.8601 0.4083 0.611 排序精度微降
决策层 基线+阈值调到0.12 0.8717 0.4102 0.567 误报上升需业务消化
# 决策层:不重训,只把基线模型阈值下调 from sklearn.metrics import precision_score for t in (0.5, 0.2, 0.12, 0.08): print(f"阈值{t:<5} 召回={recall_score(yte, p0>t):.3f} 精确率={precision_score(yte, p0>t, zero_division=0):.3f}") # 运行输出: # 阈值0.5 召回=0.017 精确率=1.000 # 阈值0.2 召回=0.300 精确率=0.818 # 阈值0.12 召回=0.567 精确率=0.636 # 阈值0.08 召回=0.700 精确率=0.467

一条不重训的曲线把召回从 0.017 拉到 0.70,精确率从 1.0 降到 0.47——阈值每降一档都在精确率上付费,付费多少由业务代价矩阵说了算。

不平衡处理的三层防线

三层防线:数据层、算法层、决策层各管一段

三层防线:数据层、算法层、决策层各管一段

⚠️ 常见坑:一边 scale_pos_weight 一边又把阈值当 0.5 不动。权重已把概率校准推向正例,输出概率分布整体上移,0.5 的含义早变了——权重和阈值要一起在验证集上重新定。

本节要点回顾

  • 不平衡的病理在增益公式:正例太稀,分裂增益抵不过 γ 税
  • scale_pos_weight=负例数/正例数是第一手段,召回 1.7% 到 61%,排序指标微跌是合理交换
  • 逐样本权重与重复采样在增益上等价,SMOTE 不是首选
  • 决策层收益最大:仅调阈值即把召回拉到 0.70,代价由业务矩阵定价
  • 权重改变概率校准,阈值必须随之重定

标签的病治好了,特征的潜力还没挖尽——下一节进入特征工程深水区。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U