最近在组里做论文分享挑了一篇挺有代表性的工作——ETEGRec全称是 End-to-End Learnable Item Tokenization for Generative Recommendation翻译过来就是“端到端可学习的物品分词与生成式推荐”。这篇文章的核心思路非常直接像NLP 里把句子分词成 token 一样把推荐系统里的物品也拆成一小段离散 token然后用一个生成式模型去预测用户下一个会交互的物品。如果你正在做序列推荐、生成式推荐或者平时搞搜推广但对 NLP 的分词器、量化编码这类技术感兴趣这篇很适合拿来读一读。它没有依赖大模型却把“分词 生成”这套玩法搬到了推荐场景里而且整个过程是端到端可微分的不是先训练一个分词器再单独训一个推荐器那种两段式做法。下面我就从背景、方法、实验到复现细节把我读下来的理解和踩坑经验一并写出来。1. 为什么需要端到端的物品分词1.1 传统推荐系统的“语义天花板”传统序列推荐最主流的做法是把每个用户的历史行为表示成一个物品 ID 序列比如 [1024, 2048, 3072]然后丢给 SASRec、BERT4Rec 这类 Transformer 模型去建模。物品 ID 本质上就是一个原子 token模型只知道它是第几号物品不知道它“是什么”“有什么属性”。这套做法的天花板很明显不同物品 ID 之间没有任何共享信息ID 的表示完全靠协同过滤信号一点点学出来。遇到长尾物品、新物品交互数据少ID 的 embedding 就学不充分冷启动物品的推荐质量会明显下降。你有没有发现很多线上系统对热门物品越推越准对冷门物品基本是瞎猜根因之一就在这里ID embedding 缺乏语义先验。要打破这个天花板就得让物品表示携带语义信息。那问题是怎么把语义信息塞进一个模型友好的离散结构里这就引出了物品分词的想法。1.2 从 NLP 分词到物品分词NLP 中文本处理早就不是整句 one-hot 了而是把句子拆成 subword token比如 BPE、WordPiece。这样模型可以复用“词根”“前缀”这些共享子结构遇到没见过的词也能通过已有 token 组合出来。物品分词想干的事情一模一样把每个物品表示成一段离散 token 序列比如一部手机可以拆成 [电子, 手机, 旗舰, 5000mAh]一部耳机拆成 [电子, 耳机, 无线, 降噪]。这样一来不同物品之间就有了共享的子 token“电子产品”“耳机”“降噪”这些概念可以在不同物品间复用长尾物品也能借助高频子 token 获得较好的初始表示。更重要的是物品变成 token 序列之后推荐任务就顺理成章地变成了生成任务用户历史行为是一段 token 序列预测下一个物品等价于生成下一段 token 序列。这就是生成式推荐的核心逻辑。1.3 端到端到底“端”在哪里“端到端”这个词这两年听得很多自动驾驶有端到端方案网络测试里有端到端的发包收包测试甚至运维也提全链路端到端监控。放到这里端到端有两层含义。第一层是架构层面的端到端物品编码器、分词器、推荐 Transformer 是一整条可微链路输入是物品原始属性输出是推荐结果中间没有人工拆模块。第二层是目标层面的端到端分词器不只是做一下物品重建它还要被推荐的监督信号反向优化。也就是说分词结果好不好不是只看能不能重建物品更要看它能不能帮模型更好地预测用户行为。这两层都很关键。早期一些工作也做过物品 token 化但大多是先用自编码器训好一个离散编码器冻结住再拿 token 序列去训推荐模型。这么做的问题是分词器看不见推荐目标它学到的离散表示可能对“预测下一个物品”这个任务并不友好。ETEGRec 的卖点就是把这条链路真正打通让推荐任务反向指导分词器该怎么分。2. ETEGRec 的核心方法拆解2.1 物品分词器用残差量化把物品变成 TokenETEGRec 的物品分词器走的是 VQ-VAE 那条路线具体来说是用残差量化。我先解释一下残差量化的直觉把物品的属性特征比如标题文本的 embedding通过一个编码器得到连续向量 h然后把它量化到离散 codebook 上。普通 VQ 是一次性把一个向量映射成一个 token但一个 codebook 容量有限一次量化丢的信息太多于是就有了残差量化第一层 codebook 负责捕捉主体信息剩下没编码完的残差再交给第二层、第三层逐层细化。比如说物品标题是“Apple iPhone 15 Pro 256G”编码器得到向量 h第一层量化可能对应“手机”残差里还有“iPhone”“Pro”“256G”这些信息后面几层 token 会逐步把这部分补上。最终这个物品就表示成一段长度为 L 的 token 序列比如 [token_1, token_2, token_3, token_4]。这里有几个参数很关键codebook 层数决定 token 序列长度层数越大信息越丰富但序列越长后续 Transformer 计算量越大每层 codebook 容量通常设置为 256 或 512表示每层最多能表示多少个离散原型量化过程用最近邻查找也就是把编码器输出向量和 codebook 里的向量做距离比较取最近的那个注意这里有一个技术难点最近邻查找的 argmax 操作不可导梯度没法从 codebook 传到编码器。VQ-VAE 的做法是 straight-through estimator简单说就是前向传播走 argmax反向传播把梯度原封不动地拷贝给编码器输出。ETEGRec 也沿用了类似思路同时加了 commitment loss 约束编码器的输出不要离 codebook 太远。2.2 生成式推荐器把用户行为序列当文本读物品一旦变成了 token 序列用户的历史行为就不再是物品 ID 序列而是“token 序列的拼接”。举个例子用户历史物品是 [item_1, item_2, item_3]每个物品分词为 4 个 token那模型输入就是一段长度为 12 的 token 序列。ETEGRec 用 Transformer decoder 来做生成式推荐。训练时给定用户历史 token 序列模型要做的是预测“下一个物品对应的所有 token”。怎么理解这个目标你把它想象成语言模型预测下一个词只不过这里的“词”不是一个 token而是一小段 token 块对应一个完整物品。具体来说假设预测目标是 item_4 的 token 序列为 [t1, t2, t3, t4]那么模型在生成 t1 时看到的是历史 token 序列生成 t2 时看到的是历史 token 序列加上 t1生成 t3 时看到的是历史加上 t1、t2依此类推。每个位置的预测都做交叉熵损失。这个做法的好处是模型在预测一个物品时有机会“逐 token 地”展开它而不是把整个物品表示压成一个分类 logit。这有点像机器翻译里逐词生成句子而不是一步到位的序列分类。生成式模型的表达能力更强也允许我们在推理时用 beam search 或者采样去探索多样化的推荐结果。2.3 端到端训练的关键离散梯度怎么传刚才提到最近邻查找不可导ETEGRec 要端到端训练就必须处理离散 token 的梯度问题。我总结下来主要有三个手段配合使用。第一是 straight-through estimator前向用 argmax 选 token反向时把 loss 对 token 的梯度直接当作对编码器输出的梯度近似绕开 argmax 的不可导问题。第二是 Gumbel-Softmax 松弛训练前期不直接做 argmax而是用 softmax 对 codebook 向量加权求和温度系数从高到低退火。温度高的时候多个 codebook 向量都有权重梯度比较平滑温度低的时候分布越来越接近 one-hot最终趋向离散。ETEGRec 训练时会有一个温度退火策略不能一上来就低温否则梯度很容易就崩掉了。第三是联合损失里面加 commitment loss让编码器输出和对应 codebook 向量别差太远保证 straight-through 的近似是合理的。这三大件缺一不可。我在复现的时候试过只保留 straight-through去掉 Gumbel 温度退火训练初期 loss 降得很快但过了几千步 codebook 利用率骤降大量 token 变成“死 token”。后面把温度退火加回来才稳定下来。2.4 完整训练目标整个模型的训练目标由三部分构成推荐损失预测下一个物品 token 序列的交叉熵这是主目标重建损失从量化 token 重建物品特征保证分词器没有丢失太多语义信息commitment loss约束编码器输出贴近 codebook 向量三个 loss 通过权重系数组合在一起。端到端意味着推荐 loss 的梯度会一路传到编码器更新物品表示这是和“训练好分词器再训推荐器”最大的区别。我画一条简单的数据流帮你理解物品属性 - 编码器 - 残差量化 - token 序列 - Transformer decoder - 预测下一段 token - 推荐 loss - 梯度回传到编码器这条链路里面没有任何一个中间模块是冻结的全部一起更新。3. 实验分析效果到底怎么样3.1 实验设置ETEGRec 的实验主要在 Amazon Review 数据集上做具体选了 Beauty、Sports 和 Toys 这几个子集原因是这些品类物品属性比较丰富适合验证“分词生成”的思路。评估指标用的是序列推荐的常规指标RecallN 和 NDCGN。baseline 我列一下对比对象方便你知道它站在什么肩膀上SASRec经典的自注意力序列推荐方法BERT4Rec双向 Transformer 的序列推荐方法FDSA融合物品特征和 ID 的序列推荐方法M6-Rec把推荐任务转成自然语言生成的大模型方法TIGER早期用生成式方式做物品推荐的模型用 Item ID 作为生成目标3.2 核心结论第一个结论是端到端一定优于两阶段。论文里比较了先训分词器再训推荐器的两阶段版本和真正端到端联合训练的版本。两阶段版本在训练上更稳定时间也短但效果明显低于端到端。这说明推荐任务提供的监督信号确实在引导分词器调整 token 分布这个信号是重建任务给不了的。第二个结论是长尾物品提升最明显。因为分词器让不同物品共享子 token冷门商品也能借助高频 token比如“无线”“降噪”“粉色”获得合理表示。类似 NLP 里 subword 能让模型处理未登录词一样物品分词让模型有机会处理交互很少的新物品。这一点我复现时观察到了同样的趋势按交互数量把物品分成 head、tail 两组tail 组的 Recall 提升幅度比 head 组高出 5 到 8 个点。第三个结论是生成式框架的推理方式很灵活。因为物品已经 token 化成了一段序列推荐时可以像文本生成一样做 beam search而不是在千万级物品池里做 softmax 分类。物品池越大这种生成式做法的扩展性优势越明显。分类式推荐在物品数是百万级别时输出层的计算量非常可观而生成式推荐只要维护一个 vocab 很小的 codebook输出维度只有 codebook 大小长度等于物品 token 数。3.3 蒸馏版可视化对比方法物品表示粒度是否共享语义是否端到端推理方式SASRec物品 ID不共享不适用全物品打分BERT4Rec物品 ID不共享不适用全物品打分TIGER物品 ID不共享是生成式M6-Rec自然语言文本共享是生成式依赖 LLMETEGRec可学习离散 Token共享是生成式从表格里可以清楚看到 ETEGRec 的定位它处于“共享语义”和“端到端生成式”这个象限但不依赖大模型和自然语言模板完全自己学出一套物品词表。这让它在成本和灵活性上都比大模型方案更可控尤其适合对推理延迟有要求的推荐场景。4. 复现指南与避坑记录4.1 环境与数据准备复现前需要准备的资源PyTorch 环境推荐 2.0 以上版本单张 24GB 显存的卡基本够跑 Amazon Beauty 这种中等数据集如果跑 Sports 或更大数据建议 40GB 以上物品属性预处理把标题、类别、品牌等字段拼成一个纯文本串然后用一个轻量 tokenizer比如 BPE做预分词。这一步的作用是把物品属性变成语义 token后续的可学习分词会在语义 token 基础上进一步做残差量化我自己跑的时候预处理环节踩过一个坑物品属性字段缺失率很高很多 item 只有标题没有品牌。如果你直接把空字段拼进文本串会把“缺失”也当成语义信息。建议对缺失字段做空串处理必要时加一个特殊占位符但别把一大堆“空值”拼进文本里面。4.2 关键超参数选择我复现试下来比较稳的参数是这样的codebook 层数4对应每个物品 4 个 token每层 codebook 容量256token 序列拼接方式用户历史物品每个展开成 4 个 token拼接成模型输入序列长度按物品数计50温度退火从 5 退火到 0.1每几千步衰减一次别一步到位优化器AdamW学习率编码器和 Transformer 取 1e-4codebook 向量更新用 1e-3 或者 EMA 更新这里面温度退火最需要调。不是所有任务都适合同一个起止温度如果你的物品属性文本比较短信息量小温度可以起得低一点如果文本很长信息量大需要高温期更长让 codebook 充分探索。4.3 常见问题排查我复现过程中遇到比较多的问题整理成一张速查表给你参考。现象可能原因解决办法codebook 大量 token 不被使用codebook collapse用 EMA 更新码本加 commitment loss定期检测并重置死 token训练初期 loss 不降Gumbel 温度太高softmax 分布太平滑适当降低初始温度或用 straight-through 替代 Gumbel效果不如两阶段端到端训练不稳定先单独预训练分词器若干 epoch再解冻推荐 loss 联合微调长尾物品没提升分词后 token 序列表达力不足增加 codebook 层数或容量推理太慢生成时逐个 token 自回归解码用树状 beam search或对高频 token 前缀做缓存剪枝关于 codebook collapse我要多说一句。这是所有 VQ 类模型的通病表现为最后只有少数几个 token 被模型高频使用其余变成死 token。解决办法最有效的是 EMA 更新 codebook而不是用梯度更新。EMA 的好处是码本更新速度不受模型梯度尺度影响死 token 也更容易被“救活”。如果你发现救不活可以统计每个 token 的使用频次把低频 token 的向量随机重置。4.4 实践中的两个小技巧第一个技巧是“物品重建 loss 别给太大权重”。我一开始把重建 loss 的权重拉得很高想让分词器尽量保留信息结果推荐 loss 被压制模型变成纯自编码器推荐的提升反而不明显。后来把重建 loss 权重降到 0.1 到 0.3 之间推荐效果变好了很多。道理也不复杂分词器保留的信息不一定要多但一定要和推荐相关重建只是辅助。第二个技巧是“两阶段预训练 端到端微调”。严格意义的端到端直接训很容易崩我的做法是先用重建任务把分词器热身几百个 step让码本先稳定下来再打开推荐 loss 联合训练。这本质上还是端到端只是给了一个更好的起点。这个做法论文里没有细讲但实操中非常管用。5. 从 ETEGRec 到更广的思考读完这篇论文我最大的感受是推荐系统和 NLP 的边界正在变得越来越模糊。过去我们习惯把物品当成一个无结构 ID现在越来越多的工作在用 NLP 的方式重新定义推荐问题。物品分词把 ID 换成了可复用的离散 token生成式推荐把打分换成了逐 token 生成端到端又把分词器和推荐器绑定在一起。我个人觉得这篇工作的贡献不在于它用了多复杂的模型而在于它把一个“分词 生成”的通用框架落到了推荐场景并且证明了这个框架不依赖大模型也能取得好结果。它对长尾物品的提升尤其有价值因为推荐系统里真正难的不是把热门物品推给所有人而是把合适的冷门物品推给少数真正需要它的人。如果你看了这篇之后想动手试试我建议先从 TIGER 复现一个基础生成式推荐然后再换成 ETEGRec 的可学习分词这样能明显感受到端到端分词带来的差异。跑实验的时候留意 codebook 利用率和长尾品类的分指标别只看整体 Recall。祝顺利。