当理论撞上现实:一个酒店排序系统的离线评估演进史

从 NDCG 到 AUC + 线上 top-K 的一次工程取舍

Posted by Schoko on October 13, 2026

写在前面:

今天和AI从NDCG的原理、实际操作到可能遇到的问题、是否一定要选择这个指标,做了多轮讨论。最后让AI把来回的对话内容总结为以下此篇博客。


简介

最近我在思考一个酒店搜索排序系统的离线评估方案。起初的问题很具体:NDCG 是什么,怎么在酒店排序中落地?

但继续往下想,问题很快就从“怎么算”变成了“值不值得算”:相关性由谁定义?人工经验错了怎么办?如果线上实验已经很便宜,还有必要建一套完整的离线 NDCG 体系吗?如果整个项目只有一个工程师呢?

这篇文章保留了这个一问一答的推进过程。它最后得到的不是“标准答案”,而是一个当下更合适的选择:离线以 AUC 为主,线上用 A/B 实验与 top-K 指标做最终验证,同时保留少量低成本的诊断能力。


目录


一、NDCG 是什么?在酒店排序中怎么用?

最初的问题:简单讲解 NDCG 的原理,不要太多数学公式。如果用在工业级酒店搜索排序中,从定义相关性开始,具体应该怎么做?

假设用户搜索“上海外滩酒店”,系统返回了 20 家酒店。我们关心的不只是有没有好酒店,还关心好酒店是不是排在前面。用户往往只看头部几个结果,第 1 位和第 20 位的价值显然不一样。

NDCG(Normalized Discounted Cumulative Gain)正是为这类问题设计的。它可以拆成三个直觉:

  1. Gain,增益:结果不只有好和坏,还可以有等级。例如下单是强正反馈,点击是弱正反馈。
  2. Discount,折损:相同的好结果,出现得越靠后,价值越低。
  3. Normalization,归一化:用当前候选集可能达到的理想排序做分母,让不同搜索请求大致可比。

一句话概括:NDCG 衡量的是当前排序距离理想排序还有多远。

在酒店场景中,一套完整流程大致是:抽取真实搜索请求,为“请求—酒店”赋予相关性等级,固定候选集和特征快照,用新旧模型重新打分,再按请求计算 NDCG@K 并做分桶对比。

这里最关键的不是公式,而是相关性的定义决定了我们究竟在优化什么。


二、召回结果都符合条件,相关性该怎么定义?

我的疑问:搜索可能只有城市和入离日期,召回结果已经全部满足硬约束。它们理论上都“相关”,但价格、星级、位置各不相同。这时怎么定义相关大小?训练数据又混合了多个 query,评估时如何对单个 query 排序?

这个担心非常实际。酒店的“好”高度主观:商务用户可能看重位置,度假用户看重环境,预算敏感用户又更看重价格。让标注员排出唯一正确顺序,很容易变成“用标注员的喜好代替用户的喜好”。

更务实的做法,是不去宣称自己找到了客观真理,而是选择一个可观测的代理信号:

信号来源 优点 局限
人工标注 可解释,可覆盖未曝光酒店 贵,一致性和代表性有限
行为标签 可规模化,更接近真实选择 受位置、旧模型和曝光机会影响
业务规则 启动快,能表达业务目标 规则本身也是一个人造模型
混合方案 在规模和可控性之间折中 流程更复杂,需要持续维护

对当前场景,最低成本的起点是行为分层:下单 = 2,点击未下单 = 1,曝光未点击 = 0。但它评估的是在旧系统曝光机制下被观测到的行为,不是用户喜好的无偏真值。没曝光的酒店不应直接当作 0,前排酒店的点击也会被位置天然放大。

至于多 query 混合训练,它与评估并不冲突。模型对“某次搜索请求 + 某家酒店”打分,评估时再按 request_id 分组排序即可。这里最好使用一次搜索的请求或 session 标识,而不是只用“上海酒店”这种文本,因为日期、人数和用户上下文也是需求的一部分。


三、AUC 为主、NDCG 为辅时,NDCG@K 怎么算?

我想到一个折中方案:AUC 作为基础离线指标,NDCG 作为辅助指标。不考虑未曝光样本;有曝光的样本里,未点击为 0,点击未下单为 1,点击且下单为 2。这样的 NDCG@K 如何计算?

这套定义可以先用来快速验证:

行为 rel 处理
曝光未点击 0 参与评估
点击未下单 1 参与评估
点击并在归因窗口内下单 2 参与评估
未曝光 — 暂不进入评估集

对每个搜索请求,先按模型分排序,取前 K 个结果,计算:

\[\mathrm{DCG@K}=\sum_{i=1}^{K}\frac{2^{rel_i}-1}{\log_2(i+1)}\]

其中 $rel_i$ 是第 $i$ 个位置的相关性等级。rel=0/1/2 对应的增益是 0/1/3,高等级行为会得到更大奖励;位置越靠后,贡献越小。

再把同一请求的候选酒店按 rel 从高到低排列,用同样的公式得到 IDCG@K。最后:

\[\mathrm{NDCG@K}=\frac{\mathrm{DCG@K}}{\mathrm{IDCG@K}}\]

例如某次搜索的实际标签顺序是 [2, 1, 0, 0]:

位置 rel 增益 折损后贡献
1 2 3 3.000
2 1 1 0.631
3 0 0 0
4 0 0 0

DCG@4 = 3.631。如果这恰好也是理想顺序,那么 IDCG@4 = 3.631,NDCG@4 = 1。

落地时可按以下流程执行:

固定搜索请求与曝光候选快照
    → 按行为映射 0/1/2 标签
    → 用待评估模型重新打分
    → 按 request_id 分组计算 NDCG@K
    → 跨请求聚合,并做分桶对比

我更倾向于同时保留两种聚合:Macro NDCG 让每个有效请求等权,更容易看到长尾;流量加权 NDCG 更接近整体用户体感。两者差异很大时,往往意味着热门与长尾场景表现不均。

还有几个口径要先定好:

  • K 要参考真实浏览深度,可同时观察 @5、@10 和 @20。
  • IDCG=0 的请求可以不进入均值,但必须同时报告其比例。
  • 下单要有明确归因窗口。
  • 候选集、特征、标签口径和模型版本都要可追溯。

这里也要修正一个容易出现的误解:标准 NDCG 不需要对相同等级的样本设计二级排序。 如果两家酒店的 rel 都是 2,它们的 gain 相同,互换位置不会改变 IDCG。如果希望“同为下单,但位置匹配更好的酒店价值更高”,那实际上是在重新定义相关性或 gain。

最后,只用已曝光样本的 NDCG 主要评估旧曝光集中的重排效果,它无法回答“一家从未曝光的酒店被新模型排上来后会怎样”。这是方案的边界,不是多算一个公式就能解决的。


四、人工标准会犯错,评估尺度改了怎么办?

继续追问:初期人工经验可能有误。如果后期修改了指标,前后尺度不一致,会怎么影响模型评估?NDCG 是否过于依赖人工经验,而人工又无法归纳每个人的喜好?

先承认一个事实:所有离线指标都带有人为定义。 AUC 看似客观,但“点击是否算正样本”“下单归因多久”也是人定的。NDCG 只是定义空间更大,主观性更容易被看见。

如果标准从 v1 改成 v2,最直接的影响是:两个版本的绝对分数不能直接比。 模型的 NDCG 从 0.72 变成 0.68,可能只是尺子换了,不是模型变差了。

处理方式可以很工程化:

  1. 将标签规则、数据快照、归因窗口、K 和聚合方式一起版本化。
  2. 任何两个模型都在同一版本标准下比较。
  3. 更改标准后,用新口径重算至少一个线上基线和当前候选模型。
  4. 如果无法重算,就承认指标断层,从新口径重新建立基线。
  5. 切换前让新旧口径双跑一段时间,看它们是否改变了模型的相对排名。

还需要保留一个警惕:“同一把有偏的尺子”并不会自动产生可信结论。如果尺子奖励的方向就是错的,稳定地比较也只是稳定地选错模型。因此,可比性是必要条件,不是充分条件;离线指标还必须定期和线上结果做校验。

人工也确实无法归纳每个人的动态喜好。离线指标能做的,是在给定人群和场景中衡量平均趋势;个性化效果仍然要靠分桶分析和线上实验验证。


五、离线只看 AUC,还是同时看 NDCG@K?

方案 1:离线评估和模型选择只看 AUC,A/B 实验看 CVR、CR、CTR 和 top-K 点击/下单指标。方案 2:离线同时看 AUC 和 NDCG@K,线上实验相同。两者的优劣是什么?

维度 方案 1:只看 AUC 方案 2:AUC + NDCG@K
建设成本 低,基本复用训练流程 较高,需要请求级数据和独立评估流水线
维护成本 低 较高,需维护快照、标签和口径版本
头部敏感度 弱 强
分级反馈 通常只表达正负 可区分点击与下单等强弱信号
离线诊断 较弱 较强,便于按请求和场景分析
指标偏差 存在,但口径较简单 存在,且等级定义更复杂
决策速度 快 可能被指标分歧拖慢
适合团队 资源紧、迭代快、A/B 成熟 资源足、头部优化进入精细阶段

AUC 成熟、稳定、便宜,适合快速筛掉明显不好的模型。但它并不特别关心用户最先看到的几个位置。如果将不同请求的样本全部混在一起算全局 AUC,还可能被跨请求的容易样本主导。此时可同时关注按请求聚合的 GAUC,但它仍然不是头部指标。

NDCG@K 补上了头部敏感性和分级反馈,但它不是“白送的额外准确性”。如果行为标签受旧排序严重影响,或 NDCG 对线上转化没有稳定预测力,那么多一个指标反而只是多一个决策噪声。

因此,选择时应该问三个问题:上线一次实验有多贵?候选模型是否多到必须强力预筛?历史上 NDCG@K 对核心线上指标是否真有预测力?


六、线上 top-K 点击和下单指标,能补上短板吗?

我在实际中已经监控了线上 top-K 点击比和下单比。这对方案 1 有改善吗?

有,而且是很有价值的改善。AUC 对头部顺序不敏感,top-K 指标却直接观察用户在头部结果中的真实行为。可以把这套做法称为方案 1+:

离线:AUC / GAUC 做基础质量门槛
    → 线上:A/B 实验观察 CTR、CVR、CR 和 top-K 指标
    → 最终:根据真实业务效果决策
维度 线上 top-K 指标 离线 NDCG@K
时机 模型上线后 模型上线前
数据 当前真实用户行为 历史行为或标注数据
作用 验证实际效果 预测候选模型的头部表现
主要优势 真实,直接对应业务 无需暴露真实流量即可初筛
主要短板 需要上线,受流量噪声和位置影响 受标签定义和旧曝光机制影响

但“top-K 点击比”可能对应不同公式,口径一定要写清:

指标 示例定义 回答的问题
top-K CTR top K 点击数 / top K 曝光数 头部每次曝光获得点击的能力
top-K 下单率 top K 归因下单数 / top K 曝光数 头部曝光带来下单的能力
top-K 点击贡献占比 top K 点击数 / 全列表点击数 点击是否集中在头部
top-K 下单贡献占比 top K 下单数 / 全列表下单数 下单是否集中在头部

贡献占比变高,可能是头部变好了,也可能是后部变差了。所以至少要固定 K、分母、归因窗口和实验单元。

方案 1+ 仍然只能事后验证,全局平均也会掩盖长尾场景,而且指标往往只告诉我们“变差了”,不告诉“为什么”。不过,按城市、价格带和请求类型分桶,再定期抽取 bad case,已经能低成本地补上很多诊断能力。


七、如果迭代快、上线便宜,方案 1+ 是否更有优势?

如果团队支持快速迭代,实验上线成本也低,那么方案 1+ 是不是更有优势?

是的。离线指标的一个重要价值,是在上线验证很贵时,先廉价地筛选候选模型。当实验平台成熟、流量充足、发布与回滚都很轻量时,上线本身就变成了一种相对便宜的评估手段。

线上验证越便宜,重型离线筛选的边际价值往往越低。

但这个判断有前提:

  • A/B 的发布、分流、监控和回滚确实成熟。
  • 流量足够,能在可接受时间内得到稳定结论。
  • 实验位不稀缺,候选模型数量也可控。
  • 核心线上指标对排序变化足够敏感。
  • 有安全门槛、护栏指标和快速回滚能力。

如果流量小、一个实验要跑几周,或一次有几十个候选模型,那么离线头部指标的价值会重新上升。所以这不是“线上永远优于离线”,而是一道成本题。

当前可以以方案 1+ 为主干,同时保留几个廉价的“期权”:存一份评估数据快照,分桶看线上指标,定期抽取头部 bad case,并持续记录离线 AUC 与线上结果的对应关系。


八、如果只有一个工程师,应该选哪套方案?

如果这个大型项目只有一个工程师负责全链路开发,而且她还要开发和维护其他项目,更应该支持哪个方案?

在这个约束下,我会明确选方案 1+。

原因不是 NDCG 没价值,而是一套指标的真实成本远不止“写一个计算函数”。它还包括数据口径、快照、归因、版本、监控、回溯、文档和持续校准。如果没有人长期照料,一套精密但失修的评估体系,可能比没有它更危险——因为它会给人一种“已经严谨验证”的错觉。

事情 方案 1+ 完整 NDCG 体系
离线评估 复用 AUC/GAUC 额外建立请求级评估流程
线上验证 复用现有 A/B 平台 同样需要 A/B,不会被取代
长期维护 指标口径与分桶报表 还需维护标签、快照和版本
单点风险 标准业务链路相对易接手 大量隐性知识可能随人流失
对迭代的占用 低 持续占用时间和注意力

不过,轻量不等于什么都不做。只有一个人负责时,反而更应该把少数关键事情做成默认动作:

  1. 离线 AUC/GAUC 作为基本门槛,排除明显退化。
  2. 线上看核心业务指标与 top-K 指标,设置护栏和快速回滚。
  3. 报表至少按城市、价格带、搜索类型分桶。
  4. 保存一份请求、候选集和行为标签快照,但暂时不建重型平台。
  5. 定期抽看 bad case,弥补数字无法解释原因的问题。
  6. 用一页文档记录指标定义、实验流程和决策规则,降低单点风险。

等团队扩编、A/B 实验位变稀缺、候选模型大量增加,或现有线上指标无法支撑精细化迭代时,再考虑完整 NDCG。在此之前,把稀缺的工程时间留给模型、数据质量和线上闭环,更容易产生真实回报。


九、工业界真的没有标准答案吗?

最后的感受:一个工业界推荐项目可能有理论上的技术标准答案,但实际操作中似乎永远没有唯一答案。最后还是要权衡资源、人力和成本,做出一个可能不完美、但相对较好的系统。

这大概就是整次讨论中最重要的结论。

工业系统不是一道只有一个答案的数学题,而是一个不断变化的多目标约束优化问题:目标可能互相冲突,人力、时间、流量和实验位都有上限,历史数据又必然受旧系统影响。更麻烦的是,这些约束还会不断变化。

维度 理论上的理想方案 实际工程方案
前提 数据、人力和时间相对充足 多种资源同时受限
目标 追求单个指标的最优 在效果、成本、风险和维护性之间取舍
方式 假设问题定义稳定 假设标准会变,需要迭代修正
评价 是否达到最优 是否足够好、可落地、可维护、可演进

回看这次方案的演进:

想引入 NDCG@K
    → 发现相关性定义、历史曝光偏差和维护成本
    → 考虑 AUC 为主、NDCG 为辅
    → 发现团队的线上试验很便宜
    → 转向 AUC + 线上 top-K 的方案 1+
    → 在单工程师约束下,增加分桶、快照和 bad case 分析

这条路径不是从“正确”退到“凑合”,而是随着约束逐步暴露,把方案收敛到真正可执行的位置。

有意识地接受不完美 随便做
知道哪里不完美 不知道风险在哪里
明确取舍由什么约束造成 没有建立约束意识
保留监控、回滚和升级路径 出问题后才临时找补丁
核心目标清晰 连“好”是什么都没定义

我现在更愿意这样理解理论的价值:理论告诉我们天花板在哪里,工程判断力决定了当下应该走到哪里。

对当前项目,这意味着:先用方案 1+ 稳定跑通“训练—上线—验证—诊断—回滚”闭环,再根据真实瓶颈决定是否投资 NDCG。如果未来实验资源变紧、团队扩大、候选模型增多,当初保留的数据快照和指标文档,就会成为继续演进的起点。


写在最后

这篇文章记录的不是一套通用最佳实践,而是一次真实的方案收敛过程。NDCG 仍然是有价值的排序指标,AUC 也不是只要计算就一定可靠,线上 A/B 更不是没有风险的万能答案。

真正重要的是:我们知道每个工具在回答什么问题,它没有回答什么问题,以及为了得到这个答案,团队需要长期支付多少成本。

好的系统不一定是最完整的系统,而是当下能运行、风险可见、且为下一步留了路的系统。