概率统计:描述统计与数据质量

本节目标

统计推断不能修复错误的表。计算均值或置信区间前,先确认一行代表什么、分母是谁、哪些值缺失、哪些记录重复,以及数据是否按时间或任务成组。本节建立一份可重复的 Agent 评测数据体检流程。

先定义分析单位

分析单位(unit of analysis) 是一条可被视为独立观察的基本对象。它可能是:

  • 一个唯一任务;
  • 同一任务的一次模型生成;
  • 一个用户会话;
  • 一次工具调用;
  • 一天的服务聚合值。

这些单位不能随意混用。若每个任务生成 20 次,把 20 次输出全部当作 20 个独立任务,会把任务内相关性误当成更多信息。至少保留以下标识:

字段作用
task_id标识任务,支持 A/B 配对。
run_id / sample_id区分同一任务的重复生成。
model_version说明处理版本。
task_type用于预先定义的分层。
score明确量表、方向和取值范围。
latency_ms、cost保留质量以外的工程指标。
timestamp检查时间漂移、故障窗口和停止规则。
grader_id人工或模型评分者存在时识别评分者结构。

在数据字典里写清每个字段的单位、允许值、缺失含义和生成方式。“0 分”与“没有评分”不是一回事。

分母比百分比更重要

成功率为:

报告 80% 之前要说明:

  • 分子是什么事件;
  • 分母包含或排除了哪些任务;
  • 失败、超时、拒答和解析错误如何计分;
  • 是否有任务因系统错误被删除;
  • 每个任务权重是否相同。

例如 100 个任务中 70 个成功、10 个超时、20 个失败。若把超时从分母移除,会得到 70/90≈77.8%;若超时按失败计入,则是 70/100=70%。两者回答的是不同问题,不能只报告较好看的数字。

位置:均值、中位数与分位数

  • 均值(mean):所有值之和除以数量,适合可加总的目标,但容易受长尾和离群值影响。
  • 中位数(median):排序后居中的值,对极端值较稳健。
  • 分位数(quantile):把排序分布切成给定比例。P95 延迟表示约 95% 的观测不超过该值,不是“最慢 5% 的平均值”。

对 [100, 110, 120, 130, 2000],均值被最后一个值显著拉高,中位数仍是 120。二者都不是错误答案;它们描述分布的不同方面。延迟报告通常同时给样本量、中位数、P95/P99 和超时率。

分位数算法不唯一

小样本下,不同插值定义会给出不同数值。必须记录工具、方法和样本量;不要用很少观测得到的 P99 支撑精确 SLA。Python statistics.quantiles 的 exclusive 与 inclusive 方法也回答不同建模假设。

离散程度:范围、IQR、标准差

  • 范围(range):最大值减最小值,极易受单个极端值影响。
  • 四分位距(IQR):,描述中间 50% 数据的跨度。
  • 标准差(standard deviation):先求观测相对均值的平方偏离平均值,再开平方;因此与原指标单位相同。

均值相同不代表体验相同。两个 Agent 都可能有 80% 平均成功率,但一个在所有任务类型上稳定,另一个在关键工具任务上几乎总失败。离散程度也不能替代分层;先看分布和任务结构,再解释一个汇总数。

缺失、NaN、重复与无效值

检查顺序:

  1. 总行数与唯一 task_id 数;
  2. A/B 是否拥有完全相同的任务集合;
  3. 每列缺失数量及缺失原因;
  4. 数值是否在契约范围,单位是否一致;
  5. 同一任务的重复行是否真实重复生成,还是误复制;
  6. NaN、无穷大和解析失败是否被静默带入统计;
  7. 排除规则是否在看结果前固定。

Python 官方文档指出,NaN 的比较语义会让需要排序或计数的 median、mode、quantiles 等函数产生意外结果。不要把 NaN 机械替换为 0;先追查它表示“缺测”“未适用”还是计算失败,再按预先规则处理。

分层、时间与 Simpson 悖论

总体平均会受样本构成影响。下面两组中,B 在每个难度层都优于 A:

难度AB
容易90/100 = 90%19/20 = 95%
困难1/10 = 10%40/100 = 40%

但总体上 A 为 91/110≈82.7%,B 为 59/120≈49.2%,方向反转,因为 A 评了更多容易任务、B 评了更多困难任务。这类由构成差异造成的反转常称为 Simpson 悖论。正确做法不是永远拒绝总体指标,而是使用同一任务集做配对、预先定义关键分层,并同时报告分层样本量。

时间也会形成结构:同一故障窗口内的请求相关,连续多次查看指标后选择最好时点会引入选择偏差。保留时间戳并画时间序列,而不是把所有行无条件打乱。

可运行的最小体检

from math import isfinite
from statistics import mean, median, quantiles, stdev
 
latencies = [120.0, 125.0, 130.0, 140.0, 900.0]
scores = [1, 1, 0, 1, 0]
 
if not latencies or not all(isfinite(value) and value >= 0 for value in latencies):
    raise ValueError("latency must be finite and non-negative")
if len(scores) != len(latencies) or any(score not in (0, 1) for score in scores):
    raise ValueError("scores and latencies must align with the binary contract")
 
p95 = quantiles(latencies, n=100, method="inclusive")[94]
summary = {
    "n": len(scores),
    "success_rate": mean(scores),
    "latency_mean": mean(latencies),
    "latency_median": median(latencies),
    "latency_sample_sd": stdev(latencies),
    "latency_p95_inclusive": p95,
}
 
assert summary["n"] == 5
assert summary["success_rate"] == 0.6
assert summary["latency_median"] == 130.0

这段代码只验证一个小列表的契约,并没有证明样本代表线上总体。真实分析还要检查唯一 ID、缺失原因、配对完整性、分层与时间结构。

数据体检交付模板

目标总体:
分析单位:
时间窗口:
数据来源与版本:
总行数 / 唯一任务数:
分子 / 分母定义:
缺失、超时、解析失败处理:
重复与多次生成结构:
A/B 配对完整性:
主要指标及方向:
预定义分层:
已知选择偏差与未覆盖场景:

练习与自测

  1. 对 [100, 110, 120, 130, 2000] 手算均值与中位数,说明各自适合回答什么问题。
  2. 设计一个含 task_id、run_id、版本、分层、得分、延迟和评分者的最小表头。
  3. 分别用“超时计失败”和“排除超时”计算同一份数据,写出两个指标的不同含义。
  4. 解释为什么把同一任务复制 20 次不会产生 20 倍独立信息。
  5. Simpson 悖论示例中,怎样修改评测设计才能公平比较 A/B?
  • 我先定义分析单位和目标总体,再计算统计量。
  • 我报告分子、分母、样本量、缺失与排除规则。
  • 我能根据分布选择均值、中位数、分位数和离散度。
  • 我会检查配对、重复、分层和时间结构。

下一步:概率、条件概率与贝叶斯。

参考资料

核验日期:2026-07-14。