🏠 总目录📚 本教程 07 · 没标签怎么发现退化 ← →
📑 本页目录(点开跳转)

07 · 没有标签怎么发现退化

⏱ 42 分钟 | ⭐ 最容易被忽略的盲区


🎯 一句话

教科书假设你知道对错,现实里你常常不知道。 退货要等 7 天、欺诈要等 30 天、用户会不会续费要等一年 —— 这段时间里,你靠什么判断模型还活着?


⏳ 一、标签延迟有多常见

场景 标签什么时候到 延迟
点击率预估 几秒内 ✅ 几乎实时
转化率预估 用户下单后 小时~天
退货预测 收货 7 天后 1–2 周
信贷违约 首次逾期 1–3 个月 ⭐
欺诈 用户投诉 / 对账 数周,且很多永远不来 ⭐
续费预测 合同到期 1 年

🔑 注意欺诈那一行:很多标签永远不会来。 没被发现的欺诈,在你的数据里就是「正常交易」—— 你的标签本身就是有偏的,这是比延迟更深的问题(见第五节)。

操作步骤

⭐ 一个残酷的算术:
信贷模型标签延迟 3 个月
你今天发现的问题,是 3 个月前就开始的
这 3 个月里放出去的所有贷款,都已经是坏决策了

🧭 二、没有标签时,能看什么(五种代理信号)

操作步骤

  1. 按「有多接近真实指标」排序:
  2. 输入侧:特征分布漂移 ← 第 6 章,最早的信号
  3. 输出侧:预测分布变化 ← 第 5 章
  4. 行为侧:用户对结果的即时反应 ⭐ 最有价值
  5. 一致性:模型之间互相验证 ⭐
  6. 抽样:人工标一小批 ← 最准,最贵

③ 行为侧代理:找一个「早期标签」

核心思路:真实标签来得晚,但和它相关的某个行为来得早。

最终标签(慢) 早期代理(快) 相关性
是否退货(2 周) 是否点了「查看退货政策」(当天) 中
是否违约(3 个月) 首期是否按时还(1 个月) 高 ⭐
是否续费(1 年) 月活跃天数、核心功能使用率(当月) 中高
是否欺诈(数周) 风控规则命中数、设备异常分(实时) 中

操作步骤

⭐ 用法不是「用代理替代真实标签」,而是:
① 用【历史数据】算出代理和真实标签的相关性
② 只要这个相关性是稳定的,就能用代理做【早期预警】
③ 代理指标异动→提前几周知道要出事
④ 真实标签到了之后,回头验证代理是否还可靠 ⭐

⚠️ 第 ④ 步不能省:代理和真实标签的关系本身也会漂移。 每次真实标签到齐后,都要重新算一次相关性 —— 否则你会依赖一个已经失效的代理。


④ 一致性检查:让模型互相验证

三种做法,都不需要标签:

结果对照

A. 新旧模型对比
同一批请求,新旧模型的预测差异突然变大
至少有一个出问题了
B. 冠军-挑战者(champion-challenger)
线上跑主模型,同时用影子流量跑一个【简单模型】(如逻辑回归)
简单模型不容易坏,可以当参照系 ⭐
两者差异突然变大 = 复杂模型可能出问题了
C. 自洽性检查
同一个用户,特征只变了一点点,预测却剧烈变化
模型不稳定

⭐ B 特别值得做:一个简单模型作为「基线哨兵」。 它的效果一定不如主模型,但它的稳定性是主模型的参照系。 成本很低(逻辑回归几乎不占资源),价值很高。


⑤ 人工抽样:最准的那把尺子

关键信息

每天/每周随机抽 100~500 条,人工标注真实答案
得到一个【小样本但无延迟】的准确率估计
⭐ 关键设计:
· 必须【随机抽】,不能只抽模型不确定的(那会高估错误率)
· 要固定流程和标注规范,否则标注本身会漂
· 样本量要够算出可用的置信区间(第 12 章会讲怎么算)

💡 一个省钱的技巧:分层抽样

关键信息

按模型预测的置信度分层,每层抽一些:
· 高置信层抽少一点(错的概率低)
· 低置信层抽多一点(信息量大)
再按层的占比加权还原总体准确率
⭐ 同样的标注预算,估计精度明显更高

🔬 三、把它们组合成一套预警体系

关键信息

时间轴 →
T+0 特征分布漂移告警 ← 最快,但可能误报
T+0 预测分布异常
T+1天 行为代理指标异动 ⭐
T+1周 人工抽样准确率下降
T+3月 真实标签确认 ← 最准,但太晚
⭐ 越早的信号越不准,越晚的越准
早期信号用来【触发调查】,不用来【自动回滚】

🔑 这条时间轴决定了告警该怎么设计: T+0 的信号 → 通知人去看一眼(因为它可能误报) T+1周 的信号 → 可以触发回滚决策 把早期信号接成自动回滚,会因为频繁误报而被关掉(第 8 章)。


💀 四、一个特别的陷阱:模型自己制造的标签缺失

关键信息

风控模型拒绝了一笔贷款
这笔贷款没有发生
【永远不会知道】它会不会违约 💀
你的训练数据里,只有【被通过的】那些人的标签
模型只在"它自己认为安全的"人群上学习

这叫「拒绝推断」(reject inference)问题,本质上是第 1 章那个反馈闭环的极端形式。

三种常见缓解:

做法 说明 代价
随机放行一小部分 ⭐ 对被拒绝的人随机通过 1%,拿到无偏标签 真实损失,但是唯一能拿到干净数据的办法
用外部数据补 征信、第三方数据看他在别处的表现 有合规限制
建模推断 用统计方法估计被拒人群的表现 依赖假设,可能自我强化

🔗 注意第一条和第 2 章的「留 1% 随机流量」是同一个思想: 主动付出一点代价,换一批没有被模型污染的数据。 在推荐里它对抗幸存者偏差,在风控里它对抗拒绝推断。


📋 五、可以照抄的落地清单

逐项核对

  • 弄清每个标签的实际延迟(很多团队从没量过)⭐

  • 找到 1-2 个行为代理指标,并算出它和真实标签的历史相关性

  • 上线一个简单模型当「基线哨兵」(冠军-挑战者)⭐

  • 建立人工抽样流程,固定频率和标注规范

  • 留一小份随机流量/随机放行,作为无偏数据源 ⭐

  • 画出你的「信号时间轴」,明确哪个信号触发什么动作

  • 真实标签到齐后,回头验证代理指标是否仍然可靠

⭐ 第一条最容易被跳过,也最重要: 很多团队根本不知道自己的标签延迟是多少。 量一下(画个标签到达时间的分布图),你会发现它比想象的长, 而且有很长的尾巴。


💀 六、代理指标最危险的失效方式:模型把它优化坏了

前面说"代理和真实标签的关系会漂移"。最狠的一种漂移,是你自己造成的。

结果对照

某内容平台,真实目标是【次日留存】(要等一天)
选了【完播率】当代理指标(几秒内就有)
历史相关性 r = 0.61 —— 看起来是个不错的代理 ✅
三个月后,模型开始把完播率当优化目标之一:
短视频天然完播率高→推荐里短视频占比从 31% 涨到 68%
完播率从 42% 涨到 54%(+12pp)🎉
次日留存却从 38.5% 掉到 37.3%(−1.2pp)💀
重新算相关性:r 从 0.61 掉到 0.08
时点 完播率 次日留存 两者相关性
选定代理时 42% 38.5% 0.61 ✅
三个月后 54% 37.3% 0.08 💀

⭐ 这是 Goodhart 定律的教科书案例: 一个指标一旦变成优化目标,它就不再是个好指标。 代理之所以能当代理,是因为在原来的策略下它和真实目标相关; 而模型改变了策略本身。

🔑 三条防线: 1. 代理指标只用来"预警",不用来"优化" —— 一旦它进了损失函数或排序公式,它就死了。 2. 定期重算相关性(至少每月),把 r 本身画成一条曲线监控起来。 3. 留一份不受模型影响的对照:一小份随机流量上,代理和真实指标的关系是干净的。

🔗 第 15 章专门讲代理指标和长期效应,这里是它的入口。


🔨 七、三段可以直接跑的代码

① 先量一下你的标签延迟到底是多少

第五节说"很多团队从没量过"。量它只要几行:

import pandas as pd, numpy as np

def label_delay_profile(df, t_pred="predict_time", t_label="label_time"):
    """df 里每行是一次预测;label_time 为空 = 标签还没来"""
    arrived = df[t_label].notna()
    d = (df.loc[arrived, t_label] - df.loc[arrived, t_pred]).dt.total_seconds() / 86400

    print(f"标签到达率 {arrived.mean():.1%}")          # ⭐ 这个数常常远小于 100%
    for q in [.5, .8, .9, .95, .99]:
        print(f"  P{int(q*100):<3} {d.quantile(q):6.1f} 天")
    # ⭐ 关键:右侧尾巴有多长决定了你的"评估窗口"该留多久
    print(f"  最长 {d.max():.1f} 天")
    return d

关键信息

一个真实场景跑出来的结果(退货预测):
标签到达率 93.2% ← ⚠️ 有 6.8% 永远不会来
P50 2.1 天
P80 6.8 天
P90 11.4 天
P95 19.7 天 ← ⭐ 尾巴比想象长得多
P99 58.3 天
⭐ 结论:如果你按"7 天后统计准确率",
实际只覆盖了 80% 的样本,而且【漏掉的那 20% 系统性偏向慢退货】
你算出来的准确率是【乐观偏差】的

② 冠军-挑战者的差异监控

from scipy.stats import spearmanr

def champion_challenger_gap(p_main, p_simple, base_gap):
    """p_main / p_simple: 同一批请求上两个模型的预测分数"""
    # ⭐ 用【排序相关性】而不是绝对差值——两个模型的分数尺度本来就不同
    rho = spearmanr(p_main, p_simple).statistic
    mean_shift = abs(p_main.mean() - p_simple.mean())
    alerts = []
    if rho < base_gap["rho"] - 0.15:                 # ⭐ 和历史基线比,不是和 1.0 比
        alerts.append(f"两模型排序相关性 {base_gap['rho']:.2f}→{rho:.2f}")
    if mean_shift > base_gap["shift"] * 2:
        alerts.append(f"两模型均值差扩大到 {mean_shift:.3f}")
    return alerts

⚠️ 一个必须提前想清楚的问题:差异变大时,是谁坏了? 单看这一个信号回答不了。但它能把范围从"整个系统"缩小到"这两个模型之一", 再结合特征漂移(第 6 章)就能定位。 哨兵的价值是缩小搜索空间,不是直接给答案。

③ 分层抽样的权重还原

def stratified_accuracy(labels, preds, strata, strata_pop_share):
    """strata: 每条抽样样本所属的层;strata_pop_share: 各层在总体中的占比"""
    acc, se2 = 0.0, 0.0
    for s, w in strata_pop_share.items():
        m = strata == s
        if m.sum() < 5:                               # ⭐ 层内样本太少,估计不可信
            print(f"⚠️ 层 {s} 只有 {m.sum()} 条")
        a = (labels[m] == preds[m]).mean()
        acc += w * a
        se2 += (w ** 2) * a * (1 - a) / max(m.sum(), 1)   # ⭐ 方差也要按权重合成
    return acc, se2 ** 0.5                            # 返回 (加权准确率, 标准误)

🧭 八、按标签延迟选方案(决策表)

你的标签延迟 主力手段 辅助 别做什么
< 1 小时 直接看真实指标 特征漂移兜底 别为它建复杂的代理体系
1 天 ~ 1 周 行为代理 + 每日真实指标 ⭐ 冠军-挑战者 别按小时看真实指标(样本不够)
1 周 ~ 1 月 人工抽样 + 行为代理 ⭐ 特征漂移、哨兵模型 别等标签齐了才评估
> 1 月 随机放行/随机流量 + 早期代理 ⭐ 全部手段都要上 💀 别只靠真实标签做决策
标签有系统性缺失(欺诈、风控) 随机放行是唯一干净来源 外部数据交叉验证 💀 别用有偏标签算"准确率"

⚠️ 无标签监控的五个坑:

坑 后果 怎么修
代理指标进了损失函数 Goodhart,代理失效且不可逆 💀 代理只用于预警 ⭐
抽样只抽模型不确定的 高估错误率,看起来一直在恶化 严格随机 + 分层
按"标签已到达"的样本算指标 乐观偏差(慢标签系统性缺席)⭐ 固定观察窗口,窗口内未到达的按规则处理
哨兵模型也跟着重训 两个模型一起漂,参照系失效 💀 哨兵长期冻结,只在明确的时点更新 ⭐
标注规范没写下来 标注本身在漂,指标变化分不清来源 固定规范 + 定期一致性抽检

⭐ 第四条特别值得记:哨兵模型的价值来自"它不变"。 一旦你也定期重训它,它就和主模型一起漂走了 —— 参照系没了。


🔗 九、和站内其他章的关系

相关的地方 和这一章的关系
第 2 章留随机流量 同一思想的另一个应用
第 6 章概念漂移 它需要标签,所以才有这一章
推荐算法 02反馈闭环 拒绝推断是它的极端形式
推荐算法 13探索 随机放行 = 探索的风控版

✅ 检查点

  1. 标签延迟为什么是个严重问题?用信贷的例子说明。
  2. 欺诈场景的标签有什么比"延迟"更深的问题?
  3. 五种代理信号是什么?按什么排序的?
  4. 行为代理的正确用法是什么?哪一步不能省?为什么?
  5. 「冠军-挑战者」为什么值得做?
  6. 人工抽样为什么必须随机抽?分层抽样怎么省钱?
  7. 为什么早期信号不该接自动回滚?
  8. 什么是拒绝推断?唯一能拿到干净数据的办法是什么?
  9. 「完播率」那个事故里发生了什么?它是什么定律的案例?三条防线是什么?
  10. 量标签延迟时,为什么要看 P95 而不只是中位数?「按 7 天统计准确率」会有什么偏差?
  11. 冠军-挑战者的差异为什么要用排序相关性而不是绝对差值?这个信号能回答"是谁坏了"吗?
  12. 标签延迟超过一个月时,主力手段应该是什么?
  13. 为什么哨兵模型不能跟着重训?
👀 答案
  1. 因为你今天发现的问题是延迟那么久之前就开始的。信贷延迟 3 个月 → 这 3 个月里放出去的所有贷款都已经是坏决策了。
  2. 很多标签永远不会来——没被发现的欺诈在数据里就是"正常交易",标签本身就是有偏的。
  3. ①特征分布漂移 ②预测分布变化 ③行为侧即时反应 ④模型间一致性 ⑤人工抽样。按有多接近真实指标排序。
  4. 用法是用历史数据算出代理和真实标签的相关性,只要相关性稳定就能做早期预警。不能省的是最后一步:真实标签到了之后回头验证代理是否还可靠——因为代理和标签的关系本身也会漂移。
  5. 因为简单模型不容易坏,可以当参照系;它效果不如主模型,但稳定性是主模型的参照系。成本极低(逻辑回归几乎不占资源)。
  6. 必须随机抽是因为只抽模型不确定的会高估错误率。分层抽样:按置信度分层,高置信层少抽、低置信层多抽,再按占比加权还原——同样预算精度更高。
  7. 因为越早的信号越不准、可能误报;接成自动回滚会因频繁误报而被关掉。早期信号应该触发调查而不是自动动作。
  8. 风控拒绝了一笔贷款 → 它没发生 → 永远不知道会不会违约,训练数据里只有被通过的人。唯一能拿到干净数据的办法是随机放行一小部分(付出真实损失换无偏标签)。
  9. 完播率被选为次日留存的代理(r=0.61),但模型开始优化它之后,短视频占比从 31% 涨到 68%,完播率 42%→54% 而次日留存 38.5%→37.3%,相关性掉到 0.08。这是 Goodhart 定律:一个指标一旦变成优化目标,它就不再是个好指标——代理能当代理是因为"在原来的策略下"相关,而模型改变了策略本身。三条防线:①代理只用来预警,绝不进损失函数/排序公式 ②定期重算相关性并把 r 本身监控起来 ③留一份不受模型影响的随机流量做干净对照。
  10. 因为尾巴比想象长得多(真实例子:P50 只有 2.1 天,但 P95 是 19.7 天、P99 是 58.3 天,还有 6.8% 永远不来)。按 7 天统计只覆盖约 80% 的样本,而漏掉的 20% 系统性偏向慢退货 → 算出来的准确率有乐观偏差。
  11. 因为两个模型的分数尺度本来就不同,绝对差值没有可比性;而且要和历史基线比,不是和 1.0 比。它回答不了"是谁坏了"——但它能把范围从"整个系统"缩小到"这两个模型之一"。哨兵的价值是缩小搜索空间,不是直接给答案。
  12. 随机放行 / 随机流量 + 早期代理,且全部辅助手段都要上。绝不能只靠真实标签做决策——等它到齐时损失早已发生。
  13. 因为哨兵的价值来自"它不变"。一旦跟着定期重训,它就和主模型一起漂走了,参照系就没了。应该长期冻结,只在明确的时点更新。

🛑 可以停在这里

⚡ 走神救援

⭐ 教科书假设你知道对错,现实里常常不知道:退货等两周、违约等几个月、续费等一年。⭐⭐ 残酷算术:标签延迟三个月,意味着你今天发现的问题三个月前就开始了,这期间的决策全是坏的。

⚠️ 欺诈那一侧还更深一层:很多标签永远不会来——没被发现的欺诈在数据里就是「正常交易」,标签本身有偏。

五种代理信号按「离真实指标多近」排:特征漂移 → 预测分布 → ⭐ 行为代理 → 模型间一致性 → 人工抽样。

⭐ 行为代理是找一个来得早、又和最终标签相关的行为(违约看首期还没还上)。⚠️ 用法有个必须做的回头动作:真实标签到齐后要验证代理是否还可靠——那个相关关系本身也会漂。 ⭐ 冠军-挑战者:拿影子流量跑一个简单模型当哨兵——它效果差,但它的稳定性是主模型的参照系,成本极低。 ⚠️ 人工抽样必须随机:只抽不确定的会高估错误率。

⭐ 信号的时间轴决定告警怎么设计:早期信号只触发调查,不接自动回滚——否则会因误报被人关掉。

💀 代理指标最狠的失效方式是你自己造成的:拿完播率当次日留存的代理,模型开始优化它之后,短视频占比翻倍、完播率涨了十几个点,而次日留存反而跌,两者的相关性掉到几乎为零。⭐ 这就是古德哈特定律:一个指标一旦变成优化目标,它就不再是个好指标。三条防线:代理只用来预警、绝不进损失函数;每月重算相关性并把相关系数本身监控起来;留一份随机流量做干净对照。

📏 ⭐ 先量你自己的标签延迟:中位数可能只有两天,而尾部要拖近三周、还有几个百分点永远不来——按「N 天后统计准确率」只覆盖一部分样本,而漏掉的那部分系统性偏慢,算出来的准确率带乐观偏差。

💀 五个坑里最反直觉的一个:哨兵不能跟着重训——⭐ 它的价值恰恰来自它不变。

下一节 👉 08-告警为什么没人看.md

打卡记录保存在你的浏览器里,首页能看到总进度