← 文章 / AI技术
Eugene Yan 17小时前 · 2026-09-06 02:29:10 · 4 阅读

训练 LLM-RecSys 混合模型:利用 Semantic IDs 实现可控推荐

当我初次听说 Semantic IDs 时,我就忍不住深究起来。这个想法很简单:我们不再用随机哈希 ID 来标识视频、歌曲或商品,而是使用 LLM 能够原生理解的语义 token。我好奇的是,能否利用那些让当代推荐系统如此高效的丰富行为数据,来训练一个 LLM-推荐系统混合模型?

令我惊讶的是,我们确实可以!结果得到了一个既能用英语对话、又能用 item ID 对话的语言模型——它不依赖检索或其他工具,而是一个单一的“双语”模型,item(即语义 ID)成为了其词汇表的一部分。像推荐模型一样,它可以根据历史交互来推荐 item。但最大的惊喜——以及能力上的突破——是我发现只需与模型对话即可引导其推荐方向,它还能推理自己的选择、提供解释,并创造性地为商品组合命名

下面是一个演示视频。这里是 代码,用于准备数据、训练模型以及与之对话。(注意:这是一个小型模型,经过了非常基础的微调,因此提示词的效果至关重要。此外,由于微调有限,它的通用性和鲁棒性不如大多数 LLM。)

等等,这有什么用?

这项技术融合了推荐系统和语言建模各自的长处。一方面,语言模型拥有世界知识,能优雅地描述商品,但却不了解我们的商品目录。而且,它们的推荐往往千篇一律,并受流行度偏见影响。相反,搜索和推荐模型基于我们的商品目录和数十亿用户交互进行训练,擅长预测用户下一步会点击或购买什么,却无法通过自然语言进行引导,也不能对其选择进行推理。(不过,它们的共同点在于都基于序列进行学习。)

为此,我们首先通过添加 <|sid_0|><|sid_1|><|sid_2|> 等语义 ID token 来扩展语言模型的词汇表,用这些 token 来表示商品目录。然后,我们对其继续进行预训练,以学习语义 ID 与目录之间的关系,接着再在用户行为序列上进行进一步微调。综合这两步,模型便学会了基于用户偏好从我们的目录中进行推荐。

这种 LLM 与推荐系统的混合方案,精度上或许比不上专门的多阶段推荐系统,但它带来了一种全新的能力:推荐的可控性与可解释性。最终效果是搜索、推荐和聊天融为一体,用户只要开口说出需求,就能在我们庞大的商品目录中找到想要的东西。

用户:我喜欢动物类和可爱的游戏。<|rec|>

模型解析自然语言查询(“动物类和可爱”),并为相关游戏生成 Semantic ID。

<|sid_start|><|sid_173|><|sid_324|><|sid_764|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_201|><|sid_397|><|sid_701|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_173|><|sid_305|><|sid_670|><|sid_768|><|sid_end|>

助手:《动物森友会:新叶》、《DISNEY INFINITY Starter Pack 3DS》、《任天狗 + 猫:金毛寻回犬与新伙伴》

数据与模型

寻找同时包含商品元数据和用户行为序列的训练数据

我们使用 Amazon Reviews 2023(Hou 等,2024)中电子游戏品类的数据。这份数据有丰富的商品元数据,我们可以据此构建用户交互序列。

数据集包含 13.7 万个商品,每个商品都有标题、描述、特性、品类、店铺信息、评分、价格等字段。过滤掉标题不足 20 字符或描述不足 100 字符的商品后,剩下 6.6 万个。行为数据有 73.7 万条记录,我据此构建了 7.9 万条用户购买序列,每条至少包含三个有效商品,平均长度为 6.5 个商品。

我还考虑过 Amazon KDD Cup 2023 数据(Amazon,2023)。它有 50 万个商品,侧重序列行为数据,但多语言特性增加了处理复杂度,而且缺少商品品类字段,用起来很麻烦。最终我选择了 Amazon Reviews 数据集,先从简单的做起,也节省算力预算。

用 RQ-VAE 生成 Semantic ID

Semantic IDsRajput 等,2023Singh 等,2023)是一种层级化表示方法,将物品编码为 token 序列,用以替代 embedding 或基于哈希的 ID。与毫无语义含义的常规物品 ID(如 B0040JHNQG)不同,Semantic ID(如 <|sid_0|><|sid_256|><|sid_512|><|sid_768|>)能够编码物品的信息。在训练过程中,相似物品会自然共享公共前缀,形成类树状结构,ID 的每一层代表关于物品的愈发精细的信息。

Image

美食视频 Semantic IDs 的层级结构(来源

残差量化变分自编码器(Residual Quantized Variational Autoencoders,RQ-VAEs;Zeghidour 等,2021Lee 等,2022)是我们用来将连续 embedding 转换为离散 Semantic ID 的工具。我们首先将物品的元数据(如标题、描述)编码为 embedding,随后 RQ-VAE 利用层级量化将其转换为离散 token 序列。

Image

RQ-VAE 如何将 embedding 转换为 Semantic ID(来源

这是一个迭代过程。在第一层,模型会在第一个码本中找到与输入 embedding 最接近的向量;该向量成为 Semantic ID 的第一个 token。接着,模型通过从输入 embedding 中减去所选的码本向量来计算量化误差(即残差)。在第二层,它在第二个码本中找到与该残差最接近的向量,从而得到第二个 token。该过程逐层重复,每一步都能捕获前一层次未能捕捉到的更精细细节。

RQ-VAE 的损失函数值得深入讨论,因为理解它是生成高质量 Semantic ID 的关键。整体损失由两个主要部分组成:

\[L(x) = L_\text{recon} + L_\text{rqvae}\]

第一项是重建损失,确保解码器能够从最终的量化表示($\hat{x}$)中准确还原出原始的物品嵌入($x$)。它是一个标准的平方误差损失:

\[L_{recon} = ||x-\hat{x}||^2\]

第二项是量化损失,用于衡量码本向量与编码器生成的残差之间的匹配程度。它包含两项:

\[L_{\text{rqvae}} := \sum_{i=0}^{m-1} \left[ \|\text{sg}[r_i] - e_{c_i}\|^2 + \beta\|r_i - \text{sg}[e_{c_i}]\|^2 \right]\]

第一项($|\text{sg}[r_i] - e_{c_i}|^2$)是码本损失,用于训练码本嵌入。它衡量编码器残差($r_i$)与被选中的码本向量($e_{c_i}$)之间的距离。对编码器的输出($\text{sg}[r_i]$)使用停止梯度操作,将残差视为固定目标。因此,梯度仅流向码本向量,使其向编码器的输出靠拢。

第二项($\beta|r_i - \text{sg}[e_{c_i}]|^2$)是承诺损失,用于训练编码器。它衡量相同的距离,但停止梯度作用于码本向量($\text{sg}[e_{c_i}]$)。这会阻止码本的更新,迫使编码器产生输出,或承诺采用码本中已存在的向量。超参数 $\beta$ 控制这一承诺惩罚的强度。

# Pytorch code for loss function (without recursive loop)
reconstruction_loss = F.mse_loss(x, x_reconstructed)

codebook_loss = F.mse_loss(residual.detach(), codebook_vector)
commitment_loss = F.mse_loss(residual, codebook_vector.detach())
quantization_loss = codebook_loss + commitment_weight * commitment_loss

total_loss = recon_loss + quantization_loss

通过这一过程,RQ-VAE 生成语义 ID,即每个量化层级各取一个 token 的序列。由于相似物品共享常见前缀,语言模型能更好地理解商品之间的关系,这对基于树的检索同样有帮助。

# 如何将 embeddings 层级编码为 semantic IDs
def encode_to_semantic_ids(self, x: Tensor) -> Tensor:

    with torch.no_grad():
        residual = self.encode(x)
        indices_list = []

        for vq_layer in self.vq_layers:
            vq_output = vq_layer(residual)
            indices_list.append(vq_output.indices)
            residual = residual - vq_output.quantized
        
    return torch.stack(indices_list, dim=-1)

不过,这种方法有一个实际问题:无法保证每个商品的 ID 都是唯一的。在我用三级 codebook(每级 256 个码)做的实验中,6.6 万个商品里约有 10% 出现了碰撞。为了解决这个问题,我加了第四级——给每个 ID 追加一个递增的序号 token,确保每个商品都能被唯一标识。

SASRec、Qwen3-Embedding-0.6B 和 Qwen3-8B

除了 RQ-VAE,我们还用到另外三个模型。首先,训练一个基于 semantic IDs 的 SASRec,用来验证 semantic IDs 的质量,并与基于商品 ID 的 SASRec 基线做对比。然后,用 Qwen3-Embedding-0.6B 模型把商品元数据编码成 embeddings。最后,对 Qwen3-8B 模型做微调,让它通过 semantic IDs 来理解和推荐商品。

SASRec(Kang & McAuley, 2018)是一个受 Transformer 架构启发的序列推荐模型。它对用户的历史交互序列进行编码,借助 self-attention 机制衡量最相关的历史商品,从而预测下一个商品。这让模型能够学习用户行为中的长期依赖,性能优于 RNN、GRU 等旧的循环模型,同时因为支持并行计算,效率也更高。

Qwen3-Embedding-0.6B(Zhang et al., 2025)属于一个 embedding 模型系列,提供 0.6B、4B、8B 三种规格。它通过多阶段流程训练:先在合成数据上预训练,再进行有监督微调和模型合并以增强鲁棒性。其中 8B 版本在 MTEB Multilingual 基准上达到了 SOTA 水平。

Qwen3-8B (Yang et al., 2025) 是 Qwen3 系列中的一款稠密语言模型。尽管体积较小,但通过基于 Qwen3-235B-A22B 和 Qwen3-32B 的强弱蒸馏优化了后训练过程,使其在同等规模模型中表现相对出色,在超过半数的评测基准上超越了 Qwen2.5-14B 等更大的上一代模型,尤其在 STEM 和编程领域。与 Qwen3 系列其他模型一样,Qwen3-8B 具备双思考和无思考两种模式。

数据清洗与用户序列构建

首先,我们准备物品元数据,确保语义 ID 模型获得高质量输入。我们首先排除标题少于 20 个字符或描述少于 100 个字符的物品,这使物品数量从 13.7 万个减少至 6.6 万个唯一物品。

接着,我们使用 Gemini 2.5 Flash Lite (Comanici et al., 2025) 清洗物品描述,修复截断句子、移除 HTML 并删减冗余表达,使平均描述长度从 1,038 个字符减半至 538 个字符。同时,我们去除推广性文字并统一标题格式,例如将冗长的"NEW! LIMITED! Sega Saturn RGB SCART LEAD CABLE…"整理为整洁的"Sega Saturn RGB SCART Cable"。

然后,我们通过提取结构化元数据进行数据增强,包括产品类型(游戏、硬件、配件)、平台(PS4、Xbox、Wii)、类型(Roguelike、类魂、银河恶魔城)、硬件类型、品牌、多人模式等。该平台信息的提取覆盖率达 98%,品牌识别覆盖率为 78%,类型分类覆盖率为 51%。

最后,我们构建用户序列:对去重后的用户生成交互历史,得到 9.15 万个序列。从中排除无元数据的物品,再过滤掉少于 3 个物品的序列。序列最大长度截断为 100 个物品(仅截断 28 个序列),最终得到 7.86 万个序列的数据集,其中位数为 5 个物品,均值为 6.5 个物品。

训练 RQ-VAE 输出语义 ID

在嵌入商品时,我们使用了 Qwen3-Embedding-0.6B 模型。该模型支持针对不同任务自定义输入指令,我们在商品描述前添加了前缀“给定一个产品描述,生成一个能捕捉其关键特征和属性的语义嵌入”。通过末尾 token 池化得到 1024 维的嵌入向量,并在保存前进行 L2 归一化。

RQ-VAE 由编码器、三个各有 256 个码本的量化层级以及对 decoderr 组成。为了保证训练稳定性,我们采用了旋转技巧 (Fifty et al., 2025)来替代直通估计器(用于计算 $L_\text{recon}$ 的梯度)。其他优化手段包括使用 k-means 聚类初始化码本、重置未使用的码词以及使用较大的批量大小。我也尝试了一些无益的技巧,例如用 EMA 更新码本或停止向解码器的梯度传递。

训练好的 RQ-VAE 在三个量化层级上,对 6.6 万件商品实现了 89% 的唯一语义 ID。为解决剩余碰撞,我在第三个 token 后追加了第四个 token,为共享相同前三项码词的商品分配唯一、连续的 ID(0、1、2、…),从而确保每个商品都拥有唯一的四段式语义 ID。

我进行了几十次实验,以更深入地了解 RQ-VAE 及其输出的语义 ID,并找到模型的最优配置。以下是几个关键发现。

首先,我实验了 commitment weight $\beta$,该参数用于平衡重建精度与码本承诺程度。我测试了 0.25(黄色)、0.5(橙色)和 1.0(红色)这几个值,发现较高的 $\beta=1.0$ 能产生最多的唯一 ID,但也带来了最高的验证损失。而较低的 $\beta=0.25$ 产生的唯一 ID 略多一些,但 $\beta=0.5$ 却获得了最低的验证损失。因此,在该数据集上,我后续训练的 RQ-VAE 均采用 $\beta = 0.5$。(注:这与使用 $\beta = 0.25$ 的 Semantic ID 论文不同。)

Image

$\beta = 0.25$(黄色)、0.5(橙色)和 1.0(红色)的曲线

附:这些指标各自的含义简介:

  • loss/reconstruction:衡量 RQ-VAE 在将原始商品嵌入压缩为语义 ID 后再解压缩,重建原始嵌入的效果。
  • loss/vq:codebook 损失和 commitment 损失在所有层级的合计。确保 encoder 输出接近 codebook 向量,同时 codebook 向量也能适配 encoder 输出。是将 embedding 压缩成有意义的语义 ID 的关键。
  • loss/total:重建损失加 VQ 损失之和,用于监控整体训练进度。
  • loss/validation:在留出的验证集上的总损失,用于监控泛化能力。
  • metrics/avg_residual_norm:经过所有量化层级后残余的“剩余”残差大小。残差越低,说明 codebook 越能捕捉输入 embedding。
  • metrics/unique_id_proportion:一个 batch 中拥有唯一 ID 的物品占比,用于检测 codebook 塌缩。占比越高,说明区分不同物品的能力越强。

我还尝试了更浅的 encoder,以及元数据清洗的影响。更浅的 encoder(绿色)效果更差:验证损失更高,唯一 ID 数量更少。但在数据清洗上投入的功夫没有白费(蓝色):这个模型的重建损失和验证损失都最低,唯一 ID 占比却最高。我最终采用了这一轮训练出的 RQ-VAE。

Image

三条曲线:beta = 0.5(橙色)、更浅的 encoder(绿色)、清洗后的数据(蓝色)

评估 RQ-VAE 的另一种方法是查看 codebook 的利用率。各个 code 在所有层级上被相对均匀地使用,说明模型充分发挥了其表达能力。最终的 RQ-VAE 就做到了这一点:三个量化层级的 code 使用分布都很均匀,方差很低,如下方直方图所示。

Image

codebook 使用分布良好的 RQ-VAE 示例

相反,收敛不佳的 RQ-VAE 的 code 使用会既稀疏又高度集中。下面的直方图就展示了这种失败模式:少数 code 被过度使用,而 codebook 的大部分都被闲置。

Image

codebook 使用分布不佳的 RQ-VAE 示例

借助训练好的 RQ-VAE,我们将所有商品嵌入编码为语义 ID 格式,例如 <|sid_start|><|sid_191|><|sid_260|><|sid_716|><|sid_768|><|sid_end|>。随后,我们将 7.86 万条用户购买序列从常规 ID 转换为语义 ID 序列。这些序列构成了训练数据,既用于以 SASRec 为基线验证 ID 质量,也用于微调 Qwen3-8B 模型。

在常规商品 ID 与语义 ID 上训练 SASRec 的对比

为验证我们的语义 ID 是否能捕捉到有意义的产品关系,我们训练了两版 SASRec:一版基于常规商品 ID 的基线模型,另一版基于语义 ID 的变体,然后对比它们的性能表现。

基线 SASRec 遵循标准架构。它将每个商品视为独立的原子单元,从零开始学习其嵌入表示,完全基于行为模式建模。该模型采用 2 个因果自注意力块、64 维隐藏状态,并通过二元交叉熵(BCE)损失在判别任务上进行训练——即区分序列中的下一个商品与随机采样的负向商品。

# Baseline SASRec predict function
def predict(self, input_ids: torch.Tensor, candidate_ids: torch.Tensor) -> torch.Tensor:
    """Predict scores for candidate items.

    Args:
        input_ids: Item sequences [batch_size, seq_length]
        candidate_ids: Candidate items to score [batch_size, num_candidates]

    Returns:
        Scores for each candidate [batch_size, num_candidates]
    """
    # Get sequence representations
    hidden_states = self.forward(input_ids)  # [B, T, H]

    # Use only the last hidden state for prediction
    final_hidden = hidden_states[:, -1, :]  # [B, H]

    # Get candidate embeddings
    candidate_embs = self.item_emb(candidate_ids)  # [B, C, H]

    # Compute scores via dot product
    scores = torch.bmm(candidate_embs, final_hidden.unsqueeze(-1)).squeeze(-1)  # [B, C]

    return scores

相比之下,Semantic ID SASRec 将推荐任务重构为条件生成问题。其目标不是对项目打分,而是逐 token 生成下一个项目的 4 部分语义 ID。这需要更大的网络架构:包含 4 个 Transformer 块和 384 维的隐藏状态。与使用 T5 编码器-解码器结构的 TIGER 论文 不同,该 SASRec 变体仅采用解码器结构,因此能与基线 SASRec 更直接、更公平地进行比较。由于我们使用的是语义 ID,不再为 66k 个项目分别设置 embedding,而是总共使用 1,024 个 token 级 embedding,其中语义 ID 的每个层级包含 256 个 token。

# Semantic ID SASRec 预测函数
def predict_next_item(self, input_ids: torch.Tensor, teacher_forcing: bool = True, 
    target_tokens: Optional[torch.Tensor] = None) -> Dict[str, torch.Tensor]:
    """逐步预测下一个物品的语义 ID token。

    Args:
        input_ids: token 序列 [batch_size, seq_length * num_levels]
        teacher_forcing: 训练时用真实值作为条件输入
        target_tokens: 下一个物品的真实 token [batch_size, num_levels]

    Returns:
        包含每一层 logits 的字典
    """
    hidden_states = self.forward(input_ids)  # [B, T*L, H]

    # 取最后一个位置的表示,作为所有历史物品的上下文
    last_hidden = hidden_states[:, -1, :]  # [B, H]

    predictions = {}

    # 逐层生成:每一层的预测都以前面的层为条件
    for level in range(self.num_levels):
        if level == 0:
            # 第 0 层:直接基于序列表示预测
            context = last_hidden
        else:
            # 第 1-3 层:以之前预测或真实的 token 为条件
            if teacher_forcing and target_tokens is not None:
                # 训练时使用前面各层的真实值
                prev_tokens = target_tokens[:, :level]  # [B, level]
            else:
                # 推理时使用预测出的 token
                prev_tokens = self._sample_from_predictions(predictions, level)

            prev_embeds = self.token_emb(prev_tokens)  # [B, level, input_dim]
            prev_embeds_projected = self.input_projection(prev_embeds)  # [B, level, H]
            prev_context = prev_embeds_projected.mean(dim=1)  # [B, H]

            # 与序列上下文拼接
            combined = torch.cat([last_hidden, prev_context], dim=-1)  # [B, 2*H]
            context = self.context_combiners[level - 1](combined)  # [B, H]

        # 预测当前层
        logits = self.level_heads[level](context)  # [B, codebook_size]
        predictions[f"logits_l{level}"] = logits

    return predictions
这种生成式方法改变了我们的训练和评估方式。损失函数不再是简单的二元交叉熵(BCE loss),而是语义 ID 各层级交叉熵损失的总和,迫使模型正确预测整个序列。评估也更为复杂:项目得分不再由点积决定,而是其联合对数概率,即将生成每个 token 的对数概率相加得到。为提高训练稳定性,我们采用教师强制(teacher forcing)策略,利用上一级真实 token 来引导下一级的预测。 为了评估两种模型,我们使用了一个验证集,其中为每个正样本的下一个 item 添加了 500 个负样本。虽然基线 SASRec 表现优于语义 ID 变体,但考虑到预测四个正确 token 这一极具挑战性的生成任务,语义模型的表现仍属可圈可点。此外,语义 ID 变体具备处理冷启动 item的能力,通过复用相似产品的共享 token 前缀实现这一功能,而基线模型则不具备此能力。这揭示了一个核心权衡:获得泛化能力的代价是每个 item 需要多出 4 倍次数的预测,以及更高的训练和推理算力开销。 | Model | Hit@10 | NDCG@10 | MRR | Mean Rank | Median Rank | |---|---|---|---|---|---| | **Baseline SASRec** | 0.2812 | 0.1535 | 0.1300 | 138.9 | 41.0 | | **Semantic ID SASRec** | 0.2020 | 0.1138 | 0.1007 | 179.7 | 79.0 |

微调 Qwen3-8B 以推荐语义 ID

接下来,我们让语言模型学会用语义 ID 进行对话。为此,我们对 Qwen3-8B 进行微调,使其成为“双语”模型,既能流畅处理自然语言,也能熟练处理语义 ID。 首先,我们构建了一个包含 420 万条对话示例的训练数据集,用以教授模型有关语义 ID 和推荐的知识。这些数据涵盖多种任务类型,包括将语义 ID 映射到对应文本描述(及反向映射)、预测用户序列中的下一个 item、理解 item 类别之间的关系,以及多跳推理。每条示例均以对话格式呈现,包含系统提示、用户指令和助手回复。

随后,我们以两阶段方式对模型进行微调。第一阶段专注于词表扩展:我们在 Qwen3-8B 的词表中新增 1,027 个语义 ID 相关 token(即 <|sid_start|><|sid_end|><|sid_0|><|sid_1023|>,以及 <|rec|>),并相应调整模型的嵌入矩阵大小。在此阶段,除输入和输出嵌入层外,我们冻结了所有模型参数,仅训练 12.3 亿参数(占总参数的 15.3%),以较高的学习率训练 1,000 步。

在语义 ID token 的嵌入被初始化之后,我们进入下一阶段——在所有 83 亿参数上进行全量微调。为此,我们采用较低的学习率、3% 的 warmup 调度以及 8-bit AdamW 优化器以提升内存效率,并在完整数据集上训练三个 epoch。为监控训练进展,我们使用了多个自定义回调函数,实时记录梯度范数等训练指标,评估推荐任务上的层次准确率,并在固定的一组对话输入上采样模型的输出结果。

与 LLM-推荐系统混合模型的对话

微调后的模型可用于推荐商品,并能理解语义 ID 与自然语言。由于语义 ID 是其词表的固有组成部分,模型通过逐个 token 预测序列中的下一个 ID 来生成推荐内容。对于交互式会话,聊天界面会将包含语义 ID 的模型原始输出翻译回商品标题,从而生成人类可读的对话。

用户:“Halo 3 Limited Edition -Xbox 360”,“Lost Planet: Extreme Condition - Xbox 360”,“Lost Planet: Extreme Condition Collector’s Edition -Xbox 360”

<|rec|>

模型获取用户的交互历史(一系列 Xbox 360 科幻射击游戏),并执行下一项预测。

<|sid_start|><|sid_64|><|sid_351|><|sid_733|><|sid_768|><|sid_end|>

助手:“Mass Effect - Xbox 360”

基础测试表明,该模型能够基于用户历史记录提供标准的下一项商品推荐,并能根据自然语言约束(如平台限制(“类似……的 Xbox 游戏”)或题材类型(“我喜欢可爱风格的动物类游戏。”)来优化推荐结果。

用户: 推荐 Xbox 上类似《塞尔达传说:时之笛 3D》的游戏:<|rec|>

模型根据种子物品(《塞尔达传说》)和自然语言约束("Xbox"),把推荐引导到另一个平台。

<|sid_start|><|sid_64|><|sid_271|><|sid_576|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_64|><|sid_435|><|sid_615|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_167|><|sid_271|><|sid_578|><|sid_768|><|sid_end|>

助手: 《辐射:新维加斯 - Xbox 360 终极版》《光环 4 - Xbox 360(标准版)》《光环:致远星 - 传奇版》

模型还能解释推荐理由、描述两个物品之间的关系,甚至为商品组合起有创意的名字和写描述。

用户: > 我刚玩完《勇者斗恶龙:英雄 II 探险家版 - PlayStation 4》。再推荐一款 <|rec|> 并解释原因:

模型执行了一个混合任务:先生成相关的 semantic ID,再给出推荐解释。

<|sid_start|><|sid_139|><|sid_289|><|sid_534|><|sid_768|><|sid_end|>

如果你喜欢《勇者斗恶龙:英雄 II》,你可能会喜欢《无夜之国》,
因为两者都是 PlayStation 4 上的动作 RPG,侧重战斗和角色成长。
两款游戏都提供以叙事为驱动的体验,并高度重视战斗机制,
说明喜欢这类题材的玩家兴趣是相通的。

助手: 《无夜之国 - PlayStation 4》

如果你喜欢《勇者斗恶龙:英雄 II》,你可能会喜欢《无夜之国》,因为两者都是 PlayStation 4 上的动作 RPG,侧重战斗和角色成长。两款游戏都提供以叙事为驱动的体验,并高度重视战斗机制,说明喜欢这类题材的玩家兴趣是相通的。

最后,虽然它可能比不上专门的推荐系统在纯准确率上的表现,但这种统一接口让用户可以通过多轮对话来发现我们目录中的商品,从而实现搜索、推荐系统和聊天的融合。

这一切都通过模型本身完成,无需意图分类器、路由器、检索模块等组件。

[对话 1] 用户:我想找一些类似《马里奥赛车》的游戏。<|rec|>

模型为三款卡丁车风格的竞速游戏生成语义 ID。

<|sid_start|><|sid_131|><|sid_503|><|sid_738|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_131|><|sid_260|><|sid_577|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_145|><|sid_416|><|sid_671|><|sid_768|><|sid_end|>

助手:《极品飞车:最高通缉 - 索尼 PSP》、《极品飞车:热力追踪 2 - PlayStation 2》、《火爆狂飙:传奇 - 任天堂 DS》

[对话 2] 用户:换 Xbox 平台上有类似风格的游戏怎么样?<|rec|>

模型结合对话历史和新的“Xbox”约束,为符合条件的 Xbox 街机竞速游戏生成语义 ID。

<|sid_start|><|sid_183|><|sid_461|><|sid_517|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_183|><|sid_461|><|sid_562|><|sid_768|><|sid_end|>, 
<|sid_start|><|sid_183|><|sid_461|><|sid_616|><|sid_768|><|sid_end|>

助手:《极品飞车:亡命狂飙 - Xbox 360》、《疯狂都市赛车 3 - Xbox 360》、《极限竞速 4 - Xbox 360》

[对话 3] 用户:为这个捆绑包起个名字并写一段描述。

模型理解到上下文已从推荐任务切换到创意文本生成任务。

助手:Xbox Racing Legends: Speed & Style Pack(Xbox 竞速传奇:速度与风格包)

• • •

好了,就是这些!希望你喜欢这篇文章,和我一样兴奋!如果我有更多算力,我会尝试对语义 ID 使用 SentencePiece 分词——DeepMind 和 YouTube 的研究表明这有帮助——并进一步探索语义 ID 对冷启动的影响。我还会在包含数百万商品及更多样化任务和对话的数据集上训练 RQ-VAE 和语言模型,以测试该方法的扩展性和泛化能力。

你训练过带有语义 ID 的模型,或者基于它构建过 UX 吗?我很乐意交流,欢迎联系我

感谢DevanshShrikar的讨论,以及LukeRunpod提供的实验算力。(请在使用我的专属推荐码,以便我获得更多实验认可!)另外感谢VibhuShawnCheng以及忍受我絮叨的各位。

参考文献

Hou, Yupeng, Jiacheng Li, Zhankui He, An Yan, Xiusi Chen, and Julian McAuley. “Bridging Language and Items for Retrieval and Recommendation.” arXiv:2403.03952. Preprint, arXiv, March 6, 2024. https://doi.org/10.48550/arXiv.2403.03952.

Amazon. “Amazon KDD Cup ‘23 - Multilingual Recommendation Challenge Dataset.” AIcrowd, 2023. https://www.aicrowd.com/challenges/amazon-kdd-cup-23-multilingual-recommendation-challenge.

Rajput, Shashank, Nikhil Mehta, Anima Singh, et al. “Recommender Systems with Generative Retrieval.” arXiv:2305.05065. Preprint, arXiv, November 3, 2023. https://doi.org/10.48550/arXiv.2305.05065.

Singh, Anima, Trung Vu, Nikhil Mehta, et al. “Better Generalization with Semantic IDs: A Case Study in Ranking for Recommendations.” arXiv:2306.08121. Preprint, arXiv, May 30, 2024. https://doi.org/10.48550/arXiv.2306.08121.

Zeghidour, Neil, Alejandro Luebs, Ahmed Omran, Jan Skoglund, and Marco Tagliasacchi. “SoundStream: An End-to-End Neural Audio Codec.” arXiv:2107.03312. Preprint, arXiv, July 7, 2021. https://doi.org/10.48550/arXiv.2107.03312.

Lee, Doyup, Chiheon Kim, Saehoon Kim, Minsu Cho, and Wook-Shin Han. “Autoregressive Image Generation Using Residual Quantization.” arXiv:2203.01941. Preprint, arXiv, March 9, 2022. https://doi.org/10.48550/arXiv.2203.01941.

Kang, Wang-Cheng, and Julian McAuley. “Self-Attentive Sequential Recommendation.” arXiv:1808.09781. Preprint, arXiv, August 20, 2018. https://doi.org/10.48550/arXiv.1808.09781.

Vaswani, Ashish, Noam Shazeer, Niki Parmar, et al. “Attention Is All You Need.” arXiv:1706.03762. Preprint, arXiv, August 2, 2023. https://doi.org/10.48550/arXiv.1706.03762.

Zhang, Yanzhao, Mingxin Li, Dingkun Long 等。“Qwen3 Embedding: Advancing Text Embedding and Reranking Through Foundation Models.” arXiv:2506.05176。预印本,arXiv,2025 年 6 月 11 日。https://doi.org/10.48550/arXiv.2506.05176。

Yang, An, Anfeng Li, Baosong Yang 等。“Qwen3 Technical Report.” arXiv:2505.09388。预印本,arXiv,2025 年 5 月 14 日。https://doi.org/10.48550/arXiv.2505.09388。

Comanici, Gheorghe, Eric Bieber, Mike Schaekermann 等。“Gemini 2.5: Pushing the Frontier with Advanced Reasoning, Multimodality, Long Context, and Next Generation Agentic Capabilities.” arXiv:2507.06261。预印本,arXiv,2025 年 7 月 22 日。https://doi.org/10.48550/arXiv.2507.06261。


如果这篇文章对你有帮助,欢迎引用:

Yan, Ziyou.(2025 年 9 月)。Training an LLM-RecSys Hybrid for Steerable Recs with Semantic IDs. eugeneyan.com. https://eugeneyan.com/writing/semantic-ids/。

或者使用 BibTeX:

@article{yan2025semantic-ids,
  title   = {Training an LLM-RecSys Hybrid for Steerable Recs with Semantic IDs},
  author  = {Yan, Ziyou},
  journal = {eugeneyan.com},
  year    = {2025},
  month   = {Sep},
  url     = {https://eugeneyan.com/writing/semantic-ids/}
}

原始来源: Eugene Yan

评论 (0)