ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

二项分布实战:抽检、A/B实验与异常检测阈值

二项分布实战:抽检、A/B实验与异常检测阈值

1. 为什么二项分布值得反复琢磨

我第一次真正把二项分布用明白,不是在课本上,而是在一个抽检场景里。当时业务方给了一批货,说抽 20 件看看有多少件不合格,问我"抽到 3 件以上不合格就退货,这个规则靠不靠谱"。我一开始凭直觉说"20 件里 3 件不合格,15% 的不良率,有点高吧",结果被反问了一句:"如果这批货真实不良率只有 5%,你抽 20 件出现 3 件以上不合格的概率有多大?"我当场卡住了。

这个问题的答案,就是二项分布要解决的事。二项分布描述的是在 n 次独立重复的伯努利试验中,成功次数所服从的概率分布。一句话拆开有三个硬条件:试验次数 n 是固定的、每次只有成功/失败两种结果、每次成功的概率 p 保持不变且各次之间互不影响。满足这三条,成功次数 X 就服从参数为 (n, p) 的二项分布,记作 X ~ B(n, p)。

它解决的是一类非常具体的问题:在重复做同一件事若干次的前提下,我想知道"成功几次"这件事的概率有多大。抛 10 次硬币正面朝上几次、100 个用户里有多少人点广告、一批零件抽 50 个有几个次品、投篮 20 次命中几个,本质上都是同一个模型。适合谁看?如果你是需要做质量抽检、A/B 实验评估、转化率判断、风控阈值设定的从业者,或者只是被"n 选 k 的概率"绕晕过的学生,这篇内容都能给你一条从头算到尾的清晰路径。

我下面不光讲公式长什么样,更重要的是讲清楚每个参数怎么来的、什么时候这个模型会失效、以及我在实操中踩过的那些坑。公式背下来十分钟就够,但知道它什么时候不能用,往往要花好几年。

2. 把二项分布拆开看:从一次抛硬币到 n 次重复

2.1 伯努利试验:整个模型的原子单位

二项分布的地基是伯努利试验,名字听着唬人,其实简单到不能再简单:一次试验只有两种结果,成功或者失败,成功概率记作 p,失败概率就是 1-p。抛一次硬币、点一次广告、检查一个零件,这些都是伯努利试验。

这里有个新手最容易忽略的点:"成功"不代表好事。在质量抽检里,"成功"往往指的是"抽到次品",因为我们关心的就是次品出现的次数。在医学统计里,"成功"可能指的是"出现不良反应"。所以看到 p 的时候,千万别默认它代表好消息,一定要回到具体场景确认 p 到底是谁的概率。我在早期做过一个错误判断,就是因为把"合格率"当成了 p 代进去算"不合格件数",方向整个反了,结论自然也全错。

另一点是 p 必须固定。一个人投篮,体力下降后命中率会变,那就不满足固定 p 的要求;一台设备连续运行会升温,不良率会漂移,也不满足。伯努利试验强调的是"同一个人、同一状态、同一条件下重复",这个"同一"是模型成立的前提,不是修辞。

2.2 独立性的真实含义:为什么它比 p 固定更容易被破坏

独立性说的是各次试验之间互不影响,前一次的结果不会改变后一次的概率。听起来理所当然,实际场景里反而是它更容易被违反。

举个生活化的例子:从一副牌里有放回地抽,每次抽到红桃的概率都是 1/4,各次独立,可以套二项分布;但如果是无放回地抽,第一张抽走了红桃,剩下的牌里红桃比例就变了,第二次的概率不再是 1/4,这时候要用的是超几何分布,不是二项分布。这个差别在样本量小的时候特别明显:总数 20 张抽 5 张,无放回和有放回算出来的概率能差出好几个百分点;但总数 10000 抽 5 张,差别小到可以直接忽略,工程上就常拿二项分布近似替代。

还有一种隐蔽的不独立:群体效应。比如一个班级里问"是否支持某个方案",学生之间会互相讨论,前一个人的回答影响后一个人,这时候"独立"就破了。或者一条生产线上相邻的零件,共用同一批原料、同一个模具,缺陷往往是成簇出现的,一个出问题后面几个也容易出问题。这类数据用二项分布算出来的概率会偏乐观,因为它低估了波动。

提示:拿到一个问题先问自己三件事——n 是不是事先固定的?每次是不是只有两种结果?各次之间有没有互相影响?三个都是"是",再往下用二项分布。

2.3 组合数从哪来:概率公式的前半截

假设 n=3,p=0.5,问恰好 1 次成功。成功可能发生在第 1 次、第 2 次或第 3 次,这是三种不同的排列。每种排列的概率都是 p × (1-p) × (1-p) = 0.5 × 0.5 × 0.5 = 0.125,三种加起来 0.375。

这里的"三种"就是组合数 C(3,1) = 3。一般地,n 次里恰好 k 次成功,成功位置的选法是 C(n,k) 种,每种选法对应的概率都是 p^k × (1-p)^(n-k)(因为 k 次成功各贡献一个 p,n-k 次失败各贡献一个 1-p,乘法交换律保证顺序不影响结果)。所以:

P(X = k) = C(n,k) × p^k × (1-p)^(n-k)

我建议你把这个公式读成一句话:"先挑位置,再算这一种情况的概率,最后相乘"。C(n,k) 管挑位置,p^k 管成功那几次, (1-p)^(n-k) 管失败那几次。这样记比死背符号有用得多,也不容易把指数搞反。

顺便说一个常见的手算错误:C(n,k) 是对称的,C(10,3) = C(10,7) = 120,很多人在算"至少"类问题时会用错端点,比如算 P(X ≥ 8) 时只算了 P(X=8),忘了 9 和 10。这种错误在实际报告里出现过不止一次,而且因为结果看起来"合理",很难被肉眼发现。

2.4 期望与方差:两个数字看透分布形态

二项分布的期望是E(X) = np,方差是Var(X) = np(1-p)。这两个式子不用推,但要理解它们的含义。

期望 np 是"长期平均会发生多少次成功"。100 次试验、p=0.2,平均成功 20 次,这符合直觉。方差 np(1-p) 描述的是波动大小,它有个很有意思的性质:p=0.5 时方差最大,p 越靠近 0 或 1,方差越小。翻译成人话:当你做一件成败各半的事,结果的波动最剧烈;当你做一件几乎必成或几乎必败的事,结果非常稳定,几乎每次都一样。

这个性质在实操里很关键。同样是 n=100,p=0.5 时标准差是 5,意味着结果常在 45 到 55 之间晃;p=0.01 时标准差约 1,结果基本就在 0 到 2 之间。所以我做监控告警阈值时,一定会先看 np(1-p) 有多大,波动大的指标阈值要给宽,波动小的可以收紧,绝不能所有指标用同一套阈值。这一点我在早期吃过亏,给小概率事件设了对称的上下限,结果天天误报。

3. 手算一遍:5 分钟把公式跑通

3.1 一个完整的手算示例

还是用开头那个抽检场景:抽 20 件,真实不良率 5%,问恰好 3 件不合格的概率。

第一步,确认条件:n=20,p=0.05,k=3,各件之间独立(假设是从大批量里随机抽,无放回的影响可忽略)。

第二步,算组合数 C(20,3) = 20×19×18 ÷ (3×2×1) = 1140。

第三步,算 p^k = 0.05^3 = 0.000125。

第四步,算 (1-p)^(n-k) = 0.95^17。这个数手算麻烦,实际用计算器:0.95^17 ≈ 0.4181。

第五步,相乘:1140 × 0.000125 × 0.4181 ≈ 0.0596。

所以恰好 3 件不合格的概率约 5.96%。回到业务方的问题,"3 件以上不合格就退货",需要算 P(X ≥ 3) = 1 - P(X ≤ 2):

kP(X=k) 计算结果
00.95^200.3585
120 × 0.05 × 0.95^190.3774
2190 × 0.0025 × 0.95^180.1887

P(X ≤ 2) ≈ 0.9246,所以 P(X ≥ 3) ≈ 0.0754,也就是 7.5% 左右。换句话说,即使这批货完全合格(不良率就是 5%),也有约 7.5% 的概率会被这个规则判为退货。这就是第一类错误,业务方看到这个数字后才意识到规则定得偏严。

3.2 用代码替代手算

手算适合理解,生产环境肯定用代码。Python 里 scipy 是标准选择:

from scipy.stats import binom n, p = 20, 0.05 # 恰好 3 件不合格 print(binom.pmf(3, n, p)) # 0.05958... # 3 件及以上 print(binom.sf(2, n, p)) # 0.07548... (sf = 1 - cdf) # 不超过 2 件 print(binom.cdf(2, n, p)) # 0.92451... # 期望与标准差 print(n * p, (n * p * (1 - p)) ** 0.5) # 1.0 0.9747

这里有两个细节值得说。第一,求"至少 k 件"用 sf(k-1) 而不是 1-cdf(k)。cdf(k) 是 P(X≤k),所以要 P(X≥k) 应该用 1-cdf(k-1),或者直接用 sf(k-1)。写成 1-cdf(k) 会漏掉恰好等于 k 的那一项,这是极高频的 bug,我自己写过,也在别人的代码里见过好几次。第二,p 很小时 pmf 的数值会非常小,做报告时记得转成"每万次"之类的量级,不然读者看到 1.2e-08 会直接失去判断力。

如果不想装 scipy,用 math 库手搓也行,逻辑更透明:

from math import comb, pow def binom_pmf(k, n, p): return comb(n, k) * (p ** k) * ((1 - p) ** (n - k)) def binom_cdf(k, n, p): return sum(binom_pmf(i, n, p) for i in range(0, k + 1))

小样本下自己写反而更可控,也方便在不能装第三方库的环境里跑。但要提醒一句,n 大了以后直接用 comb 算组合数会有大整数溢出风险,工程上一般会转到对数空间用 lgamma 算,这个在 n 超过几百时就要考虑了。

3.3 参数怎么定:n 和 p 的确定顺序

实操中很多人卡在"p 从哪来"。我的经验是先定 n,再估 p,不要反过来。

n 是由实验设计决定的:抽检 20 件就是 20,跑 7 天每天 1000 次曝光就是 7000。n 应该是一个你事先就说清楚、事后不能改的数字。这点特别重要,因为事后调整 n 会让整个概率解释失效。我见过一种做法:先看数据,发现第 30 次的时候效果最好,就报"前 30 次的结果",这在统计上是不成立的,因为 n 变成了一个随结果变化的东西。

p 的估计有两条路。有历史数据就用历史频率,比如过去三个月平均不良率 5%,就取 0.05;没有历史数据就做保守假设,取最坏情况或者做敏感性分析,把 p 从 0.01 扫到 0.10 看结论稳不稳。我强烈建议后者,因为单一 p 值算出来的结论往往给人一种虚假的确定感,而实际上 p 本身就有误差。

注意:如果 n 很大、p 很小、np 在 5 到 10 之间,可以用泊松分布近似,计算会简化很多;如果 n 很大而 p 接近 0.5,可以用正态近似。但近似的适用边界要守住,np 小于 5 的时候正态近似误差会很明显,尤其是尾部概率。

4. 实操场景:三个真实感的落地案例

4.1 质量抽检:把验收规则算清楚

抽检是二项分布最经典的应用。假设一批货 5000 件,约定不良率不超过 1% 就算合格,验收方案是抽 50 件,不合格数超过 2 件就退货。我们要评估这个方案的两种风险。

第一种风险叫弃真:货其实是好的(p=0.01),却被判退货。算 P(X ≥ 3 | n=50, p=0.01):

binom.sf(2, 50, 0.01) # 约 0.0138

只有 1.4% 左右,说明误伤概率很低,这个方案对好货比较友好。

第二种风险叫存伪:货其实不行(比如 p=0.05),却被判合格。算 P(X ≤ 2 | n=50, p=0.05):

binom.cdf(2, 50, 0.05) # 约 0.5405

高达 54%,意味着不良率 5% 的货有超过一半的概率能蒙混过关。这个结果一出来,业务方立刻明白 50 件的样本量太小了,后来把方案改成抽 200 件、不合格超过 6 件退货,再算一遍两种风险才达到可接受的水平。

这个案例我想强调的是:抽检方案的设计本质是在两条错误概率之间做权衡,而不是拍脑袋定一个"看起来合理"的数字。样本量越大,两条错误概率可以同时压低,但成本上升。二项分布在这里的作用,是让你在拍板之前就能看到每种方案的代价,而不是等出了事故再复盘。

4.2 转化率判断:A/B 实验里最容易踩的坑

假设某功能改版,对照组 1000 次曝光 100 次转化(10%),实验组 1000 次曝光 120 次转化(12%)。直觉上"提升了 20%",要不要全量上线?

用二项分布的角度看,我们要问的是:如果两组真实转化率一样(都是 11% 左右),出现"一组比另一组多 20 次"这种差异的概率有多大。这里可以用两组独立二项分布之差来判断,也可以在单组内做近似检验。

import numpy as np from scipy.stats import binom, norm n, p0 = 1000, 0.11 # 单组标准差 sd = (n * p0 * (1 - p0)) ** 0.5 # 约 9.9 # 两组差异的标准差 sd_diff = (2 * n * p0 * (1 - p0)) ** 0.5 # 约 14.0 z = (120 - 100) / sd_diff print(z) # 约 1.43

z 大约 1.43,对应的单侧 p 值约 0.076,双侧约 0.15。这个差异在统计上并不显著,仅凭这组数据全量上线是有风险的。很多团队在这里犯错,是因为只看百分比不看绝对量和波动,12% 对 10% 看起来漂亮,但 1000 的样本量下这点差异完全可能是随机波动造成的。

我在实操中的做法是:先把 p 的历史值和 n 摆出来,算出"要检测出多大的提升才需要多少样本量",再去决定实验要跑多久。这个顺序反过来——先跑一周再看数据够不够——就会陷入"数据不够但不想再等"的尴尬,最后往往靠主观判断收场。

4.3 异常检测:给监控指标设合理阈值

线上系统每天有 10000 次请求,历史失败率 0.3%,问每天失败次数在什么范围内算正常。

期望是 10000 × 0.003 = 30 次,标准差是 √(10000 × 0.003 × 0.997) ≈ 5.47。按正态近似的经验法则,95% 的情况下失败次数落在 30 ± 1.96×5.47,也就是大约 19 到 41 之间。

n, p = 10000, 0.003 lo = binom.ppf(0.025, n, p) # 约 20 hi = binom.ppf(0.975, n, p) # 约 41 print(lo, hi)

所以阈值可以设成"单日失败超过 41 次告警",这样正常情况下误报率控制在 2.5% 左右。如果按固定比例设阈值(比如"失败率超过 0.5% 就告警",对应 50 次),那就太松了,真有问题的日子也可能不报;如果按固定次数(比如超过 35 就报),又太紧,天天误报。

这个场景里我想说的是,阈值的合理性来自于对分布的定量理解,而不是"凭经验感觉"。p 和 n 一确定,正常波动范围就确定了,剩下只是选一个你能接受的误报率。而且这里还有个隐含前提:每天的请求互相独立。如果某次故障会导致连续多天失败率偏高,那独立性就破了,这时候应该考虑用更复杂的模型或者直接看趋势,而不是套二项分布。

5. 常见误区与排查实录

5.1 五个高频错误对照表

我在带人和做评审的过程中,反复见到的错误其实就那么几类,整理成表方便对照:

错误表现根本原因正确做法
用 1-cdf(k) 求 P(X≥k)混淆了 cdf 的定义端点用 sf(k-1) 或 1-cdf(k-1)
把"至少一次成功"算成 np把期望当成概率P(X≥1) = 1-(1-p)^n
无放回抽样直接套二项分布忽略了独立性被破坏样本占比小于 5% 时可近似,否则用超几何
p 用了合格率去算不合格数没有确认 p 的语义明确"成功"事件到底是什么
事后改变 n 或筛选数据破坏了 n 的预先固定性n 必须在看数据前确定并记录

第三行的"5% 经验法则"值得展开一句:当抽样比例不到总体 5% 时,无放回带来的概率变化很小,二项分布的误差可以接受;超过这个比例,就该换模型或者加有限总体校正。这个 5% 不是铁律,但在工程上足够用。

5.2 一个我排查了很久的数值问题

有次做小概率事件的概率计算,n=500,p=0.001,要算 P(X ≥ 5)。用 1-cdf(4) 算出来是 0.0002 左右,但换成 sf(4) 却是 0.00018 左右,两者对不上,差了 10%。

原因是浮点数相减的精度损失。cdf(4) 的值接近 0.9998,用 1 去减,有效数字被吃掉了一大截。这种情况下必须用 sf,它是直接从尾部累积计算的,不经过"1 减"这一步。

from scipy.stats import binom n, p = 500, 0.001 print(1 - binom.cdf(4, n, p)) # 0.000200... 精度受损 print(binom.sf(4, n, p)) # 0.000183... 更准确

这个坑的教训是:凡是算尾部概率,一律优先用 sf 而不是 1-cdf。同理,算 P(X ≤ k) 用 cdf 没问题,但算接近 1 的概率时要留意精度。这类问题在报告里不会报错,只会给你一个看起来合理但偏差不小的数字,特别难发现。

5.3 模型失效的三个信号

有几个信号出现时,就该怀疑二项分布不适用了:

信号一,方差明显大于 np(1-p)。这就是统计学里说的过度离散。真实数据里非常常见,比如用户行为有聚集性、缺陷有成簇性。检验方法很简单,拿多组观测算样本方差,跟理论方差比,如果大出一截,说明独立性假设不成立。这时候可以考虑用贝塔二项分布,它允许 p 本身是个随机变量,能吸收掉额外的波动。

信号二,p 随时间漂移。做时间序列图,如果成功率有明显趋势或周期性,那 p 就不是常数。这时候应该分段建模,或者引入时间因素,而不是把整段数据揉成一个 p。

信号三,n 不固定。比如"每天记录当天发生的成功次数",但每天的试验次数本身是随机的,那这其实是泊松过程的场景,不是二项分布。两者的区别在于 n 是否事先固定:n 固定用二项,n 随机用泊松。

提示:判断一个场景该用哪个分布,最简单的问法是"试验次数是我定的,还是自然发生的?"我定的是二项,自然发生的是泊松。

5.4 报告里怎么把概率讲清楚

最后一个坑是沟通。算出来 P(X ≥ 3) = 7.5%,直接丢给业务方,对方很可能没感觉。我的做法是换三种说法各讲一遍:第一种用频率,"每 100 次抽检大约有 7 到 8 次会被误判";第二种用对比,"这比你要求的 5% 误判率还高一点,规则得放宽";第三种用代价,"按每月抽检 20 批算,一年会误退大约 18 批"。

把概率翻译成次数和代价,决策者才能做判断。这一步看着不像技术活,但我认为它比公式推导更影响项目的实际价值。毕竟算得再准,如果没人能拿它做决定,这个分析就等于没做。

6. 我个人在实际操作中的几点体会

用二项分布这些年,我最大的感受是:它的难点从来不在公式,而在条件校验。公式四则运算就能算完,但要确认 n 固定、p 恒定、各次独立这三条,往往需要跟业务方来回问好几轮,甚至要去看数据是怎么采的、抽样是不是真的随机。

我现在的习惯是,动笔算之前先在纸上写三行:n 是多少、p 是什么事件的概率、各次之间有没有关联。这三行写不出来,就先别算。写出来了,后面基本是机械劳动。另外就是前面反复提到的,尾部概率一律用 sf,多组比较先算样本量再跑实验,这两条能避免掉大部分低级但致命的错误。

如果你要往下延伸,贝塔二项分布、泊松分布、超几何分布是三个自然的下一站,它们分别对应"p 不确定"、"n 不固定"、"无放回"这三种二项分布失效的情形。把这几条边界搞明白,比多背十个公式有用得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进