《模型不玄学》第21章 稳定性与偏置审计

第21章 稳定性与偏置审计(数字会骗人,切片会说话)

小白一句话

前几章的评估都是"一个时间点、一个整体数字"。这一章把同一套评估放到多个时间点和多个子人群上重看:指标会不会漂,某些人是不是被系统性地高估或低估。

这一章在干嘛

整体 AUC 0.70、Brier 0.18 看起来还行,但这只是"某一天、全体人"的一个快照。本章做三种审计:

  1. 滚动起点:换个训练截止日、往后多测几个时间点,看指标是稳还是漂。
  2. 配对显著性:两个模型(或两个配置)差的那点数字,统计上是不是真的分得出来。
  3. 偏置切片:把人群切开,看"谁被高估、谁被低估"——整体好看不代表处处公平。

这三件事串起来就是本章的立场:整体数字好 ≠ 处处都好;单点数字 ≠ 稳定。前两章说"别信单点小样本数字",本章说"还要别信整体数字"。

1) 滚动起点:指标会漂吗

示例数据只有 3 个评分日,滚动评估能做两个点:

评估点训练测试测试基准率AUCBrier
点 107-0807-15(56 行,49 正)0.8750.6950.110
点 207-08 + 07-1507-22(57 行,42 正)0.7370.6980.178

AUC 几乎没动(0.695 → 0.698)——排序质量很稳。但 Brier 从 0.110 涨到 0.178——概率刻度明显变差。为什么?

点 2 的测试集更难:基准率从 0.875 掉到 0.737,里面开始出现 U0009、U0025 这种"之前活跃、正在掉"的人(第16、17章的熟人)。人还在活跃的时候谁都能猜准,Brier 当然小;等有人开始凉了,校准就跟不上了。

结论:AUC 稳不代表 Brier 稳。不同指标对"任务变难"的敏感度不一样。正式上线前要多看几个滚动点,别拿单点数字当长期表现——这也是第29章"上线后监控再训练"要做的事的预演。

滚动前推(walk-forward):把"多看几个点"做成正式协议

上面只有 07-15、07-22 两个评估点,是快检。要更稳的评估,用滚动前推验证(walk-forward CV):把评分日排成一串,每个评估点用"它之前所有评分日的成熟样本"训练,评"下一个评分日"——训练窗口随评估点逐步扩展,测试永远在训练之后,绝不回头。

示例数据只有 30 天,取 8 个隔天评分日(07-08、07-10、…、07-22),凑出 7 个评估点(附件/06_评估篇/activity_walkforward.py):

评估点训练评分日训练行数测试行数基准率PR-AUCAUCBrier
评 07-10149540.8700.9550.7190.174
评 07-122103550.8550.9500.8160.120
评 07-143158560.8750.9870.9140.097
评 07-164214570.8600.9440.8210.102
评 07-185271570.8070.9840.9270.109
评 07-206328570.8070.9390.8050.137
评 07-227385570.7370.9480.8630.133

这 7 个点给出两条单点评估给不了的信息:

  • 训练评分日攒得越多,指标越稳。评 07-22 是同一个测试集:只用 2 个评分日训(第 21 章滚动起点)AUC 0.698,用 7 个评分日训(walk-forward 末点)0.863。训练窗口每扩展一步,AUC 大体爬一截(0.719 → 0.816 → 0.914 → …)。这就是第 8 章"多评分日样本"和第 29 章"重训前把历史攒够"的评估侧证据——样本量这道线,评估阶段同样看得见
  • 7 个点自己也在晃。AUC 0.72~0.93、Brier 0.097~0.174,均值 ± 一个标准差是 0.124 ± 0.024。单看任何一个点都会高估或低估模型:点 1(训 49 行)0.719 偏悲观,点 5(训 271 行)0.927 偏乐观。上线前对指标的预期,应该用这个范围而不是某一次的数——第 29 章监控报警的参照线,就是这么来的。

两点代价要认:一是多个滚动点意味着多训好几遍模型(每点一次全流程),评估成本上去了;二是评分日之间隔太近,相邻点的样本高度重复(同一个人的 07-10 和 07-12 特征几乎一样),信息增量递减——真实项目按周或按月滚动,比这个示例的隔天更合适。

2) 配对显著性:差异是真的吗

拿同一个测试集(07-22 的 57 人),比两个模型:

  • 全特征模型:前几章一直用的主角——10 个特征全用,第12~20章那个 AUC 0.698、Brier 0.178 就是它。
  • 去掉趋势特征的模型:本章现造的消融变体——同一个 GBDT,只是输入里少掉 f_trend 一列,其余 9 列、超参、切分、校准、种子全部一样,前文没有这个模型。造它就是为了看"少一个特征,这点差别在统计上分不分得出"。

结果:

  • AUC:全特征 0.698 vs 去趋势 0.728(去趋势反而略高?)
  • Brier:0.178 vs 0.177(几乎一样)
  • 两模型判得不一样的人只有 3 个:全特征对而另一错 2 个,去趋势对而全特征错 1 个
  • McNemar 检验 p = 1.000

p=1.000 的意思是:在 57 个样本上,这点差异统计上完全分不出来。而且去趋势的 AUC 还略高,更说明这不是"趋势特征没用"的证据,就是小样本噪声——第12章明明说 f_trend 有 0.084 的重要性、能帮 U0009 加分,但在这 57 人身上根本体现不出来。

McNemar 检验把两个模型放到一张小表里看,逻辑是这样的:

  • 把 57 个样本按两个模型的判断分成四格:都对、都错、全特征对而另一错(b)、另一对而全特征错(c)。前两格两边表现一样,不提供"谁更好"的信息,只有 b 和 c 有用。
  • 如果两个模型其实一样好,b 和 c 应该大致相当(谁对谁错只是运气);如果 b 远大于 c,说明全特征模型真的更会判,反过来同理。
  • 检验就是算"b 和 c 偏成这样、靠运气发生的概率"——这个概率就是 p 值,通常 p<0.05 才算显著差异。

本例 b=2、c=1,几乎对称,p=1.000——运气完全能解释,分不出谁好。再补一句它的局限:McNemar 只看 0/1 判断的对错,看不见排序上的细微差异(比如 AUC 0.698 vs 0.728 这种);所以"没有显著差异"不等于"两个模型一模一样",只是"这点样本上分不出"。

结论:小样本上别拿微小差异当结论。真要比较两个配置,要么攒更多样本、多跑几个滚动点,要么上线做 A/B(第29章会提到)。这一节和"换种子就抖"(第20章)是一对兄弟:一个是数字会跳,一个是跳了你还分不出谁好。

换个招:同窗配对 + bootstrap,给"差多少"配个区间

McNemar 有个够不着的地方:它只数"判对/判错",AUC 0.698 和 0.728 这种排序上的细微差别它看不见。想比较排序指标,更常用的是 bootstrap:把评估样本反复有放回地抽,每次都重算两个模型的差值,抽上几千次,就能看出"这个差距大概在什么范围里晃"。

两个模型放在同一份评估样本上比(这叫同窗配对),比的是模型本身,不掺数据的差别。

具体做一遍。评估样本用第19章那套——把评分日撑密到 07-16 ~ 07-22,57 个人一共 399 行。两个模型还是上面那对(全特征 vs 去趋势),比的是 PR-AUC 的差值 Δ:

  • 全特征 PR-AUC 0.907
  • 去趋势 PR-AUC 0.923
  • Δ = 全特征 − 去趋势 = −0.016(这回是去趋势略好)

光看这一个数没意义,bootstrap 抽 2000 次看 Δ 的分布:

抽法Δ 均值95% 置信区间区间宽度P(Δ>0)
UID 精确抽样(按用户抽)−0.015[−0.052, +0.018]0.0710.197
按行抽样(把每行当独立样本)−0.015[−0.041, +0.010]0.0510.139

怎么读这两行

  • 置信区间跨过了 0(−0.052 到 +0.018),意思是"这个差距和 0 分不开"——Δ 有时是正的、有时是负的,全看抽到哪拨人。换成人话:两个模型没分出高下
  • P(Δ>0) = 0.197,只有不到两成的抽样里全特征占优。要下"全特征更好"的结论,这个数通常得奔着 0.95 去。所以不支持。
  • 这个结论和上面 McNemar 的 p=1.000 对上了——两招路子不同,都指向同一件事:这份数据上,两个模型分不出谁好。 交叉验证过一遍,心里就踏实了。

抽样的单位很关键。 表里第二种抽法(按行抽)给出的区间比按用户抽窄了 28%。看着更"精确",其实是假的:同一用户的 7 天被当成 7 个独立样本,等于凭空多出来许多"新信息",不确定性被系统性低估了。这和第19章那个"快照级指标虚高"是同一件事——抽样单位要和评估口径一致,评估按人(UID 级)就按人抽,别把一个人的多次快照拆开当多个人用。

再补一句 bootstrap 的边界:它估的是"这份评估样本上的不确定性",估不出"换一批人、换一段时间还一不一样"。后者得靠滚动起点(前面第 1 节)和换种子(第20章)来看。三种招数凑一起,才敢说一个结论稳不稳。

3) 偏置切片:谁被高估了

整体 Brier 0.178 好看,不代表每个子人群都被公平对待。按活跃度、新近度把测试集切开,看"模型平均预测 vs 实际回访率":

切片人数实际回访率平均预测偏差(正=高估)
高活跃(总领取 > 3.5)430.8600.917+0.056
低活跃(总领取 ≤ 3.5)140.3570.588+0.231
久没来(距上次 ≥ 4 天)150.4670.629+0.163
近期还来(距上次 ≤ 2 天)370.8380.914+0.076

审计结论很扎眼:模型系统性高估"低活跃、久没来"的人——低活跃那 14 人实际只有 36% 回来,模型平均给到 59%,高估了两成多。这不是随机抖动:它和第16章 U0009"满格误判"是同一件事在人群层面上的表现——对"看起来要凉的人",模型过分乐观

业务上的含义:如果运营拿"模型说 0.6"当成"六成会回",去决定一个久没来的人"不用管",会系统性错判。审计结果告诉我们要么对这类人单独设更低的阈值、更保守的分档,要么继续补特征(第12章说过,能抓 U0009 的是趋势这类更灵敏的特征,而不是校准)。

两个读表的坑,免得误读:

  • 片内 AUC 别当回事。高活跃片内几乎全是正样本(43 人 86% 都回),没得排序,AUC 只有 0.42——不是"模型在这片里差",是"片内太同质,AUC 失去意义"。切片审计主要看"预测 vs 实际"的偏差,不是片内 AUC。
  • 切片越小越抖。14~15 人的片,偏差 0.16~0.23 这个方向可信,具体小数别抠。

它和前后章节的关系

  • 第19章讲指标、第20章讲两种评估视角,本章把"时间"(滚动起点)和"人群"(偏置切片)两个维度再切一刀。
  • 第29章上线后的监控再训练,就是滚动起点评估的自动化版本;偏置审计发现的问题(低活跃被高估),决定了上线前要不要调阈值、补特征。

动手

  1. 附件/06_评估篇/activity_audit.py,对照上面三块输出:滚动表、McNemar、切片表。
  2. 把第 2 部分的"去趋势"改成"去新近度特征"(把脚本里 FEATS_NO_TREND 换成去掉 f_recency 的那份)再跑,看这次能不能分出差异——新近度可能是比趋势更"大"的特征,样本小也可能更明显。
  3. 附件/06_评估篇/activity_bootstrap.py,看那张 bootstrap 表:Δ 的 95% 置信区间是不是跨过 0、P(Δ>0) 离 0.95 还差多远;再把按行抽样和 UID 精确抽样两个区间宽度对比一下,看前者窄了多少。
  4. activity_bootstrap.py 里的 boot_by_uid 换成按行抽(或者反过来把行数改成只取 07-22 一天),看置信区间怎么变——评估样本越小、每人票数越少,区间就越宽,这是"样本量决定你能下多细的结论"。
  5. 低活跃人群被高估 0.23,运营怎么接?别用全局阈值;对低活跃/久没来的人单独设低阈值或用更保守的分档,或继续补特征。
  6. 如果滚动评估发现 Brier 一路恶化、AUC 也在掉,先查三样:人群结构是不是变了、特征口径是不是漂了、标签定义是不是改了——这三样变了,模型没变也是废的。
  7. 附件/06_评估篇/activity_walkforward.py,对照上面那张 7 点表。把评分日间隔从 2 天改成 1 天(AS_OF_LIST 那行的 2 * k 改成 k),重跑看评估点数变多少、相邻点数字是不是更接近——评分日越密,重复信息越多。
  8. 从 walk-forward 的 7 个点里只看最后一个(评 07-22)当"模型表现",再和均值 ± 一个标准差比——想想上线前报哪个数、监控阈值拿什么当参照线。

本章配套脚本 附件/06_评估篇/activity_audit.py(滚动起点评估 + 全特征 vs 去趋势的 McNemar 配对检验 + 按活跃度/新近度的偏置切片)、附件/06_评估篇/activity_bootstrap.py(同窗配对 + UID 精确 bootstrap:ΔPR-AUC 的 95% 置信区间、P(Δ>0)、按行抽样与按用户抽样的对比、换种子复现)、附件/06_评估篇/activity_walkforward.py(8 个隔天评分日、7 个扩展窗口评估点的 walk-forward 全协议 + 指标轨迹汇总)。用到的 numpy、scikit-learn、scipy 已在第4章 附件/00_公共/requirements.txt 列出,环境搭建见第4章。训练表由 附件/05_活跃度预测篇/activity_train_table.py 生成,口径一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值