本文是 搜推算法笔记 第 4 篇。索引见 搜推算法 · 知识索引。
上一篇:向量检索与 ANN · 下一篇:AB 实验与线上迭代
召回把候选捞进池子,精排决定「谁排前面」。但「排得好」怎么定义?训练 LambdaMART 时 metric=ndcg,上线看 CTR/CVR——模型、指标、业务目标三者必须对齐,否则离线涨、线上不涨。
这篇把 LTR 精排和评价指标放在一起讲:先理解排序学习范式,再搞懂 NDCG 在量什么,最后串成完整的训练—评估—上线链路。
一、LTR 是什么
Learning to Rank:用机器学习学习「在给定 Query 下,文档的排列顺序」。
输入特征 (x(q,d,u)),输出分数 (s),按 (s) 降序排列。
| 普通分类/回归 | LTR | |
|---|---|---|
| 样本 | 独立 i.i.d. | 同 Query 下文档互相依赖 |
| 优化目标 | 点上的对错 | 列表的顺序质量 |
| 评价指标 | Acc/AUC | NDCG/MAP/MRR |
1 | 召回(够、相关)→ 精排 LTR(序)→ 机制层(打散/过滤)→ 曝光 |
二、三种 LTR 范式
| 维度 | Pointwise | Pairwise | Listwise |
|---|---|---|---|
| 样本单位 | 单个 (q,d) | 序对 (d_i, d_j) | 整个列表 |
| 损失函数 | MSE / LogLoss | RankNet | LambdaRank |
| 优化目标 | 点相关度 | 序对偏好 | 列表 NDCG |
| 工业界 | 早期 | 中等 | 精排主流 |
同一 Query 下三文档真实相关度 A=2, B=1, C=0:
- Pointwise:努力把 A 预测成 2、B 成 1——B、C 分数接近时顺序不稳
- Pairwise:强调 A>B、A>C、B>C
- Listwise:直接希望整页 NDCG 高——A 在第 1 最重要
Pointwise 损失
把相关度 (rel \in {0,1,2,3}) 当回归/分类目标:
[
\mathcal{L}=\sum_{(q,d)} \bigl(\hat{s}(q,d)-rel(q,d)\bigr)^2
]
问题:同一 Query 下文档分数绝对值不重要,相对顺序才重要;且不同 Query 的相关度尺度可能不同。
Pairwise:RankNet
对文档对 ((d_i, d_j)),若 (rel_i > rel_j),希望 (\hat{s}_i > \hat{s}_j)。RankNet 用 logistic 损失:
[
P_{ij}=\sigma(\hat{s}i-\hat{s}j)
\qquad
\mathcal{L}=-\bar{P}{ij}\log P{ij}-(1-\bar{P}{ij})\log(1-P{ij})
]
(\bar{P}_{ij}=1) 当 (rel_i>rel_j)。梯度 (\frac{\partial \mathcal{L}}{\partial \hat{s}_i}) 只依赖 (d_i) 与 (d_j) 的相对差——pairwise 比 pointwise 更贴排序。
Listwise:直接优化列表指标
Listwise 把一次 Query 的全部候选当作一个样本,损失直接对齐 NDCG/MAP。LambdaRank / LambdaMART 是工业界最实用的 Listwise 近似——不直接对 NDCG 求导(不可微),而是用 λ 梯度 近似 NDCG 交换文档位置带来的增益。
三、LambdaMART 精排
3.1 原理
LambdaMART = LambdaRank + 梯度提升树。 LightGBM objective=lambdarank 即此族。
对文档对 ((i,j)),若顺序错了且交换后 NDCG 提升大,给更大的梯度推力——比纯 Pairwise 更贴 NDCG。
λ 梯度直觉:
对 Query 下文档 (i, j),若 (rel_i > rel_j) 但 (\hat{s}_i < \hat{s}_j)(排错了),LambdaRank 给 (i) 一个向上的伪梯度:
[
\lambda_{ij}=\frac{|\Delta\mathrm{NDCG}_{ij}|}{1+e^{\hat{s}_i-\hat{s}_j}}
]
- (|\Delta\mathrm{NDCG}_{ij}|):交换 (i,j) 位置后 NDCG 的变化量——排在前面的位置交换影响更大(因为 (\log_2) 折损)
- 分母是 RankNet 的 logistic 项——分数差越大,梯度越小(已经分很开了)
LambdaMART 用 GBDT 拟合这些 λ 梯度:每棵树修正上一轮排序错误,多轮迭代后 NDCG 收敛。LightGBM 内部对 lambdarank 做了高效实现,支持 label_gain 自定义多级相关度增益。
3.2 精排模型选型
| 模型 | 类型 | 推理速度 | 效果上限 | 适用 |
|---|---|---|---|---|
| LambdaMART | Listwise GBDT | 快 | 中~高 | 搜推精排默认 |
| XGBRanker | Pairwise GBDT | 快 | 中~高 | 备选 |
| DIN/DeepFM | 深度排序 | 中 | 高 | 大数据 + GPU |
| Cross-Encoder | 交叉编码 | 慢 | 很高 | Top50 rerank |
LambdaMART 性价比最高——LightGBM 原生支持、训练快、线上毫秒级、特征可解释。Cross-Encoder 适合精排后的 rerank 层。
3.3 Label 设计
| Label | 信号强度 | 噪声 | 适合优化 |
|---|---|---|---|
| 点击 | 弱 | 高(位置偏差) | CTR |
| 加购 | 中 | 中 | 购买意向 |
| 下单/成交 | 强 | 相对低 | CVR/GMV |
| 人工标注 | 准 | 低 | 相关性(贵) |
电商常见策略:主 label 用转化,辅助分析用点击。转化极稀时可先用点击训初版,积累数据后再切换——需重新做去偏和 AB。
3.4 位置偏差与去偏
前排天然更容易被点击。直接用点击当 label 且忽略位置,模型会「学着把已靠前的继续排前」。
[
w=\frac{1}{\log_2(\mathrm{rank}+1)}
]
不要把 position 当特征——在线预测时没有「最终位置」作输入,会导致 Train≠Serve 和信息泄漏。
进阶:IPS 逆倾向加权
位置 (k) 的曝光概率 (P(\mathrm{click}\mid k)) 随 (k) 递减。逆倾向评分(Inverse Propensity Scoring):
[
w_i = \frac{1}{P(\mathrm{observed_at_rank}_i)}
]
用历史位置分布估计 (P),给后排被点击的样本更高权重——「能在第 8 位还被点,说明真的很相关」。工业界也常用 DNN 去偏模型(PAL、Unbiased LTR)联合建模位置与点击。
| 类型 | 例子 |
|---|---|
| Query | 长度、预测类目、是否品牌词 |
| Doc | log_price、log_sales、质量分 |
| 匹配 | BM25、向量余弦、词重叠、类目一致 |
| 召回 | 来自哪一路、召回分 |
| 上下文 | 端、时间、页类型 |
好特征:可在线计算、无泄漏、Train=Serve 对齐、稳定(log1p 变换)。特征重要性 ≠ 因果,上线仍以 AB 为准。
训练代码见 Python 搜推技术栈。
四、评价指标:怎么度量「排得好」
4.1 为什么排序需要专用指标
分类准确率把「排第 1 的错误」和「排第 100 的错误」一视同仁。用户几乎只看前几位——排序指标必须体现越靠前越重要。
4.2 指标选型总表
| 指标 | 位置敏感 | 多级相关 | 典型场景 | 常用层 |
|---|---|---|---|---|
| Recall@K | 否 | 否 | 相关物品进没进池 | 召回 |
| Precision@K | 否 | 否 | 前排有几个相关 | 召回/精排 |
| MRR | 是(第一个) | 否 | 导航/问答 | 搜索 |
| MAP | 部分 | 否 | 传统 IR | 搜索 |
| NDCG@K | 是 | 是 | 电商/内容排序 | 精排 |
| AUC | 否 | 否 | CTR 预估 | 粗排 |
选型口诀:
- 召回看 Recall@K——好东西进没进池
- 精排看 NDCG@K——进池后顺序对不对
- 上线看 CTR/CVR/GMV——业务最终裁判
4.3 NDCG 详解
三句话:相关度越高增益越大;排越后折损越多;用理想排序归一化到 0~1。
[
\mathrm{DCG@K}=\sum_{i=1}^{K}\frac{rel_i}{\log_2(i+1)}
\qquad
\mathrm{NDCG@K}=\frac{\mathrm{DCG@K}}{\mathrm{IDCG@K}}
]
手算: 相关度 [1, 0, 1, 0],K=4
1 | DCG = 1/log2(2) + 0 + 1/log2(4) = 1.0 + 0.5 = 1.5 |
4.4 同一结果,不同指标的不同反应
相关 G={A,C},排序=[B, A, D, C]:
| 指标 | 值 | 解读 |
|---|---|---|
| P@4 | 0.5 | 4 个里 2 个相关 |
| R@4 | 1.0 | 相关全找到了 |
| MRR | 0.5 | 第一个相关在第 2 位 |
| NDCG@4 | ≈ 0.77 | 考虑了位置折损 |
A 排到第 1 时:MRR 变 1.0,NDCG 也涨,但 P@4 不变——这就是 NDCG 比 P@K 更适合排序的原因。
4.5 MAP 与 MRR 公式
MRR(Mean Reciprocal Rank)——第一个相关结果的位置:
[
\mathrm{RR}=\frac{1}{\mathrm{rank_of_first_relevant}}
\qquad
\mathrm{MRR}=\frac{1}{|Q|}\sum_{q\in Q}\mathrm{RR}_q
]
适合「只要一个正确答案」的场景(导航型搜索、FAQ)。
MAP(Mean Average Precision)——二值相关度下,各相关文档 Precision 的平均:
[
\mathrm{AP@K}=\frac{1}{\min(R,K)}\sum_{k=1}^{K}\mathrm{P@}k \cdot rel_k
\qquad
\mathrm{MAP}=\frac{1}{|Q|}\sum_{q}\mathrm{AP}_q
]
(R) 为相关文档总数,(rel_k \in {0,1})。MAP 对「找全所有相关」敏感,电商多档相关度更常用 NDCG。
4.6 NDCG 的信息论视角
(\mathrm{Gain}_i = 2^{rel_i}-1)(常用变体),位置折损 (\mathrm{Discount}_i = \log_2(i+1)):
[
\mathrm{DCG@K}=\sum_{i=1}^{K}\frac{2^{rel_i}-1}{\log_2(i+1)}
]
直觉: 相关度从 2→3 的增益 ((2^3-1)-(2^2-1)=4) 大于 0→1 的增益 1——高相关文档价值非线性更高;同时第 1 位权重是第 10 位的约 3.5 倍((\log_2 11 / \log_2 2))。
1 | 日志 → 按 session 构图 → label + 去偏 weight |
Train = Serve
离线特征公式 ≡ 在线特征公式。典型不一致:离线用次日销量、在线用实时销量;离线有召回分、在线填 0。
离线 vs 线上
| 类型 | 例子 | 作用 |
|---|---|---|
| 离线 | NDCG@K、Recall@K | 训练闸门、快速迭代 |
| 线上 | CTR、CVR、无结果率 | 最终裁判 |
| 护栏 | P99、降级率 | 安全 |
1 | 离线 NDCG:必要非充分(过闸) |
离线涨、线上不涨?
- Train ≠ Serve
- 优化点击但要的是转化
- 位置偏差未处理
- 延迟变差 / 机制层改变列表
- 样本选择偏差(只有曝光日志)
在线推理与降级
1 | 候选 → 批量算特征 → predict → argsort → 机制层 → 分页 |
| 触发 | 动作 |
|---|---|
| 超时 / 异常 | 返回召回序 |
| kill-switch | 强制关闭精排 |
| 模型损坏 | 回退上一版本 |
可用性 > 最优效果。
下一步
- 用 AB 证明线上效果 → AB 实验与线上迭代
- Python 训练与服务骨架 → Python 搜推技术栈


