瞿才应
← 回到主页的项目列表

个人项目 · 推荐评测

离线评测会不会把推荐模型的比较带偏

推荐的离线测试集来自线上日志,日志里的视频是旧策略挑过的。我用快手开源的两份数据当对照,KuaiRand 的随机曝光日志和 KuaiRec 近乎全观测的用户 × 视频矩阵,检验常规离线评测会不会改变“哪个模型更好”。

  • PyTorch
  • LightGBM
  • DCN-v2
  • DIN
  • MMoE
  • logQ 双塔
  • RQ-VAE 语义 ID
  • SASRec
resultsKuaiRand · KuaiRec
精排差距变化
0 / 21
Holm 校正后检出显著的对数
logQ 双塔对普通双塔
2.61×→1.23×
Recall@100 之比,常规测试→随机曝光,仍排第一
常规测试目标在最热组
75.7%
随机曝光上只有 26.8%
语义 ID 对双塔
+0.006→−0.032
Recall@100 差,稠密测试→只在实际被推过的视频里算
01

问题:离线测试集本身被旧策略筛过

模型在常规离线测试上分高,可能只是更会猜旧策略推了什么,而不是更懂用户。KuaiRand-Pure 的常规测试里,有 72.8% 的行落在最热的两成视频上。

KuaiRand-Pure 同时有两份日志,27,285 个用户。

常规日志

线上推荐策略推出来的,143.7 万条交互、7,551 个视频。

随机曝光日志

推荐流里把一部分位置换成从候选池随机抽的视频,118.6 万条、7,583 个视频。没被策略筛过,可以当对照。池里都是同一时期上传的新视频。

所以问题可以直接检验。同一批模型换一份没被筛过的测试集,“谁比谁好”会不会变?

研究一的设计

训练只用常规日志,主标签 is_click(近似有效播放)
冻结模型组精排 7 个:热度、逻辑回归、LightGBM、精排双塔、DCN-v2、DIN、MMoE;召回 4 个:热度、ItemCF、双塔、logQ 双塔
常规测试同一时期,同一场景
随机曝光测试同一时期,同一场景
比差距变了多少D = 随机上的差 − 常规上的差

同一批人比

两份测试集的用户不一样,各算各的会把“评的人不同”当成曝光偏差。精排只在两边都有正负样本的 10,965 人上比,召回是 14,786 人,每人等权。

主统计量是差距变化

不只看名次翻没翻,直接检验每对模型的差变了多少。精排 21 对一起做 Holm 校正,主指标是每人等权的 UAUC。

先定规则再训正式模型

判定规则在训练正式模型前写定,之前只跑过冒烟和只出标准误的试点,用来做功效检查。模型组冻结后测试集只开一次,开之前程序核对协议、代码和模型文件的哈希。

种子也要一致

LightGBM 和神经网络各训 3 个种子,成立要求至少 2 个同向。87 次训练在一张 RTX 3080 上 35 分钟跑完。

02

精排:差距没检出变化

7 个精排模型的 UAUC:常规测试和随机曝光

每行一个模型,两个点之间的线是同一个模型换测试集后的落差。悬停或点按看数值。

常规测试随机曝光
看数据表
模型常规测试随机曝光随机 − 常规
MMoE0.60650.5825−0.0240
精排双塔0.60560.5831−0.0225
DIN0.60490.5839−0.0210
DCN-v20.60480.5816−0.0232
逻辑回归0.60470.5817−0.0230
LightGBM0.59570.5756−0.0201
热度0.57460.5494−0.0252

7 个模型一起降了 0.020 到 0.025。拿随机曝光上的 UAUC 对常规测试做回归,斜率 1.06、R² 0.98,两两差距大体按原比例保留。21 对的差距变化没有一对在 Holm 校正后显著,也没有一对显著翻转。

两边都分得清的只有三档,热度最差,LightGBM 其次,其余五个更好。这五个彼此只差 0.002 左右,比检出门槛还小(差距变化的最小可检出差中位数 0.0065,真有这么大的变化约八成能检出)。所以只说“未检出变化”,不说“排序一致”。点估计的第一名从 MMoE 换成了 DIN,但这两者在两份测试集上的差都不显著。

03

召回:差距被压得很厉害,logQ 双塔仍是第一

常规测试 Recall@100

刻度 0 到 0.4

随机曝光 Recall@100

刻度 0 到 0.03,比左图小一个数量级

看数据表
模型常规测试随机曝光
logQ 双塔0.39020.0282
ItemCF0.32600.0265
热度0.21790.0228
普通双塔0.14920.0230

常规测试上 4 个模型在 0.149 到 0.390 之间,到随机曝光全挤在 0.023 到 0.028。完全随机排序的期望约 0.013,模型只有它的 1.7 到 2.1 倍,绝对差缩小有一部分只是整体都被压低了。

logQ 双塔对普通双塔的领先从 0.241 缩到 0.005(95% 区间 0.004 到 0.007),在随机曝光上仍然显著第一。按比值看,从 2.61 倍降到 1.23 倍,相对优势也缩了,但还在。6 对的差距变化全部显著,没有显著翻转。

事先写的两条召回假设方向都反了

预期热度相对 logQ 双塔会更吃亏,实际热度的落后从 0.172 缩到 0.005;预期 logQ 的优势会扩大,实际从 0.241 缩到 0.005。按规则都不成立,照实报告。

logQ 校正是什么

双塔把同一批里其他行的视频当负样本,热门视频出现得多,会被过度打压。logQ 在训练时把 logit 减去视频在训练正样本里的对数频率。它校正的是批内采样偏差,不是曝光偏差。测试集打开后补了固定温度的对照,只算探索。按 Recall@100 看,0.241 几乎全来自 logQ,换温度的影响不到 0.01。

04

为什么常规测试把 logQ 的领先放大了

视频按训练期曝光数等分成五组,每组两成视频。常规测试约四分之三的目标在最热的一组。

召回目标在五个热度组里的占比

每组都是两成视频

常规测试随机曝光
看数据表
热度组最冷较冷中间较热最热
常规测试0.6%2.3%5.2%16.2%75.7%
随机曝光15.6%16.9%18.8%21.9%26.8%
logQ 双塔前 100 的占位0.2%0.3%1.6%9.8%88.2%
普通双塔前 100 的占位16.0%18.3%20.7%22.5%22.5%

logQ 双塔推的前 100 里有 88% 是最热组的视频,普通双塔各组差不多。分组看命中率,两份测试上 logQ 都只在最热组赢、其余四组都输。

常规测试:logQ 双塔 − 普通双塔

各热度组的组内 Recall@100 之差

随机曝光:logQ 双塔 − 普通双塔

同上,刻度约小五倍

看数据表
组内 Recall@100最冷较冷中间较热最热
logQ 双塔 · 常规0.00330.02750.05680.16810.4750
普通双塔 · 常规0.19820.21830.22630.22690.1248
logQ 双塔 · 随机0.00020.00060.00230.01070.0944
普通双塔 · 随机0.01900.02310.02200.02720.0224

常规测试 75.7% 的目标在最热组,正好是 logQ 赢的地方,所以它的领先被放大。把常规测试按随机曝光的热度组成重新加权,logQ 的领先就没了,普通双塔反而高 0.014。

那它在随机曝光上为什么还是第一?重新加权用的还是常规测试的组内命中率。到了随机曝光,普通双塔领先的那四组,组内召回掉了八到十倍;logQ 领先的最热组只掉了五倍,最热组又占随机目标的四分之一多,所以 logQ 还剩 0.005 的领先。

这组分解是测试集打开后补的分析,只作描述,不是检验。

05

点击率校准和混训随机数据

点击率在随机曝光上整体高估事先写定,成立

训练期正样本率约 0.53,随机测试只有 0.18。学到的 6 个模型直接用时 LogLoss 是 0.73 到 0.75,比报一个常数(0.46)还差。越冷门高估越多,逻辑回归在最冷组的预测约为实际的 3.6 倍,最热组约 2.3 倍。

在随机曝光的校准集上,只调截距就修掉大头;Platt 同时调斜率还能再降,说明偏差不只是整体平移;按热度分 5 组各拟合 Platt、再向全局收缩,又降 0.0020 到 0.0034,7 个模型全部成立。

训练时混一点随机数据补充实验

LightGBM 训练轮数拉平后,往训练集里加相当于原训练集 5.5% 的随机曝光行,随机测试的 UAUC 从 0.576 提到 0.602;加同样多的更新的常规行,只高 0.0003,不显著。

训练行和测试来自同一份随机日志,只是日期不同,提升可能有一部分来自贴合测试分布;只试了一个模型、一种比例,不能当线上效果。

06

研究二:换到 KuaiRec,看生成式召回

研究一的随机曝光只去掉一种偏差,而且只在 7,583 个新视频的池子里成立。KuaiRec 有一块 1,411 个用户 × 3,327 个视频的小矩阵,约 99.6% 的格子有观看记录,可以直接看一个用户对全部候选的反应。它也不是纯随机曝光,快手先挑出彼此看过很多的用户和视频,再把没看过的格子补齐;视频文本的采集时点不明,可能晚于日志。我在上面实现了语义 ID 生成式召回,和 SASRec、带文本的 logQ 双塔、热度、打乱语义 ID 一起比。正样本是播放时长超过视频时长 2 倍。

语义 ID 生成式召回怎么做的

文本向量标题、封面文字、话题、类目 → Qwen3-Embedding-0.6B,1,024 维
语义 IDRQ-VAE 压到 32 维,3 层残差量化 × 64 个码,再加第 4 位区分码
生成只有解码器的 Transformer 读最近 50 个正样本的码,生成下一个视频的 4 个 token
解码前缀树约束的束搜索,只生成这个用户的候选,取前 100

模型约 328 万参数,给 1,121 个测试用户各排一遍,GPU 上不到 1 秒。打乱语义 ID 用同一套码随机打乱,码的集合和前缀树形状不变,只去掉视频和文本的对应,用来看文本语义有没有用。

四种测试,前三种用同一份推荐列表

稠密测试

候选是用户在小矩阵里有记录的视频,每个正样本同权。1,121 人。

有偏测试

同样的目标,按曝光量计权,曝光量是训练期其他用户在这个视频上的行数。

借用曝光

给每个用户随机配一个别的用户,只把对方验证期实际看到的视频算作目标,抽 20 次取平均。1,117 人。

自然日志

另一批 4,872 个用户之后几天的正样本,另一套候选。

前三种用的是同一批用户的同一份前 100,只换目标和权重;第四种换了人。

5 个模型在四种测试上的名次(按 Recall@100)

从上到下第 1 到第 5 名,彩色是正文提到的三个模型

热度文本语义 ID双塔SASRec、打乱语义 ID
看数据表
Recall@100稠密有偏借用曝光自然日志
热度0.25710.38620.04950.0334
SASRec0.22600.28150.12200.0764
文本语义 ID0.19960.25490.08110.0467
双塔0.19460.23770.11270.0648
打乱语义 ID0.12100.16340.04950.0418

借用曝光上热度 0.049486、打乱语义 ID 0.049494,只差约 0.00001,未检出差异,图上按点估计排先后。

稠密测试和自然日志给出的排序对不上,10 对里 5 对方向相反,Kendall τ-b 是 0。按曝光量计权不改变稠密测试的排序,只把热度的领先再放大 0.074 到 0.087。同一批用户、同一份前 100,只换成在别人实际被推过的视频里算,排序就和自然日志一样。热度从第一掉到最后。

文本语义 ID − 双塔,同一批 1,117 人、同一份前 100

两边都显著,差距变化 3 个种子都同号

看数据表
1,117 人上的 Recall@100稠密测试借用曝光
文本语义 ID0.19830.0811
双塔0.19270.1127
差+0.0057−0.0317

这批人是有借用目标的 1,117 人,比上面名次图的 1,121 人少 4 个,所以稠密测试的值略有不同。

语义 ID 在稠密测试上领先双塔 0.006,只在实际被推过的视频里算就落后 0.032。换成不剪枝的精确打分,方向也不变(领先约 0.031、落后约 0.020)。我的理解是,借用的目标是当时推荐流里实际在推的视频,双塔有视频编号向量,更容易记住训练期哪些视频常被一起看;语义 ID 用文本码表示视频,推荐会往文本相似的方向偏。这一条没单独检验。

研究二的其他发现

  • 前 100 没赢 SASRec,但头部更准。按 Recall@100,文本语义 ID 在稠密测试上低 0.026、自然日志上低 0.030;稠密测试的 NDCG@20 反而高 0.034。
  • 束搜索剪掉了不少好视频。文本语义 ID 在束宽 100 下的前 100 和精确打分的前 100,人均只重合 53% 到 64%,Recall@100 少了约 12% 到 13%。精确打分下它在稠密测试上和 SASRec 点估计接近、未检出差异(0.2259 对 0.2260),借用曝光和自然日志上仍落后,所以搜索只解释了一部分。测试集打开后补的
  • 冷启动没看到优势。训练窗口里没有交互的 555 个视频,所有模型几乎都召不回,文本语义 ID 只召回 0.07%。
  • 打乱对照踩过的坑。“文本好于打乱”按事先写定的规则成立,+0.079,但大头来自打乱模型被早停留在了第一个 epoch。两个语义 ID 模型至少训 5 个 epoch 再挑,只剩 +0.011。两个模型又是各自调参的,这 0.011 也不能全算到文本语义上。这是开测试集前试跑时发现的,判定规则没改,偏离记录在案。
  • 种子之间的差比区间大一个数量级。用户 bootstrap 的区间不含训练随机性,所以成立还要求 3 个种子里至少 2 个同号。
07

能说什么,不能说什么

能说

  • 在这批数据和模型上,常规离线评测没有把精排的比较带偏到能检出的程度。
  • 常规离线评测会大幅放大召回的绝对差距。把常规测试按随机曝光的热度组成重新加权,logQ 双塔的领先就没了(事后补的描述)。
  • 只限制哪些正样本能被观测到,就足以改变召回模型谁好谁差。

不能说

  • 不能当线上效果。随机曝光仍是事后收集的离线数据,测不到新模型上线后的闭环、位置影响和留存时长。
  • 精排“未检出”不等于等效,排除不了比门槛更小的变化。
  • 热度分解、logQ × 温度、混训、束搜索误差都是测试集打开后补的,不改正式结论。
  • 自然日志是另一批用户、另一套候选,τ = 0 不能全归到曝光方式上。

落到工程上

  • 精排强模型之间离线分不出来,不为离线差的 0.001 选型,挑两三个进 A/B。
  • 召回常规离线的绝对差距不能直接信,logQ 双塔的领先到随机曝光上从 0.241 缩到 0.005,不过它仍然第一。
  • 点击率随机曝光上整体高估、越冷门高估越多,要用随机曝光样本按热度分组校准。长期留一小桶随机流量做无偏评估和校准,桶要小,也要限定在候选池里。
  • 生成式召回束宽要和候选池、码表一起调,上线前拿一小批用户和精确打分对一次。
08

术语速查

召回、精排
推荐通常分几步:召回先从全部视频里粗筛出几百个候选,精排再给这些候选逐个打分排序。
UAUC
每个用户内部算 AUC,再每人等权平均。推荐关心同一个用户内部的排序,全局 AUC 还会奖励把爱点的用户整体排前面。
Recall@100
用户的目标视频里,有多少落进模型推的前 100 个。
差距变化 D
同一对模型换测试集后差距变了多少。研究一是随机曝光上的差减常规测试上的差,研究二是稠密测试上的差减有偏或借用曝光上的差。
Holm 校正
多对比较一起做显著性检验时控制整体犯错率的方法,比 Bonferroni 稍宽松。
logQ 校正
批内负采样时,按视频在训练正样本里的频率扣掉 logit,免得热门视频被过度打压。
Kendall τ-b
两种排序有多一致,1 是完全一致。0 表示名次一致和相反的模型对一样多,互相抵消。
语义 ID
把视频的内容向量逐层量化成几个离散码,内容相近的视频共用前缀,生成模型逐位生成。
束搜索
逐位生成时每一步只留得分最高的若干个前缀;前缀下视频多时容易把好视频剪掉。
→

其他项目