Distribution-Level Contrastive Supervision for Generative Recommendation
USTC × Upwork × CUHK × NUS
把生成式推荐的监督从「离散 code 级」推进到「分布级」:RQ-VAE 每层对所有 codeword 的 softmax 亲和度(软分配)本身就是信息更丰富的语义表示,却在硬 argmin 时被丢掉。SODA 把用户历史经 Encoder AvgPool+MLP 投到 codebook 空间做同样的软量化,再用以负对称 KL 为打分的 BPR 损失,让本人历史分布比批内其他用户平均分布更贴近目标物品分布;作为辅助损失插入 TIGER/LETTER/ETEGRec 的交替训练,推理零开销。
核心思路
生成式推荐的 tokenizer(RQ-VAE)在量化每个物品时,其实已经算出了该物品到每一层所有 codeword 的距离,但最后只取 argmin 得到一个离散码,recommender 也只用这些硬 token 做交叉熵监督。这些被丢弃的「软分配」包含相似物品之间的细粒度语义差异,这正是论文要找回的信息。
SODA(Semantic-Oriented Distributional Alignment)的做法:
- 把目标物品在各层 codebook 上的 softmax 亲和度当作软语义目标 h_y;
- 把 recommender 编码器得到的用户历史表示投影到同一个 codebook 空间,做同样的软量化,得到历史分布 h+;
- 以批内其他用户历史分布的平均作为负例 h−,用负对称 KL 作打分、BPR 作损失,要求 h+ 比 h− 更接近 h_y。
它只是加在 recommender 训练里的一项辅助损失,解码和推理完全不变。
整体实现思路
- 预训练 tokenizer:RQ-VAE,重建 + 量化损失 L_token。
- 交替优化(沿用 ETEGRec):轮流更新 recommender R 和 tokenizer T。
- 在 R 的更新步中加入 L_SODA:总损失
L = L_rec + λ·L_SODA(ETEGRec 骨干另外保留其原有辅助损失)。 - 推理:与骨干相同,约束 beam search(beam=30)逐层生成 SID。
论文给出的开销:无推理开销,训练时间增加 不到 5%。
子模块实现(可复现细节)
1. Tokenizer:RQ-VAE
v_1 = E_T(z_i),c_l = argmin_k ||v_l − e_l^k||²,v_l = v_{l−1} − e_{l−1}^{c_{l−1}}- 重建:
z̃_i = D_T(Σ_l e_l^{c_l}) - 损失:
L_token = ||z̃_i − z_i||² + Σ_l ( ||sg(v_l) − e_l^{c_l}||² + α·||v_l − sg(e_l^{c_l})||² ) - 物品初始向量:sentence-t5-base 编码标题+描述,768 维(Beauty/Pet;Upwork 的物品向量来源未说明)。
2. Recommender
- T5 编码器-解码器,
L_rec = −Σ_{l=1}^{L} log P(Y_l | X, Y_{<l})。 - 其余配置和训练设置沿用 ETEGRec。
3. 目标分布 h_y
对目标物品向量 z_y 过 tokenizer 编码器,在每一层计算软分配:
p_{l,k} = exp(−||v_l − e_l^k||² / τ) / Σ_{j=1}^{K} exp(−||v_l − e_l^j||² / τ)
这是 Eq.1 硬 argmin 的软化,h_y = {p_{l,k}(y)},l=1..L、k=1..K。τ 越小越接近 one-hot。
4. 历史分布 h+ / h−
r_x = MLP(AvgPool(E_R(X))):recommender 编码器输出做平均池化,再经 MLP 投到 tokenizer 潜空间;- 对 r_x 做同样的逐层残差软量化,得到
h+ = {p+_{l,k}}; h−是批内其他用户分布的逐层平均,是单个平均负例而非多个负样本。作者的理由是:这样得到稳定的非目标参照,受语义相关的假负例影响更小。
5. 打分函数与损失
- 匹配分:
s(h_a, h_b) = −(KL(h_a||h_b) + KL(h_b||h_a)) / 2(负对称 KL) - BPR:
L_SODA = −log σ( β·( s(h+, h_y) − s(h−, h_y) ) ) - 总损失:
L = L_rec + λ·L_SODA
6. 超参搜索范围
| 超参 | 搜索范围 |
|---|---|
| λ(SODA 权重) | 1e-2 … 1e-6(按数量级) |
| τ(软量化温度) | 1 … 1e-5 |
| β(BPR 缩放) | 1 … 1000 |
| 序列长度 | 截断/填充到 20 |
| beam size | 30(约束 beam search) |
最终选用的取值和敏感性曲线正文都没有给出。
实验设置与结果
数据集
| 数据集 | #User | #Item | #Interaction | Sparsity | AvgLen |
|---|---|---|---|---|---|
| Beauty | 22362 | 12083 | 198313 | 99.93% | 8.87 |
| Pet | 19855 | 8498 | 157747 | 99.91% | 7.95 |
| Upwork(私有,雇主→自由职业者雇佣) | 15542 | 33508 | 139217 | 99.97% | 8.96 |
5-core 过滤,留一法,全量排序评测,指标为 Recall/NDCG@{10,20}。
主结果(生成式方法,Table 2)
| 数据集 | 指标 | TIGER | SODA-T | LETTER | SODA-L | ETEGRec | SODA-E |
|---|---|---|---|---|---|---|---|
| Beauty | R@10 | 0.0594 | 0.0698 | 0.0670 | 0.0741 | 0.0775 | 0.0809 |
| Beauty | R@20 | 0.0896 | 0.1010 | 0.1003 | 0.1046 | 0.1149 | 0.1147 |
| Beauty | N@10 | 0.0320 | 0.0385 | 0.0359 | 0.0414 | 0.0426 | 0.0440 |
| Beauty | N@20 | 0.0396 | 0.0463 | 0.0443 | 0.0491 | 0.0520 | 0.0525 |
| Pet | R@10 | 0.0541 | 0.0672 | 0.0628 | 0.0682 | 0.0686 | 0.0696 |
| Pet | R@20 | 0.0860 | 0.1000 | 0.0961 | 0.1024 | 0.1037 | 0.1053 |
| Pet | N@10 | 0.0278 | 0.0357 | 0.0323 | 0.0357 | 0.0365 | 0.0369 |
| Pet | N@20 | 0.0358 | 0.0440 | 0.0407 | 0.0443 | 0.0453 | 0.0459 |
| Upwork | R@10 | 0.0885 | 0.1012 | 0.0730 | 0.0811 | 0.0934 | 0.0935 |
| Upwork | R@20 | 0.1380 | 0.1568 | 0.1202 | 0.1257 | 0.1405 | 0.1421 |
| Upwork | N@10 | 0.0441 | 0.0508 | 0.0356 | 0.0403 | 0.0465 | 0.0467 |
| Upwork | N@20 | 0.0566 | 0.0647 | 0.0474 | 0.0515 | 0.0583 | 0.0589 |
传统基线中最强的是 GRU4Rec(Beauty R@10 0.0601)和 BERT4Rec(Upwork R@10 0.0855)。Upwork 上 SODA-T(0.1012)反而超过 SODA-E(0.0935)。
按 R@10 计算的相对提升(推断,由表中数字算出):
| 骨干 | Beauty | Pet | Upwork |
|---|---|---|---|
| TIGER → SODA-T | +17.5% | +24.2% | +14.4% |
| LETTER → SODA-L | +10.6% | +8.6% | +11.1% |
| ETEGRec → SODA-E | +4.4% | +1.5% | +0.1% |
消融(Beauty,Table 3)
| 变体 | R@10 | R@20 | N@10 | N@20 |
|---|---|---|---|---|
| SODA-T | 0.0698 | 0.1010 | 0.0385 | 0.0463 |
| w/o neg(点式,去负项) | 0.0639 | 0.0959 | 0.0366 | 0.0447 |
| w/o loss(去掉 L_SODA) | 0.0653 | 0.0978 | 0.0367 | 0.0449 |
| w/o alter(解耦训练) | 0.0594 | 0.0896 | 0.0320 | 0.0396 |
| SODA-L | 0.0741 | 0.1046 | 0.0414 | 0.0491 |
| w/o neg | 0.0685 | 0.0979 | 0.0387 | 0.0461 |
| w/o loss | 0.0653 | 0.0969 | 0.0376 | 0.0456 |
| w/o alter | 0.0670 | 0.1003 | 0.0359 | 0.0443 |
几点读法:
- SODA 损失本身的增益应看「完整 vs w/o loss」:SODA-T 0.0653→0.0698,SODA-L 0.0653→0.0741。TIGER→SODA-T 的 +17.5% 中有相当一部分来自交替训练(0.0594→0.0653)。
- w/o neg 在 SODA-T 上比 w/o loss 还差,说明不带负例的分布对齐反而有害,对比形式是必须的。
- w/o alter 的数值与 TIGER、LETTER 原始结果完全一致,这个变体可能直接复用了基线数字,是否还包含 SODA 损失需要存疑。
思考与可参考价值
它是什么:一个低成本的 encoder 侧辅助损失,把「用户历史」和「目标物品」放进 tokenizer 的软码本空间,用对比方式对齐。思路的价值在于提醒:RQ 量化的距离/软分配是现成的、免费的语义信号,只用硬 token 监督是浪费。
证据强度:偏弱。数据规模小(最多 3.4 万物品),没有方差和显著性检验,超参搜索空间很宽却不报告最终值;在本身就做交替训练的 ETEGRec 上,增益收窄到 0.1% 到 4.4%,这才是对「已有强基线的生产链路」更有参考意义的数字。
对电商 SID 生成式推荐的可借鉴点:
- 若 tokenizer 是 RQ / RQ-KMeans,直接用现成 codeword 距离算 softmax 软目标,加一个 MLP 投影头 + KL-BPR 损失即可试验,不动线上解码。
- 「批内平均分布作单个负例」在同质商品多的电商场景下值得一试,可以减少同簇商品被当负例的问题。
- 若线上 SID 冻结、不能交替更新 tokenizer,那么论文中的主要增益来源(交替训练)用不上,只能期待 w/o loss→完整 这一档的提升(推断)。
落地前要检查:
- 做 2×2 对照(交替训练开/关 × SODA 开/关),把两种来源的增益分开;
- 扫 τ:过小退化成 one-hot,过大分布过平,KL 信号失效;
- 关注 h+ 只来自 AvgPool 后的 encoder 表示,与逐层 decoder 状态无关,对 decoder-only 或带 Q-Former/PTR 的结构,需要重新定义池化位置并验证是否仍有效;
- 在百万到千万级商品池上验证,作者本人也把这一点列为未来工作。