第20章 两种评估视角(时间外推 vs 用户互斥)
小白一句话
同一份数据可以切成两种评估:一种训练用早的、测试用晚的(时间外推 ),考"对老用户过几天还准不准";一种训练和测试是完全不同的人(用户互斥),考"对从没见过的新用户还准不准"。两个问题不一样,数字也会不一样------而且样本小的时候数字还会自己抖。
这一章在干嘛
第16、17、19章从头到尾只用了一种切法:时间外推。本章补上第二种切法:用户互斥,然后把两者放在一起对比。
为什么需要两种视角?因为每日评分要给两类人打分:
- 存量用户:天天见的老人,评分日那天他们都有历史特征。
- 新用户:刚注册、特征还浅甚至为零的人。
两类人对应两种评估视角,缺一不可。核心观念就一句话:切法 = 回答的问题 = 防泄露的规则,这三件事是一体的。
视角 A:时间外推(存量用户)
做法(和第16-19章完全一样):
- 训练 = 07-08 + 07-15(105 行),测试 = 07-22(57 行)。同一批人,晚 7 天。
- 回答的问题:对"今天的老用户",明天打的分还准不准(时序稳定性)。
- 防泄露:测试集的标签窗(07-23~07-29)整体在训练特征之后,加上净空期(第8章),不会穿越。
跑出来(sigmoid 校准后):AUC 0.698 、Brier 0.178 、PR-AUC 0.805 ------和第 14/16 章一模一样。这不是巧合:日期切分是确定的,不靠随机抽人,所以每次跑都是这几个数。
视角 B:用户互斥(未见用户泛化)
做法:
- 把 57 个用户随机分成两份:2/3 进训练(38 人)、1/3 进测试(19 人)。每个用户的所有行(3 个评分日)都进同一边。
- 回答的问题:对"从没见过、但有历史"的人(比如新注册但已经领过几天的人),规律还成立吗(泛化能力)。
- 防泄露:同一用户的多行样本绝不能跨组------它们几乎一样(近重复样本,第8章"把评分日撑密"那个坑);不同用户之间特征、标签互不相干,天然不泄露。
跑出来(种子 20260827,sigmoid 校准后):AUC 0.794 、Brier 0.156 、PR-AUC 0.935。
等一下------用户互斥怎么比时间外推还高?这不合理吧。
先别急着比大小:这次"反直觉"的三个原因
三个原因叠在一起,让单次对比没有意义:
- 任务不一样。时间外推考的是"一周后的老用户",测试集正好包含 U0009、U0025 这类开始掉的人(第 14/15 章见过他们),任务更难;用户互斥考的是随机 19 人横跨三天的快照,里面混着 07-08 的早期快照------那时几乎人人活跃,任务更简单。
- 基准率不一样。测试集正样本率 0.765 vs 0.737,两组人本来就不完全同质。
- 样本太小。19 个测试用户、51 行,谁进测试组,数字就跟着变。
第三点最要命。把用户划分的种子换几个,用户互斥的 AUC 直接跳:
| 用户划分种子 | 测试用户数 | AUC |
|---|---|---|
| 20260827 | 19 | 0.794 |
| 12345 | 19 | 0.778 |
| 999 | 19 | 0.651 |
| 7 | 19 | 0.668 |
这里的种子只决定"哪批用户进测试组"(随机打乱 57 人后取 1/3 当测试),和聚类的种子、模型的种子都不是一回事------模型训练本身固定 random_state=20260827 不动,波动纯粹来自换了一批测试用户。
0.65 ~ 0.79 之间跳。单点数字别说"0.79 比 0.698 好",就是拿 0.65 去说"模型对新用户不行"也站不住------换批人测,数字就变了。
反过来说,这也是时间外推数字为什么稳的原因:日期切分是确定性的,不靠抽人。所以前面几章拿它当主评估是合适的。
结论:两种视角不要直接比大小排名。各自回答各自的问题,而且要报告"重复实验的分布"(多换几个种子),而不是单个数字。这和第19章"单点小样本数字别过度解读"是同一件事的两种表现。
冷启动和这两种视角的关系
用户互斥测的是"从没见过、但有历史"的新人------它回答的是新用户打分能信几分 (大约 0.65~0.79,看分布)。它测的不是零历史的人:零历史连特征都不齐,根本进不了模型(第17章三个坑之一),直接走第10章的冷启动三级路由。
所以业务上这么对号入座:
- 存量用户的分数:看时间外推评估的成绩单(AUC 0.70、Brier 0.18 那套)。
- 新用户的分数:看用户互斥评估 + 冷启动路由兜底------模型对没见过的人没把握,别拿"看起来还挺高"的单次数字当保证。
它和前后章节的关系
- 第16-19章一直用时间外推评估;本章补上第二种视角,并演示"换种子就抖"。
- 下一章(第21章)讲稳定性与偏置审计:把同一个指标放到不同切片(不同用户群、不同时间段)上看,怎么判断"某个数字是好是坏"。
- 第29章上线后的监控再训练,看的是时间维度上的漂移------本质还是"时间外推视角"的持续版。
一个实操建议:正式项目的评估报告里,两种视角都写。时间外推的数字给运营看(老用户),用户互斥的数字给算法团队看(新用户泛化),别用一个数糊弄两边。
动手
- 跑
附件/06_评估篇/activity_eval_views.py,对照上面两个视角的数字和换种子表。 - 把脚本里的
(12345, 999, 7)换成别的种子(比如 2024、42)再跑,看 AUC 跳到哪------体会"19 个测试用户的评估是个会跳的数字"。 - 思考:如果明天新用户占新增流量的一半,运营拿着时间外推的 0.698 来问"新用户也这样吗",你怎么答?答的方向:新用户该看用户互斥 + 冷启动路由,0.698 只对老用户成立。
- 再想一层:用户互斥如果改成按用户首次出现时间切(早注册的进训练、晚注册的进测试),测的又是什么?(答:测"模型对更新的一批用户适配得怎样"------一种介于随机切和冷启动之间的视角。)
本章配套脚本
附件/06_评估篇/activity_eval_views.py(时间外推 vs 用户互斥两种切法 + 换种子方差演示,同一 GBDT + sigmoid 校准)。训练表由附件/05_活跃度预测篇/activity_train_table.py生成。用到的 numpy、scikit-learn 已在第4章附件/00_公共/requirements.txt列出,环境搭建见第4章。