你的模型输出 0.87。有人把它放在仪表板上,标记为“成功概率 87%”。客户据此做出决定。
在那个数字真正有意义之前,你实际上需要多少个已解决的结果?
有一个闭合形式的答案,它比我们所有人都要古老,但大多数团队从未使用过它。
那个数字
要以 95% 置信度将比例引用在 ±ε 以内:
n ≥ p̂(1 − p̂) · (z / ε)²
进入全屏模式 退出全屏模式
z = 1.96 表示 95%。这就是全部。运行它:
from math import ceil
def min_n(p_hat: float, eps: float, z: float = 1.96) -> int:
"""引用 p_hat 需在 ±eps 范围内所需的已解决结果。"""
return ceil(p_hat * (1 - p_hat) * (z / eps) ** 2)
for p in (0.5, 0.8, 0.9):
print(p, min_n(p, 0.05))
# 0.5 385
# 0.8 246
# 0.9 139
进入全屏模式 退出全屏模式
要说“50%,误差 ±5 个百分点”,需要 385 个已解决的结果。不是训练集中的 385 行,而是你真正知道实际发生了什么的 385 个案例。
令人痛苦的部分
精度是二次方的。将 ε 收紧 5 倍需要 25 倍的数据:
for eps in (0.10, 0.05, 0.02, 0.01):
print(eps, min_n(0.5, eps))
# 0.1 97
# 0.05 385
# 0.02 2401
# 0.01 9604
进入全屏模式 退出全屏模式
从“大约一半”到“50% ± 1 个百分点”大约需要 9,500 个结果。如果你所在的领域需要 90 天才能得到结果,那不是一个冲刺周期,而是数年的时间。
反向解读
更有用的方向是反向计算。你不是问需要多少数据,而是问当前数据允许你说什么:
def widest_claim(p_hat: float, n: int, z: float = 1.96) -> float:
"""你的证据实际支持的 ± 区间。"""
return z * (p_hat * (1 - p_hat) / n) ** 0.5
print(round(widest_claim(0.5, 50), 3)) # 0.139
进入全屏模式 退出全屏模式
在 n=50 时,你能诚实声称的区间是 ±14 个百分点。所以“50%”实际上是“介于 36% 到 64% 之间”。这不是一个你应该把美元金额印在旁边的概率。
这重新定义了整个问题。你没有精度问题,你有一个措辞问题:在 n=50 时,你只能说区间,而不能说百分比。说出那个区间。
Wilson 区间,而非朴素区间
一个修正:上面的公式是正态近似,在小 n 或极端 p̂ 时会严重退化。它会毫不犹豫地给你一个超出 1.0 的区间。请改用 Wilson 区间:
def wilson(successes: int, n: int, z: float = 1.96) -> tuple[float, float]:
if n == 0:
return (0.0, 1.0)
p = successes / n
d = 1 + z**2 / n
centre = (p + z**2 / (2 * n)) / d
half = z * ((p * (1 - p) / n + z**2 / (4 * n**2)) ** 0.5) / d
return (centre - half, centre + half)
print(wilson(9, 10)) # (0.5958, 0.9821)
进入全屏模式 退出全屏模式
十次成功中有九次成功并不是“90% 可靠”。它是“介于 60% 到 98% 之间”,这句话放在客户面前的含义完全不同。
让它成为约束,而非指导
在生产环境中重要的部分是:没有人强制执行的规则,在截止日期的压力下就会失效。文档无法拯救你。把守门机制放在代码路径中。
MIN_N = 385
def render_score(p_hat: float, n_resolved: int) -> str:
if n_resolved < MIN_N:
lo, hi = wilson(round(p_hat * n_resolved), n_resolved)
return f"band: {lo:.0%}–{hi:.0%} (n={n_resolved})"
return f"{p_hat:.0%} (n={n_resolved})"
进入全屏模式 退出全屏模式
现在,诚实的版本成为默认路径,而过度声明需要有人在经过审查的 PR 中删除代码。这与一篇 wiki 页面上写着“使用概率时要小心”有着非常不同的组织事实。
一个陷阱:哪个 n?
n 是已解决的结果,而解决过程正是偏差潜入的地方。
如果你的成功是自动从下游数据解析的,而你的失败需要人工标记,那么你的已解决集合就不是现实的随机样本。它会偏向成功,而你基于它计算的每一个区间都会自信地出错。非随机缺失的数据不会自我宣告——它只会让你的校准看起来很好。
在相信这个数字之前,请检查产生结果的机制是否因结果类型而异。这个检查比任何模型诊断都帮我发现了更多错误的仪表板。
为什么我在意这个
我构建计费软件。当我们的系统说某项索赔很可能被收回时,有人会根据这个数字安排人手,诊所会做出工资决策。一个自信错误的百分比的代价是某人的一个月收入。
所以我们在数量达到要求之前发布区间,并且产品在架构上禁止在达到要求之前说“概率”。这让我们牺牲了看起来更好的演示。但它从未失去过一个客户。
如果你想了解更长的论证——一个系统在结构上被禁止过度声明意味着什么,以及做出声明的实体绝不能是判断其是否正确的那一方——我在这里写了下来:The Uncapturable Judge。
你的团队在什么时候允许一个分数称自己为概率?
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.