读完 TIGER、LETTER 或 OneRec,最容易留下两个错觉:Semantic ID 只是把商品编号换了一种写法;beam search 也不过是“多保留几个候选”。真正把代码跑一遍,会发现困难恰好藏在这两句话省略的部分——向量为什么能被离散化、离散地址怎样进入生成目标、合法 token 为什么仍能拼成不存在的商品,以及最后究竟该评估推荐质量还是生成质量。
这份 tutorial 把整条链路压缩到一个可以从头运行的教学系统里。它与「20 篇论文读懂生成式推荐」共用同一条叙事主线,但不要求你先读完所有文章。
先选一种运行方式
你可以按自己的目的选择三种入口:
| 入口 | 适合什么 | 你能改什么 |
|---|---|---|
| 本页交互实验 | 先建立直觉,几分钟走完整条链路 | 商品、temperature、beam width、decode depth、Trie 开关与 Python |
| 下载完整 Notebook | 本地 Jupyter / VS Code 中逐格实验 | BPR 训练、codebook、Semantic ID、解码器与评测 |
| 在 Colab 打开 | 不安装环境,复制一份到自己的 Google Drive | Notebook 中的全部代码与参数 |
本页实验只使用八件商品,强调交互反馈;Notebook 使用 12 件商品和八条用户历史,会真正训练一个小型 BPR、拟合多层 residual codebook,再运行有约束和无约束的生成与评测。两者讲的是同一件事,只是放大倍数不同。
实验地图:它和系列文章怎样对应
| 实验阶段 | 系列中的对应章节 | 这一阶段要回答的问题 |
|---|---|---|
| 连续打分 | 01 · BPR | 推荐系统原本怎样学习用户与商品向量? |
| 序列变成 token | 02 · GRU4Rec、03 · SASRec、04 · BERT4Rec | 为什么用户历史不能再被压成一个静态点? |
| 检索变成生成 | 10 · DSI、11 · TIGER | 模型怎样直接写出目标的离散地址? |
| 设计 Semantic ID | 13 · LC-Rec、14 · LETTER、15 · ETEGRec | 好 ID 应该保留内容、协同信号,还是服务最终推荐损失? |
| 统一生成 | 17 · OneRec | 召回与排序能否共享同一个生成目标? |
| 失败与泛化 | 2026 · CARE、2026 · 泛化审计 | 深层误差、非法 ID 与 unseen item 应该怎样测? |
0. 我们到底在优化什么
设用户历史为 (x_{1:t}),传统打分模型为每个候选商品计算 (s(u,i)),再从目录中取 Top-K。生成模型则把目标商品写成 (M) 个离散 token:
这个改写带来三个新接口:
- Tokenizer:连续商品向量怎样变成稳定、有区分度的离散地址;
- Decoder:用户条件怎样影响每一层 token 的概率;
- Catalog constraint:怎样保证生成的完整地址确实对应商品。
Notebook 会把三个接口分开实现,避免“一个大模型把一切都做了”掩盖真正的工程边界。
1. 从 BPR 得到商品向量
Notebook 先把每条用户历史的最后一件商品留作 next-item 检查,再使用其余隐式反馈训练一个 NumPy 版 BPR。对用户 (u)、正样本 (i) 和负样本 (j),目标是:
你会看到 pairwise loss 下降、每个用户的 Top-3 结果,以及商品 embedding 的二维投影。这里最重要的不是 toy Recall@3 数字,而是确认后续 tokenizer 接到的是由推荐行为学习出来的连续表示。
对应系列:BPR 解释打分目标;LC-Rec 则追问只用内容语义是否足够。
2. 用 Residual Quantization 得到 Semantic ID
第 (m) 层 codebook 只解释上一层留下的残差:
四层 codebook 最终给每件商品一个四 token 地址。前缀表达粗粒度区域,后续 token 逐步修正重构。Notebook 会同时输出:
- 每件商品的完整 Semantic ID;
- 每增加一层后的平均 residual norm;
- ID 碰撞率;
- 每层 token 使用分布。
低重构误差并不自动等于好推荐。两个商品可能在向量空间很近,却属于不同协同人群;一个 codebook 也可能重构很好,却把大量商品挤进少数 token。TIGER、LETTER 和 DIGER 的区别,正是在“谁来定义好 ID”。
3. 把推荐改写成下一 token 生成
页面里的可视化使用固定 logits,让你能立即调节参数。Notebook 则根据生成出的 Semantic ID 统计一个带 smoothing 的 toy token model。它不是 Transformer,但保留了自回归解码最关键的计算:
- Temperature 改变分布尖锐程度;
- Beam width 决定每层保留多少条前缀;
- Decode depth 让你观察误差怎样沿层级累积;
- 完整前缀 而不是单个 token,才是目录中的真实地址。
这一步连接 DSI 的“生成即检索”、GPTRec 的列表生成与 OneRec 的统一生成目标。
4. 为什么 Trie 约束不是可选装饰
假设每一位 token 都来自合法词表,完整序列仍可能不对应任何商品。无约束模型会把不同商品的高概率局部片段拼在一起,得到一个目录外 ID。Trie 在每一步只保留至少能通向一件真实商品的前缀:
页面允许你直接关闭约束;Notebook 会把 beam 中合法和非法的地址画成两种颜色,并对多个 temperature 采样。默认实验通常会看到无约束采样的大量地址落在目录外,而 Trie 约束后的 invalid-ID rate 精确为零。
注意:Trie 只能保证“商品存在”,不能保证“商品适合这个用户”。它是解码合法性层,不是推荐相关性模型。
5. 评测不要只留一个 Recall@K
一个完整实验至少要把四组指标拆开:
| 层面 | 最小指标 | 它防止什么误判 |
|---|---|---|
| 推荐质量 | Recall@K、NDCG@K、分人群/头尾商品结果 | 模型能生成,不等于推荐得准 |
| Tokenizer | 重构误差、碰撞率、codebook usage、前缀负载 | 向量重构漂亮,不等于 ID 可用 |
| 解码 | invalid-ID rate、catalog coverage、延迟、beam 成本 | Top-1 看似正常,长尾 beam 可能已经失控 |
| 泛化 | unseen-item、冷启动、时间外推、目录变更 | 记住训练目录,不等于会泛化 |
Notebook 的最后几个 cell 会运行可失败的 assertions:BPR loss 必须下降、残差必须缩小、Semantic ID 长度必须一致、Trie 输出不能出现非法 ID。它们很小,但比只展示一张漂亮图更接近可复现研究。
6. 推荐的动手顺序
第一次先保持默认参数完整运行,然后一次只改一件事:
- 把 temperature 改成
0.4与1.4,比较非法率和 catalog coverage; - 把 beam width 改成
4与32,观察更多候选是否只是暴露更多非法组合; - 把 codebook size 改成
3或6,记录重构误差、碰撞与 token 使用率; - 把 codebook 层数改成
2或5,观察深度与误差累积; - 最后把 BPR embedding 换成内容 encoder 或序列模型输出。
第 3、4 步对应 LETTER 与 CARE;第 5 步会把你带向 LLaRA、HSTU、ContRec 与 DIGER。
7. 这份教程没有假装复现什么
本页与 Notebook 都是机制实验,不是 TIGER、OneRec 或 DIGER 的 benchmark 复现。它们没有真实规模的序列 Transformer、端到端 tokenizer 学习、负载均衡损失、分布式 beam search,也没有线上延迟和 A/B 测试。
它们真正提供的是一条可检查的骨架:每一个中间表示都能打印,每一个解码选择都能改,每一个合法性假设都能被关闭。下面先用页面实验建立直觉;需要完整代码路径时,再进入 Notebook。
FULL NOTEBOOK · 完整实验
从训练 BPR 到评估非法 ID,逐格运行。
24 个教学 cells、11 个已执行代码 cells,包含四张可视化与可失败的结果检查。无需 GPU。COMPANION LAB · 交互实验
先把商品变成一种可以生成的语言。
选择一件商品。图上的位置来自四层 code vector 相加;共享前缀的商品会先落在同一片粗粒度区域,再被后续 token 逐步分开。02 · RESIDUAL QUANTIZATION
每一层只负责解释上一层没解释完的部分。
橙色终点是商品向量。四段箭头依次加入 code vector;越靠后的 token,修正越细。图中是教学用二维投影,不代表真实 RQ-VAE 只有两个维度。目标向量[0.64, 0.53]
重构结果[0.64, 0.53]
剩余残差[0.00, 0.00]
03 · AUTOREGRESSIVE DECODING
生成一个 ID,就是在商品目录树里一步步选路。
下面的概率来自同一个固定 toy model。Temperature 改变分布的尖锐程度,beam width 决定保留多少条路,decode depth 决定走到第几位。Token 1
Token 2
Token 3
Token 4
同一个模型,两种解码结果
无约束模型最喜欢的完整地址并不一定对应真实商品。Trie 在每一步屏蔽非法前缀,让概率只能流向目录中的路径。04 · EDITABLE PYTHON
现在改代码,而不只是看动画。
代码会在你的浏览器里真实执行。第一次运行需要加载约 12 MB 的本地 Python 核心;之后再次运行会复用已经启动的环境。输出idle
点击“运行 Python”查看 beam search 结果。
边界说明:这个 lab 用 8 件商品和手写 logits 教机制;完整复现还需要真实商品 encoder、RQ-VAE 训练、序列数据与离线评测。