5.5 评估指标与基准测试


文档摘要

5.5 评估指标与基准测试 全章的杠杆都上过了,本节回答怎么证明它们有效。作为第五章的收束与方法论底座,这里给出检索系统评估的三组核心指标——召回、吞吐、尾延迟——的严格定义与测法,再给出一套"用自己的数据做基准"的操作规程。5.3 的调参流程、5.4 的预热验证,都假设这套测量已经存在;反过来说,没有它的调优都不可信。 三组指标,各管一件事 召回率回答"找回来的对不对"。测法固定:一批探针查询,先用精确检索算出每条的标准 TopK,再与被测系统的结果求交集,交集占比即召回率。要点有三:K 必须与业务返回条数一致(TopK 变了召回不可比);探针必须采样自真实查询(随机向量测不出业务分布,5.3 强调过);量化或图参数的召回波动要配置信区间——探针量少时多跑几组求均值方差,别被单次波动骗了。

5.5 评估指标与基准测试

全章的杠杆都上过了,本节回答怎么证明它们有效。作为第五章的收束与方法论底座,这里给出检索系统评估的三组核心指标——召回、吞吐、尾延迟——的严格定义与测法,再给出一套"用自己的数据做基准"的操作规程。5.3 的调参流程、5.4 的预热验证,都假设这套测量已经存在;反过来说,没有它的调优都不可信。

三组指标,各管一件事

召回率回答"找回来的对不对"。测法固定:一批探针查询,先用精确检索算出每条的标准 TopK,再与被测系统的结果求交集,交集占比即召回率。要点有三:K 必须与业务返回条数一致(TopK 变了召回不可比);探针必须采样自真实查询(随机向量测不出业务分布,5.3 强调过);量化或图参数的召回波动要配置信区间——探针量少时多跑几组求均值方差,别被单次波动骗了。吞吐回答"扛得住多少并发",常用每秒查询数表达,测量时并发度要爬坡着测出拐点,而不是只报一个峰值——拐点之后延迟陡增,那个数字没有业务意义。尾延迟回答"最差的时候有多差",用百分位表达:一半请求快于 P50、九成五快于 P95、九成九快于 P99。平均延迟会掩盖长尾,而用户体验恰恰由长尾决定——十次查询里九次二十毫秒、一次两秒,平均值三十毫秒很好看,用户记住的却是那次两秒。

图 5-3 召回率与延迟的权衡曲线

图 5-3 召回率与延迟的权衡曲线

把测量写成代码

评估的价值在于可复现,所以测量本身应当是代码而不是操作手册:

import numpy as np, time from concurrent.futures import ThreadPoolExecutor def recall_at_k(results, truths): """results 与 truths 均为按查询组织的 id 列表""" hit = sum(len(set(r) & set(t)) for r, t in zip(results, truths)) return hit / sum(len(t) for t in truths) def bench(index, probes, truths, top_k=10, workers=32): t0 = time.perf_counter() with ThreadPoolExecutor(max_workers=workers) as pool: results = list(pool.map(lambda q: index.search(q, top_k), probes)) wall = time.perf_counter() - t0 lat = [] for q in probes[:500]: # 单发延迟单独测,避免并发干扰 s = time.perf_counter() index.search(q, top_k) lat.append(time.perf_counter() - s) return { "recall": recall_at_k(results, truths), "qps": len(probes) / wall, "p50": float(np.percentile(lat, 50)), "p95": float(np.percentile(lat, 95)), "p99": float(np.percentile(lat, 99)), } print(bench(index, probes, exact_answers))

这段基准脚本的骨架可以直接搬走,但有三个纪律要立好:探针集合与标准答案要版本化固定,否则两次测量不可比;每一轮只变一个因子(索引配置、数据量、并发度),变了两个就说不清归因;报告里必须带环境信息(机型、内存、并发度、数据规模),没写环境的延迟数字没有意义。

公开基准与自己的数据

学术界与各库维护方发布了大量公开基准,它们的用处是了解算法族的相对表现——哪个结构在什么数据画像上占优、量化档位的召回大致损失多少。但把公开基准的绝对数字搬进容量规划是常见错误:维度不同、分布不同、查询模式不同,数字不可迁移。规程应当是:公开基准用来缩小候选范围,入围后立刻在自己的数据上跑同一套基准脚本——探针从真实日志采样、标准答案用精确检索现算、并发画像按业务高峰建模。本章前面所有工作的有效性,最终都由这套自有基准背书。

还有一类常被漏测的指标值得补上:带过滤场景的"凑不满"率(先搜后滤策略下返回条数不足 TopK 的查询占比)与墓碑率、段数量这类健康度指标。它们不是性能指标,却是性能指标变坏的前兆,适合直接进监控面板,第六章的运维清单会给它们留好位置。

问题:探针集合多大才够?

从统计稳定性倒推。召回率估计的标准误与探针数的平方根成反比:要分辨两个配置间零点零一的召回差异,探针量通常要上千条;只做粗粒度趋势判断(零点零三以上的差距),几百条就够。另一个常被忽略的维度是覆盖度——探针要覆盖查询的类型分布(长短查询、冷热领域、带过滤与不带过滤),类型偏斜的探针再多也测不出整体成色。实务配方是:从日志分层采样一到两千条做主集合,另备一组边界案例做冒烟检查,两者都版本化保存。

问题:基准测试要不要模拟并发下的互相干扰?

要,且这是单发测量与真实负载最大的偏差来源。并发查询共享内存带宽、缓存与调度,尾延迟在并发下可能恶化数倍——只测单发会得到过于乐观的 P99。正确做法是双层测量:单发测能力上限,爬坡并发测吞吐拐点与尾延迟形态(5.5 开头的并发爬坡)。报告里两层都给,容量规划用并发层数据,参数调优用单发数据,各取所需。

问题:线上探针和离线基准要分开建吗?

分开,因为它们回答不同的问题。离线基准回答"这个配置的能力上限",允许重负载、破坏性实验与长时间扫描;线上探针回答"此刻的服务在业务分布下的成色",必须轻量、定时、且不能干扰生产。两者的探针可以同源(离线集合的子集线上化),但结果不可互相替代——离线九成九的召回救不了线上墓碑堆积导致的暗降。把离线数字当作能力的体检报告,把线上探针当作日常的手环,两套都要,各司其职。

本节要点回顾

  • 召回率、吞吐、尾延迟三组指标各管对错、容量、体验。
  • 探针必须采样自真实查询,标准答案由精确检索现算并版本化固定。
  • 尾延迟看 P95 与 P99,平均值会掩盖长尾,而体验由长尾决定。
  • 单因子测量、环境入档,公开基准只用于缩小候选范围。
  • 健康度指标(凑不满率、墓碑率、段数量)是性能变坏的前兆,一并进面板。

第五章的工程闭环到此完整。下一章换上决策者的帽子:这些技术能力如何在选型、部署与运维里变成生产系统的口碑。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U