第 25 章 / 共 57 章 · 约 5 分钟
ML 案例 2:电商搜索排序
先花 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. 模型
- 基线:BM25 分数加销量,手调权重
- 主模型:LambdaMART,几百个特征,直接优化 NDCG
- 语义召回:双塔(bi-encoder)把查询和商品编码成向量,解决“词不同、意思相同”的问题,比如“跑鞋”和“运动鞋”
- 精细相关性:交叉编码器(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+) | 从相关性和成交的平衡出发,覆盖查询理解、语义召回、交叉编码器的成本取舍、交错实验和发版关卡 |