cswork / 系统设计

第 25 章 / 共 57 章 · 约 5 分钟

ML 案例 2:电商搜索排序

本页目录
  1. 1. 需求澄清
  2. 2. 问题形式化
  3. 3. 数据与特征
  4. 4. 模型
  5. 5. 训练与评估
  6. 6. 服务与监控
  7. 7. 回答分级
  8. 8. 不同级别的期望
  9. 章节测验

先花 10 分钟自己设计:需求、核心实体、API 和高层架构,各写几行。写完或者跳过之后,下面的讲解才会展开。

先写你的思路

只存在这台设备上;登录后会存进你的账号。

搜索排序和推荐很像,都是从海量物品里挑出一个有序列表,但多了一个硬约束:结果必须和查询词相关。用户搜“跑步鞋”,排第一的却是销量最高的电饭煲,模型再懂用户也没用。这道题考的是检索和排序怎么分工,怎么从点击日志里提取可靠的相关性信号,以及怎么评估一个排序。

1. 需求澄清

功能需求

  • 用户输入查询词,返回按相关性和购买可能性排序的商品列表,支持翻页
  • 处理拼写错误、同义词和长尾查询

非功能需求

  • 规模:1 亿件商品;峰值 5 万 QPS
  • 延迟:端到端 p99 < 300 ms,其中排序阶段约 50 ms
  • 价格和库存变化后,几分钟内反映到搜索结果里

业务目标:提升每次搜索带来的成交(转化率、成交额),同时守住相关性,用户搜不到想要的东西会直接离开。广告混排不在本题范围内。

2. 问题形式化

搜索排序的两阶段流程
滚动到此处后加载图表…
图表可横向滚动,展开后可缩放查看。
查看图表源码
flowchart LR
  accTitle: 搜索排序的两阶段流程
  query["查询词"] --> qu["查询理解:纠错、同义词、类目预测"]
  qu --> lexical["倒排索引召回(BM25)"]
  qu --> semantic["向量召回(语义匹配)"]
  lexical --> merge["合并去重:约 1000"]
  semantic --> merge
  merge --> ltr["学习排序(LTR):取前 50"]
  ltr --> page["结果页"]

排序部分是学习排序(learning to rank),有三种建模方式:

方式做法特点
Pointwise给每个(查询,商品)预测一个相关分或点击概率简单,但不直接优化顺序
Pairwise学习“A 应该排在 B 前面”更贴近排序,RankNet 是代表
Listwise直接优化整个列表的指标(如 NDCG)和评估指标一致;LambdaMART 用按 NDCG 变化加权的成对梯度来近似

LambdaMART(GBDT 实现)是很强的基线:训练快,适合表格特征,还能看特征重要性。

3. 数据与特征

标签有两个来源:

  • 人工相关性标注:抽样一批查询,请标注员给每个结果打 0~4 分。无偏但昂贵,规模有限,主要用来评估,也可以训练专门的相关性模型
  • 用户行为日志:点击、加购、购买。量大、及时,按强度分级,比如购买 > 加购 > 点击 > 展示未点击

行为日志有位置偏差:排在前面的商品点击多,不全是因为更相关。常见处理:

  • 跳过前面(skip-above):用户点了第 5 个、跳过了第 1~4 个,就认为第 5 个比前面几个更相关,生成成对样本
  • 逆倾向加权:先估计每个位置被看到的概率(可以在少量流量上随机交换相邻结果来估计),再用它给点击样本加权

特征:

类别例子
查询长度、预测的类目、查询热度
商品标题、类目、价格、评分、近 30 天销量、上架时间
查询-商品标题和描述上的 BM25 分数、查询向量与商品向量的相似度、类目是否匹配、该查询下这个商品的历史点击率(加平滑)
用户与上下文品牌偏好、价格带偏好、所在地区(配送)

长尾查询:大部分不同的查询词都很少出现,查询-商品级别的历史统计几乎是空的。这时要依赖内容相关性特征和语义向量;统计特征要做平滑(例如加先验的贝叶斯平均),防止几次偶然点击带来噪声。

4. 模型

  1. 基线:BM25 分数加销量,手调权重
  2. 主模型:LambdaMART,几百个特征,直接优化 NDCG
  3. 语义召回:双塔(bi-encoder)把查询和商品编码成向量,解决“词不同、意思相同”的问题,比如“跑鞋”和“运动鞋”
  4. 精细相关性:交叉编码器(cross-encoder,把查询和商品标题拼在一起输入 Transformer)最准,但每一对都要跑一次大模型,只用于前 50 个结果,或者蒸馏成小模型

查询理解同样关键:拼写纠错、同义词扩展、类目预测(“苹果”是水果还是手机)都发生在召回之前。召回阶段丢掉的商品,排序再好也救不回来。

5. 训练与评估

离线:

  • 人工标注集上的 NDCG@10,这是发版前的关卡
  • 召回阶段的 Recall@1000:相关商品有没有进入候选
  • 日志上去偏后的点击指标作为补充

线上:

  • 主指标:每次搜索的转化率、每次搜索的成交额
  • 护栏:零结果率、查询改写率(用户马上换一个词重搜,说明不满意)、延迟
  • 交错实验(interleaving):把两个排序器的结果交替合并成一个列表给同一个用户,看点击落在哪一边。它比 A/B 灵敏得多,需要的流量少,适合快速筛选候选模型,最后再用 A/B 确认业务指标

6. 服务与监控

一次搜索:查询理解 → 并行查询倒排索引的各个分片(scatter-gather)和向量索引 → 每个分片先用轻量模型粗排,返回各自的前几百个 → 聚合层合并成约 1000 个 → 取特征 → LambdaMART 精排 → 前 50 个可选地再用小型交叉编码器重排 → 返回。

  • 商品特征大多存在索引里,查询-商品特征在线计算
  • 价格和库存走增量更新,几分钟内生效;更换向量模型后要重建向量索引
  • 热门查询的结果可以短时间缓存,带个性化的部分单独处理
  • 监控:零结果率、各位置的点击率、查询分布的季节性变化;每次发版前在人工标注集上跑一遍 NDCG,防止相关性回退

7. 回答分级

检索与排序的分工

  • Bad:只用关键词匹配加销量排序,或者用一个大模型给全部商品打分。
  • Good:倒排索引加向量召回,合并后用 LambdaMART 精排;能说出 pointwise、pairwise、listwise 的区别。
  • Great:分片上先粗排、聚合层再精排;交叉编码器只用于前几十个结果;把查询理解放在召回之前,说明召回丢掉的商品排序救不回来。

标签与偏差

  • Bad:直接把点击当作相关性标签。
  • Good:区分人工标注和行为日志,按购买、加购、点击分级。
  • Great:处理位置偏差(skip-above、逆倾向加权),讲清怎么估计位置倾向;长尾查询依赖内容特征,统计特征做平滑。

评估

  • Bad:只看点击率。
  • Good:离线用标注集上的 NDCG@10,线上 A/B 看转化率和零结果率。
  • Great:用交错实验快速比较排序器,再用 A/B 确认业务指标;把查询改写率当作不满意的信号;发版前用标注集防止相关性回退。

8. 不同级别的期望

级别期望
Mid(L4)在提示下给出召回加排序的结构,知道用 NDCG 评估,能列出主要特征
Senior(L5)讲清学习排序的几种方式、行为日志的位置偏差和处理办法,给出离线和线上的评估方案
Staff(L6+)从相关性和成交的平衡出发,覆盖查询理解、语义召回、交叉编码器的成本取舍、交错实验和发版关卡

章节测验

6 道选择题
  1. 用户点击了第 5 个结果,跳过了第 1~4 个。按 skip-above 的思路,可以得到什么训练信号?
  2. 人工相关性标注在搜索系统里最主要的用途是?
  3. LambdaMART 和普通的 pointwise 回归相比,关键区别是什么?
  4. 交叉编码器(cross-encoder)最准,为什么通常只用在前几十个结果上?
  5. 交错实验(interleaving)相比普通 A/B 测试的主要优势是?
  6. 对于很少出现的长尾查询,哪类特征最可靠?

章节讨论

正在加载讨论…