.. _gr_search: 查询补全与商品检索 ================== 在推荐场景中,OneRec 通过用户历史行为序列推测用户的潜在兴趣,输入与输出均是封闭词表的物品 ID。然而,电商搜索面临着截然不同的挑战:\ **用户通过明确的文本查询表达意图,系统需要在强相关性约束下从海量商品库中返回精准匹配的结果**\ 。这种“文本查询 → 商品结果”的映射涉及开放词表(用户可输入任意查询)与封闭词表(商品库有限)的混合处理,以及查询理解、商品语义匹配、个性化排序等多层次任务。 传统电商搜索系统同样采用多阶段级联架构(MCA):查询理解模块负责纠错、改写和意图识别;召回模块通过倒排索引和向量检索从数亿商品中筛选出数千候选;预排序和精排模块依次进行粗细粒度排序。这种架构在电商场景下的问题尤为突出: - **查询与商品检索的解耦**\ :查询理解、召回、排序各自优化独立目标,导致查询意图在传递过程中损失 - **冷启动与长尾问题**\ :新上架商品缺乏交互数据,难以通过倒排索引召回;低频查询缺乏历史共现模式,难以精准匹配 - **商品信息噪声**\ :商品标题堆砌大量关键词(如“2024新款韩版修身显瘦连衣裙”),传统文本编码器难以识别核心语义 为了从根本上解决这些问题,\ **OneSug** 和 **OneSearch** 分别针对电商搜索链路的前半段(查询补全)和后半段(商品检索)提出了端到端生成式解决方案。它们共享统一的生成式架构哲学,但在输入输出空间、ID 设计、序列建模等方面展现出不同的设计权衡。 OneSug:查询补全生成 -------------------- 查询补全(Query Suggestion)是电商搜索的第一道关口:当用户输入前缀“红色连”时,系统需要实时生成完整查询候选(如“红色连衣裙”、“红色连帽衫”)。传统电商平台通常采用多阶段级联架构(MCA):首先通过前缀树(Trie)进行粗召回(从 :math:`10^8` 候选查询中筛选 :math:`10^4` 个),然后经过预排序(筛选至 :math:`10^2` 个)和精排,最终展示 16 个查询候选。这种架构存在两个核心问题:\ **前序阶段的性能瓶颈限制了后续阶段的优化上界**\ ,以及\ **各阶段独立优化目标导致全局次优**\ 。 OneSug 将查询补全重新定义为\ **端到端的条件文本生成任务** :cite:`guo2025onesug` : .. math:: P(\text{Query} | \text{Prefix}, \text{UserContext}) 通过统一的生成式架构直接从前缀和用户上下文生成候选查询,绕过传统的多阶段链路。其核心挑战在于: - **短前缀的语义歧义**\ :前缀通常仅 1-2 个字(如“苹”可能指水果或手机),缺乏明确意图信号 - **个性化与流行度的平衡**\ :既要捕获用户历史偏好,又要推荐高转化查询 - **多层级用户反馈的精细化建模**\ :电商搜索日志包含订单、商品点击、查询点击、展示等多级反馈,如何利用这些信号优化模型的排序能力? - **实时性约束**\ :查询补全需在 100ms 内返回结果,对模型推理速度要求极高 **Encoder-Decoder 架构** OneSug 采用 **Encoder-Decoder 架构**\ (基于 BART 或 mT5),将查询补全转化为序列到序列生成任务。编码器需要整合多源异构信息以充分表达用户意图,解码器则以自回归方式生成目标查询。 .. _onesug_framework: .. figure:: ../img/one_sug.png :width: 600px OneSug 的三阶段架构 **图说明**\ :OneSug 架构包含三个核心模块:(a) **PRE 模块**\ 通过语义与协同查询增强短前缀表示;(b) **Encoder-Decoder** 融合用户多尺度特征生成候选查询;(c) **RWR 策略**\ 利用多级交互反馈优化排序能力。 **前缀-查询语义对齐** 对于纯文本前缀 :math:`p`\ ,使用预训练的 Text Encoder(BGE)提取语义嵌入 :math:`\mathbf{e}_p \in \mathbb{R}^{768}`\ 。然而,通用 NLP 模型在电商领域的语义空间与实际业务特性存在偏差。OneSug 对 BGE 编码器进行领域对齐微调:从搜索日志中挖掘高质量的 prefix-query 和 query-query 共现对(使用 ItemCF 类算法),通过对比学习使协同相关的查询在嵌入空间更接近: .. math:: \mathcal{L}_{\text{align}} = -\log \frac{\exp(\text{sim}(\mathbf{e}_{q_i}, \mathbf{e}_{q_j}) / \tau)}{\sum_{q_k \in \mathcal{B}} \exp(\text{sim}(\mathbf{e}_{q_i}, \mathbf{e}_{q_k}) / \tau)} 这使编码器同时捕获\ **语义相似性**\ (如“连衣裙”与“裙子”)和\ **行为共现性**\ (同一用户常搜的查询对)。实验表明,对齐后的 BGE 模型在查询检索任务上的语义相关性从 0.67 提升至 0.81。 **前缀表示增强(PRE 模块)** 短前缀的语义表示往往不足以支撑精准生成。OneSug 提出 **Prefix2Query Representation Enhancement (PRE)** 模块,通过检索与前缀语义和协同相关的查询来增强其表示。 具体而言,对于前缀 :math:`p`\ ,从历史日志中检索与其共现的高质量查询集合 :math:`\{q_1^c, q_2^c, ..., q_k^c\}`\ (例如,用户输入“红色连”后常继续搜索“红色连衣裙”、“红色连帽卫衣”等)。这些查询的平均嵌入被加权融合到前缀表示中: .. math:: \bar{\mathbf{e}_q^c} = \frac{1}{k} \sum_{i=1}^{k} \mathbf{e}_{q_i}^c .. math:: \mathbf{e}_p^* = (1 - w) \cdot \mathbf{e}_p + w \cdot \bar{\mathbf{e}_q^c}, \quad w = 0.5 OneSug 在训练时设置增强权重 :math:`w=0.5`\ 。消融实验表明,该策略使 MRR 相比无增强版本提升 2.3%,但当 :math:`w>0.7` 时会引入过多噪声导致性能下降。 为了高效检索相关查询,OneSug 借鉴生成式推荐中的语义 ID 思想,使用 **RQ-VAE** 将查询编码为分层离散码(4 层,每层码本大小 512),在推理时通过从粗到细的层级匹配策略快速定位相关查询,将计算复杂度从向量检索的 :math:`O(N \cdot d)` 降低到码本查找的 :math:`O(W^C)`\ 。 **用户行为序列与画像特征** 除了增强后的前缀表示,编码器还整合了两类用户特征: - **短期历史查询**\ :用户近期 :math:`n=10` 条历史查询 :math:`\mathcal{H}_u = \{q_1^h, q_2^h, ..., q_n^h\}`\ ,直接反映当前会话意图。实验表明超过 10 条会引入噪声(如数月前的偶然搜索),导致 MRR 下降 1.2% - **用户静态画像**\ :年龄、性别、地域、消费等级等特征 :math:`\mathcal{U}`\ ,离散化为 Embedding 后拼接,捕获用户的长期偏好 值得注意的是,OneSug **不引入商品交互特征**\ (如点击过的商品类目),因为查询补全发生在用户输入阶段,尚未产生商品曝光行为。编码器的输入构造为: .. math:: x_u = \{t_{\text{[CLS]}}, \mathbf{e}_p^*, t_{\text{[SEP]}}, \mathcal{H}_p, t_{\text{[SEP]}}, \mathcal{H}_u, t_{\text{[SEP]}}, \mathcal{U}\} 其中 :math:`\mathcal{H}_p` 是 PRE 模块检索到的相关查询序列。编码器输出为: .. math:: \mathbf{Z}_{enc} = \text{Encoder}(x_u) **解码器:自回归查询生成** 解码器采用标准的 Causal Transformer 结构,以自回归方式生成目标查询的每个子词(subword)。训练时使用 Teacher Forcing 策略,最小化 Next Token Prediction 损失: .. math:: \mathcal{L}_{NTP} = -\sum_{t=1}^{|q|} \log P(q_t | q_{ 对,其奖励分数为: .. math:: r(x_u, q) = \lambda \cdot e^{pi} 其中 :math:`pi` 是该查询在对应层级的归一化出现频率。这种设计使得\ **高频交互的查询获得更高奖励**\ 。举个具体的例子: - “手机 → 手机性价比排行”被点击了50次(等级3): :math:`r = 1.0 \times e^{50/总点击数}` - “手机 → 手机壳”产生了20个订单(等级1): :math:`r = 2.0 \times e^{20/总订单数}` **对比偏好对构造** 从 6 个层级中构造 9 类偏好对(选择正样本层级 > 负样本层级),例如: - (强正 vs 弱负) - (中正 vs 强负) - (弱正 vs 随机负) 对于每个偏好对,用户的偏好差异 :math:`rw_{\Delta}` 定义为: .. math:: rw_{\Delta} = \frac{1.0}{r(x_u, q_w) - r(x_u, q_l)} 其中 :math:`q_w` 是正样本(win),\ :math:`q_l` 是负样本(lose)。较小的 :math:`rw_{\Delta}` 值对应难区分的样本对(如 ),训练时需要更大的梯度权重以学习细微差异。 **混合偏好对齐损失** OneSug 在标准的 DPO(Direct Preference Optimization)损失基础上引入两项改进: - **奖励加权**\ :通过 :math:`rw_{\Delta}` 动态调整不同样本对的学习权重 - **目标奖励边界 :math:`\delta`**\ :强制正负样本的奖励差异至少为 :math:`\delta`\ ,提升区分度 结合 pair-wise 和 list-wise 建模,最终损失为: .. math:: \mathcal{L}_{\text{pair-wise}} = -\mathbb{E} \left[ \log \sigma \left( rw_{\Delta} \cdot \max(0, \hat{r}_\theta(x_u, q_w) - \hat{r}_\theta(x_u, q_l) - \delta) \right) + \alpha \log \pi_\theta(q_w | x_u) \right] .. math:: \mathcal{L}_{\text{list-wise}} = -\mathbb{E} \left[ \log \sigma \left( -\log \sum_{q_l \in \mathcal{Q}_l} \exp \left( rw_{\Delta} \cdot \max(0, \hat{r}_\theta(x_u, q_l) - \hat{r}_\theta(x_u, q_w) - \delta) \right) \right) + \alpha \log \pi_\theta(q_w | x_u) \right] 其中隐式奖励 :math:`\hat{r}_\theta(x_u, q) = \beta \log \frac{\pi_\theta(q|x_u)}{\pi_{\text{ref}}(q|x_u)}`\ ,\ :math:`\pi_{\text{ref}}` 是 NTP 训练后的参考模型,\ :math:`\alpha` 是 SFT 损失权重以保持生成质量。 **OneSug 小结** OneSug 通过 Encoder-Decoder 架构和 RWR 策略,成功地将查询补全从多阶段级联系统转变为端到端的生成式模型。PRE 模块通过语义和协同查询增强短前缀表示,多视角特征融合捕获用户意图,六级交互反馈构建的奖励系统使模型能够精准理解用户偏好差异。这种统一的生成式框架不仅简化了系统架构,更重要的是实现了全局优化,避免了传统多阶段系统中前序阶段的性能瓶颈。 然而,当用户完成查询输入后,系统面临着更加复杂的挑战——从数亿商品中检索最相关结果。这要求同时处理\ **开放词表的查询**\ 和\ **封闭词表的商品**\ ,并引入更强的相关性约束和个性化排序能力。接下来我们将深入探讨专门针对商品检索的端到端生成模型 **OneSearch**\ ,它需要解决三个核心问题: - **商品 ID 设计**\ :如何为商品构造融合语义与协同信号、同时保留层次结构和单品区分度的 ID? - **相关性建模**\ :如何建模查询-商品的细粒度相关性,确保“红色连衣裙”不会返回“蓝色连衣裙”? - **噪声与冷启动**\ :如何处理商品标题的关键词堆砌问题,以及新上架商品的冷启动困境? OneSearch:商品检索生成 ----------------------- 当用户在电商平台输入“红色连衣裙”并按下搜索键,系统需要在一秒内从数亿商品中找到最匹配的结果。在传统电商搜索系统中,这个过程需要经历查询理解、倒排索引召回、向量检索、预排序、精排等多个阶段。每个阶段都有独立的模型和优化目标,导致查询意图在传递过程中不断损失——一个相关商品可能在早期阶段就被过滤掉,无论后续排序模型多么精准也无法挽回。 **电商搜索的三大独特挑战** 与我们之前讨论的视频推荐(OneRec)和查询补全(OneSug)相比,电商商品检索面临着更加复杂的约束条件: 第一,\ **强相关性约束是第一优先级**\ 。在推荐场景中,系统可以基于用户历史行为推荐风格相似但类目不同的商品。例如,用户喜欢运动鞋,推荐运动服也是合理的。但在搜索场景下,相关性是不可妥协的底线——用户搜索“红色连衣裙”,即使该用户历史上经常购买蓝色商品,返回“蓝色连衣裙”也是严重的相关性违反。这要求系统必须\ **先满足相关性,再优化个性化**\ ,这与推荐系统的优化目标存在本质差异。 第二,\ **商品信息充斥着噪声和冗余**\ 。为了提高曝光率,商家往往在商品标题中堆砌大量关键词。例如,一件连衣裙的标题可能是“2024新款秋季韩版修身显瘦长袖连衣裙女学生小个子甜美气质裙子百搭”,包含多达十几个属性词,且语义顺序混乱(品牌、风格、功能词随意排列)。更糟糕的是,商家可能添加不相关的热门关键词来蹭流量。传统的文本编码器(如 BERT)在处理这类文本时会被冗余信息淹没,难以识别真正的核心属性。 第三,\ **语义层次与商品独特性的平衡问题**\ 。理想的商品表示需要同时满足两个看似矛盾的需求:一方面,系统需要理解商品的类目层次结构(“服装 → 女装 → 连衣裙 → 韩版连衣裙”),这样才能在粗粒度上匹配查询;另一方面,系统必须保留每个商品的独特属性(具体款式、品牌、价格),否则所有“韩版连衣裙”都会被映射到相同的表示,无法区分哪件是用户真正想要的。 **OneSearch 的设计理念** OneSearch :cite:`chen2025onesearch` 的核心思想是将传统的“查询 → 召回 → 预排序 → 精排”多阶段流程统一为一个端到端的序列生成任务: .. math:: P(\text{商品序列} | \text{查询}, \text{用户上下文}) 这意味着模型直接输入查询文本和用户行为特征,输出一个有序的商品列表。为了实现这一目标,OneSearch 设计了四个核心模块,它们分别解决上述三大挑战: 1. **关键词增强的分层量化编码(KHQE)**\ :为每个商品构造一个融合语义与协同信号的多层离散 ID,既保留类目层次又突出核心属性,同时增强查询-商品相关性约束 2. **多视角行为序列注入(Mu-Seq)**\ :从用户 ID 构建、短期序列、长期序列三个角度建模用户偏好,在保证相关性的前提下实现个性化 3. **统一的 Encoder-Decoder 生成架构**\ :将查询理解、商品召回、个性化排序融合到一个生成模型中 4. **偏好感知奖励系统(PARS)**\ :通过多阶段监督微调和自适应奖励模型,学习用户的精细化偏好差异,同时保持相关性约束 下面我们逐一展开这些技术细节,并分析它们如何在工业系统中落地。 .. _onesearch_framework: .. figure:: ../img/onesearch.png :width: 600px OneSearch模型结构图 **图说明**\ :OneSearch 的完整架构包含四个核心模块:(a) KHQE 通过关键词增强和分层量化为商品构建语义 ID;(b) Mu-Seq 从三个视角注入用户行为序列;(c) Encoder-Decoder 架构融合所有特征进行商品生成;(d) PARS 通过多阶段微调和奖励系统优化个性化排序能力。 关键词增强的分层量化编码 ~~~~~~~~~~~~~~~~~~~~~~~~ 在深入技术细节之前,我们先理解一个核心问题:\ **在生成式框架中,如何表示数亿个商品?** 传统的推荐系统使用原子 ID(如 ``item_12345678``\ )来唯一标识每个商品。但这种方法在生成式框架中遇到两个致命问题:第一,数亿商品需要数亿词表大小,Softmax 层的计算复杂度为 :math:`O(|\mathcal{V}|)`\ ,在如此庞大的词表下完全不可行;第二,原子 ID 是随机数字,不包含任何语义信息,模型无法学习商品之间的语义关联(例如“红色连衣裙”和“红色长裙”在语义上相近,但它们的原子 ID 可能毫无关系)。 OneSearch 采用\ **分层语义 ID(Hierarchical Semantic ID)**\ 策略,将每个商品映射为一个多层离散码序列: .. math:: \text{商品} \rightarrow [\text{L1}, \text{L2}, \text{L3}, \text{OPQ1}, \text{OPQ2}] 例如,某件韩版连衣裙可能被编码为 ``[3856, 724, 385, 142, 201]``\ 。这是一个\ **长度为 5 的离散序列**\ ,词表大小约 **6000 个唯一 token**\ (4096 + 1024 + 512 + 256 + 256),远小于原子 ID 的数亿规模。这个 5 层编码不是随意设计的,而是经过精心设计以平衡\ **语义层次**\ (前 3 层)和\ **商品独特性**\ (后 2 层)。 让我们看看这个编码是如何生成的。 **商品表示学习** 要构建有意义的语义 ID,第一步是为每个商品生成一个高质量的嵌入向量。这个向量需要同时捕获两类信息:\ **内容语义**\ (商品本身是什么)和\ **协同信号**\ (用户如何与商品交互)。 OneSearch 从搜索日志中收集多源信息作为商品的原始特征: - **文本信息**\ :商品标题、详情页 OCR 文本、关键词标签 - **结构化属性**\ :价格区间、类目层级、品牌信息 - **统计特征**\ :近期点击率、转化率、加购率等业务指标 这些异构特征被输入蒸馏后的 BGE 模型(一个轻量级的文本编码器),生成初始的商品嵌入 :math:`\mathbf{e}_i \in \mathbb{R}^{768}`\ 。但是,通用的文本编码器往往偏向语义相似性,而忽略了电商场景下的协同过滤信号。例如,“苹果手机”和“苹果电脑”在语义上都包含“苹果”品牌,但用户行为模式可能截然不同。 为了让嵌入同时反映\ **语义关联**\ 和\ **行为协同**\ ,OneSearch 设计了多类对齐任务: 1. **query-query 和 item-item 对比学习**\ (\ :math:`\mathcal{L}_{q2q}`, :math:`\mathcal{L}_{i2i}`\ ):从搜索日志中挖掘协同相关的查询对和商品对(使用 ItemCF、Swing 等算法),通过对比学习拉近它们的嵌入距离。例如,用户搜索“连衣裙”后常搜“长裙”,这两个查询在嵌入空间中应该接近 2. **query-item 对比学习**\ (\ :math:`\mathcal{L}_{q2i}`\ ):确保查询和商品在同一语义空间,为后续的检索任务奠定基础 3. **分层反馈对齐**\ (\ :math:`\mathcal{L}_{\text{rank}}`\ ):对不同用户行为层级(曝光、点击、下单)的查询-商品对赋予不同的 Margin 权重,学习协同信号的细微差异。例如,被点击的商品应比仅曝光的商品更接近查询 4. **难样本相关性校正**\ (\ :math:`\mathcal{L}_{\text{rel}}`\ ):对于余弦相似度接近阈值的难样本对,使用 LLM(如 GPT-4)基于完整上下文评分相关性,然后让 BGE 模型拟合这个分数,提升在边界样本上的判断能力 总体对齐损失为这些子任务的加权和: .. math:: \mathcal{L}_{\text{align}} = \lambda_1 \mathcal{L}_{q2q} + \lambda_2 \mathcal{L}_{i2i} + \lambda_3 \mathcal{L}_{q2i} + \lambda_4 \mathcal{L}_{\text{rank}} + \lambda_5 \mathcal{L}_{\text{rel}} 其中 :math:`\lambda_i` 是可调节的权重系数,可以根据不同的业务目标进行调整。 **核心关键词增强** 经过对齐训练后,商品嵌入 :math:`\mathbf{e}_i` 已经同时包含语义和协同信号。但电商商品标题中充斥着大量冗余词(如“爆款”“包邮”“限时特价”),这些营销词会稀释真正的核心属性(如品牌、颜色、材质)的表示权重。 OneSearch 的解决方案是\ **显式提取并增强核心关键词**\ 。具体而言: 1. **构建核心属性词表**\ :快手电商团队通过命名实体识别(NER)技术,从历史搜索日志中识别出 18 类结构化属性,包括:实体(Entity)、修饰词(Modifier)、品牌(Brand)、材质(Material)、风格(Style)等。对每类属性,根据点击日志按 PV 降序排列,选择高频词作为核心词表 2. **快速关键词匹配**\ :使用 **Aho-Corasick 自动机**\ (一种高效的多模式串匹配算法,能在 :math:`O(n)` 时间内在文本中同时查找多个模式串)在商品标题中快速匹配核心关键词。对于图片类商品,使用多模态模型(Qwen-VL)识别图片中的关键视觉属性 3. **关键词增强表示**\ :假设商品 :math:`i` 匹配到 :math:`n` 个核心关键词 :math:`\{k_1, k_2, ..., k_n\}`\ ,每个关键词的嵌入为 :math:`\mathbf{e}_{k_j}`\ 。商品的优化表示为: .. math:: \mathbf{e}^o_i = \frac{1}{2} \left( \mathbf{e}_i + \frac{1}{n} \sum_{j=1}^{n} \mathbf{e}_{k_j} \right) 这种 50%-50% 的加权平均使得核心属性在后续量化编码中占据主导地位,同时保留原始嵌入的完整语义信息。查询的表示也使用相同的增强策略,这个简单的技巧在实验中带来了显著提升。 **RQ-Kmeans 语义层次编码** 现在我们有了高质量的商品嵌入 :math:`\mathbf{e}^o_i \in \mathbb{R}^{768}`\ ,下一步是将连续向量转换为离散的分层码。OneSearch 使用\ **残差量化 K-means(Residual Quantization K-means, RQ-Kmeans)**\ 来实现这一目标。 RQ-Kmeans 的核心思想是\ **逐层提取语义信息,并将残差传递到下一层**\ 。 这个过程可以类比为\ **逐层细化的类目树**\ : - **L1 层(码本大小 4096)**\ :捕获最粗粒度的类目,如“服装”、“数码”、“食品”、“家居” - **L2 层(码本大小 1024)**\ :在 L1 的基础上进一步细分,如“服装”下的“女装”、“男装”、“童装” - **L3 层(码本大小 512)**\ :捕获更细粒度的子类目,如“女装”下的“连衣裙”、“T恤”、“牛仔裤” 举个具体例子:一件“红色韩版连衣裙”可能得到编码 ``[L1=2341, L2=567, L3=89]``\ ,其中: - L1=2341 代表“服装”这个大类 - L2=567 代表“女装-裙装”这个子类 - L3=89 代表“连衣裙-韩版风格”这个细分类目 **关键优化:第 3 层的平衡约束** 为了提高码本利用率,一个直接的想法是在所有层应用\ **平衡 K-means**\ ,强制每个聚类中心包含相近数量的商品。然而,OneSearch 发现\ **在早期层(L1, L2)强制平衡会导致层次聚类崩溃**——细粒度属性不同的商品被强行分到同一聚类,丧失了语义区分度。 OneSearch 的解决方案是\ **仅在 L3 层应用平衡约束**\ ,让 L1 和 L2 自由聚类捕获主要语义,然后在 L3 层通过平衡约束实现细粒度区分。 **OPQ 商品独特性编码** 经过 3 层 RQ-Kmeans 后,我们得到了商品的分层语义码 ``[L1, L2, L3]``\ 。但此时残差向量 :math:`\mathbf{e}'` 中仍然包含重要信息——这是商品的\ **独特属性**\ ,如具体款式、品牌、价格段、具体设计细节等。如果直接丢弃这些残差,会导致大量商品被映射到相同的语义 ID,无法区分。 例如,两件都是“韩版连衣裙”的商品可能具有相同的 ``[L1, L2, L3]`` 编码,但一件是“Zara 品牌 299 元”,另一件是“无品牌 99 元”。如果只用前 3 层编码,这两件商品在生成模型中将被视为完全相同,这显然不合理。 OneSearch 引入 **优化乘积量化(Optimized Product Quantization, OPQ)** 对残差进行编码。OPQ 的核心思想是将高维向量切分成多个子向量,分别进行量化: :: 1. 将残差向量 e' ∈ R^768 切分为 M=2 个子向量 sub_1 = e'[0:384], sub_2 = e'[384:768] 2. 对每个子向量独立进行 K-means 聚类(码本大小 256) OPQ1 = argmin ||sub_1 - Codebook_OPQ1[c]||_2 OPQ2 = argmin ||sub_2 - Codebook_OPQ2[c]||_2 最终,每个商品的完整语义 ID 为 5 层编码: .. math:: \text{SID}_i = [\text{L1}, \text{L2}, \text{L3}, \text{OPQ1}, \text{OPQ2}] **为什么不对所有层都用 OPQ?** OneSearch 团队也尝试过将 RQ 的所有层都用 OPQ 编码(配置 ``4*2/256`` 和 ``4*4/256``\ ),但发现性能大幅下降。原因是这种做法破坏了\ **层次结构的语义性**——失去了“粗粒度到细粒度”的层级关系,导致模型难以学习商品类目的渐进式生成模式。 回顾整个 KHQE 模块,这种精心设计的编码方式,使得 OneSearch 在处理电商商品时,既能理解粗粒度的类目匹配(“用户搜连衣裙,不应该返回手机”),又能捕获细粒度的属性差异(“用户搜红色连衣裙,不应该返回蓝色连衣裙”)。 多视角行为序列注入 ~~~~~~~~~~~~~~~~~~ 在传统的电商搜索系统中,用户行为特征通常只在精排阶段才被引入,而且往往是通过简单的特征拼接。OneSearch 提出了一种更系统化的策略:\ **从用户 ID 构建、短期序列、长期序列三个视角注入用户行为**\ ,让生成模型能够全面理解用户的偏好和意图。 **行为序列驱动的用户 ID** 在推荐系统中,常见的做法是为每个用户分配一个随机哈希 ID(如将用户编号 ``user_98765432`` 映射为固定维度向量)。这种方法简单高效,但存在两个问题:第一,随机 ID 不包含任何语义信息,兴趣相似的用户在嵌入空间中可能完全无关;第二,由于哈希冲突,不同用户可能被映射到相同的 ID。 OneSearch 的创新在于\ **用用户的行为序列来构造 User ID**\ 。具体而言,对于用户的短期点击序列 :math:`\{s_1, s_2, ..., s_m\}` 和长期点击序列 :math:`\{l_1, l_2, ..., l_n\}`\ ,分别计算加权和: .. math:: \begin{aligned} \text{SID}_{\text{short}} &= \left\lceil \sum_{i=1}^{m} \lambda_i \cdot \text{SID}_{s_i} \right\rceil, \quad \lambda_i = \frac{\exp(\sqrt{i})}{\sum_{j=1}^{m} \exp(\sqrt{j})} \\ \text{SID}_{\text{long}} &= \left\lceil \sum_{i=1}^{n} \mu_i \cdot \text{SID}_{l_i} \right\rceil, \quad \mu_i = \frac{\exp(\sqrt{i})}{\sum_{j=1}^{n} \exp(\sqrt{j})} \end{aligned} 其中 :math:`\lceil \cdot \rceil` 表示向上取整,\ :math:`\text{SID}_{s_i}` 是第 :math:`i` 次点击商品的语义 ID。权重系数 :math:`\lambda_i` 采用 :math:`\sqrt{i}` 的指数归一化,使得\ **越近的行为权重越高**\ ,但不像线性衰减那样激进。 最终的用户 ID 是两者的拼接(总长度为 10): .. math:: \text{UserID} = [\text{SID}_{\text{short}}, \text{SID}_{\text{long}}] 这种构造方式有两个优势:第一,\ **兴趣相似的用户会得到相近的 ID 表示**\ (类似协同过滤中的隐式反馈矩阵分解);第二,对于冷启动用户,可以使用平台统计的“查询 → Top 点击商品”作为默认行为序列,避免 ID 为空。 **显式短期序列注入** 用户最近的搜索和点击行为直接反映当前会话的意图。例如,一个用户在过去 10 分钟内搜索了“运动鞋”、“跑步装备”、“运动袜”,然后输入“护膝”,那么系统应该推断用户正在准备运动装备,而不是其他场景(如医疗护膝)。 OneSearch 将用户最近的历史查询和点击商品显式放入输入序列: :: [UserID] [SEP] [Query] [SID_query] [SEP] [q_1] [q_2] ... [q_n] [SEP] # 最近 n 条历史查询 [SID_{i1}] [SID_{i2}] ... [SID_{im}] [SEP] # 最近 m 次点击商品的 SID 这里有两个设计细节:第一,\ **查询用原始文本,商品用语义 ID**\ 。这是因为查询通常很短(2-3 个词),可以直接 Tokenize;而商品标题冗长(十几个词),用语义 ID 更紧凑。第二,\ **序列长度有限制**\ (历史查询 :math:`n \leq 10`\ ,点击商品 :math:`m \leq 20`\ ),避免输入过长。 **滑动窗口数据增强:学习兴趣演化** 在训练阶段,OneSearch 使用\ **滑动窗口策略**\ 对短期序列进行数据增强。假设用户的完整点击序列是 ``[i1, i2, i3, i4, i5]``\ ,传统训练只会生成一个样本: :: 输入:[User, Query, i1, i2, i3, i4, i5] → 输出:i_target 但 OneSearch 会生成多个训练样本(最大窗口长度 :math:`m=5`\ ): :: 样本 1:[User, Query] → i1 样本 2:[User, Query, i1] → i2 样本 3:[User, Query, i1, i2] → i3 样本 4:[User, Query, i1, i2, i3] → i4 样本 5:[User, Query, i1, i2, i3, i4] → i5 这种数据增强有两个好处:第一,\ **让模型学习兴趣的演化过程**\ ,而不仅仅是“给定完整历史预测下一个”;第二,\ **自然处理冷启动用户**\ ,因为训练时包含了“无历史”或“少量历史”的样本。 **Q-Former 长期序列压缩** 除了最近的短期行为,用户还有大量的历史交互数据。一个活跃用户可能在过去半年内产生了数千甚至上万次点击、加购、下单行为。这些长期行为反映了用户的\ **稳定兴趣偏好**\ (如偏好性价比、喜欢特定风格、常购买特定品类),但直接放入 Prompt 会导致序列过长,无法处理。 OneSearch 的解决方案是借鉴多模态预训练模型 BLIP-2 中的 **Q-Former(Query Transformer)** 思想,将长期行为压缩为固定长度的连续表示。具体分为三个步骤: **按行为类型分层聚合** 用户的长期行为包含三类:点击序列、下单序列、搜索相关单元(RSU)序列。对于每个商品,首先通过其语义 ID 查表获取 RQ-Kmeans 的聚类中心嵌入(3 层,每层一个向量),然后按层级分别求和: .. math:: \begin{aligned} \mathbf{M}_{\text{click}} &= \left\{ \sum_{i=1}^{m} \mathbf{Item}_{\text{emb}}^{L1}, \sum_{i=1}^{m} \mathbf{Item}_{\text{emb}}^{L2}, \sum_{i=1}^{m} \mathbf{Item}_{\text{emb}}^{L3} \right\} \\ \mathbf{M}_{\text{order}} &= \left\{ \sum_{i=1}^{n} \mathbf{Item}_{\text{emb}}^{L1}, \sum_{i=1}^{n} \mathbf{Item}_{\text{emb}}^{L2}, \sum_{i=1}^{n} \mathbf{Item}_{\text{emb}}^{L3} \right\} \\ \mathbf{M}_{\text{RSU}} &= \left\{ \sum_{i=1}^{k} \mathbf{Item}_{\text{emb}}^{L1}, \sum_{i=1}^{k} \mathbf{Item}_{\text{emb}}^{L2}, \sum_{i=1}^{k} \mathbf{Item}_{\text{emb}}^{L3} \right\} \end{aligned} 这样,无论用户有多少历史行为(可能上千条),都被压缩为 :math:`3 \times 3 = 9` 个向量(3 类行为 × 3 个层级)。 **Q-Former 交叉注意力提取** 接下来,使用 :math:`N_q=128` 个可学习的查询向量(Query Vectors)\ :math:`\mathbf{Q} \in \mathbb{R}^{128 \times 768}`\ ,通过交叉注意力机制从聚合的行为表示中提取关键信息: .. math:: \mathbf{Q}_{\text{long}} = \text{Q-Former}([\mathbf{M}_{\text{click}}, \mathbf{M}_{\text{order}}, \mathbf{M}_{\text{RSU}}]) Q-Former 的核心是多层 Transformer,其中查询向量通过交叉注意力(Cross-Attention)与行为聚合向量交互,学习提取最相关的用户偏好信息。最终得到的 :math:`\mathbf{Q}_{\text{long}} \in \mathbb{R}^{128 \times 768}` 是固定长度的,无论用户历史行为有多少条。 **注入编码器** 压缩后的长期行为表示 :math:`\mathbf{Q}_{\text{long}}` 作为额外的输入序列,与 User ID、Query、短期序列一起输入 Encoder。由于长度固定(128 个 Token),不会显著增加计算开销。 **为什么按层级聚合?** 直接对所有商品嵌入求平均会丢失层次信息。按 L1、L2、L3 分层聚合,可以让 Q-Former 学习到用户在\ **不同粒度**\ 上的偏好。例如,L1 层聚合反映用户是否偏好“数码”还是“服装”,L3 层聚合反映用户在细分类目上的偏好。 Encoder-Decoder 生成架构 ~~~~~~~~~~~~~~~~~~~~~~~~ 有了商品的语义 ID(KHQE)和用户的多视角行为表示(Mu-Seq),OneSearch 需要一个强大的生成模型来整合这些信息,并输出有序的商品列表。OneSearch 选择 **BART(Bidirectional and Auto-Regressive Transformer)** 作为基础架构,原因有三:第一,BART 是 Encoder-Decoder 结构,编码器可以双向建模查询和用户特征,解码器自回归生成商品序列;第二,BART 有良好的预训练权重,可以快速迁移到电商领域;第三,快手内部对 BART 做了大量架构加速优化,适合工业部署。 **编码器:融合查询与用户上下文** 编码器的输入是一个异构序列,包含离散 Token(User ID、Query 文本、语义 ID)和连续向量(Q-Former 输出): :: [BOS] [SEP] [SEP] <历史查询文本序列> [SEP] <短期点击商品 SID 序列> [SEP] <长期行为 Q-Former 输出 (128 tokens)> [EOS] 所有元素经过嵌入层后拼接(离散 Token 查表得到嵌入,连续向量直接使用),加入位置编码,输入 Transformer 编码器: .. math:: \mathbf{Z}_{\text{enc}} = \text{Encoder}([\text{UserID}; \mathbf{e}_q; \text{SID}_q; \text{Seq}_q; \text{Seq}_{\text{short}}; \mathbf{Q}_{\text{long}}]) 编码器的输出 :math:`\mathbf{Z}_{\text{enc}} \in \mathbb{R}^{L \times d}`\ (\ :math:`L` 是序列长度,\ :math:`d=768` 是隐藏维度)包含了所有输入信息的联合表示。 **解码器:自回归生成商品语义 ID** 解码器的任务是逐 Token 生成目标商品的 5 层语义 ID。以生成商品 ``[3856, 724, 385, 142, 201]`` 为例,解码过程如下: :: 步骤 0:输入 [BOS] → 预测 L1 = 3856 步骤 1:输入 [BOS, 3856] → 预测 L2 = 724 步骤 2:输入 [BOS, 3856, 724] → 预测 L3 = 385 步骤 3:输入 [BOS, ..., 385] → 预测 OPQ1 = 142 步骤 4:输入 [BOS, ..., 142] → 预测 OPQ2 = 201 每一步通过 Causal Self-Attention(只能看到前面的 Token)和 Cross-Attention(关注编码器输出)生成隐藏状态 :math:`\mathbf{h}_t^{\text{dec}}`\ ,然后通过 Softmax 层预测下一个 Token: .. math:: P(\text{Token}_t | \text{Token}_{