数学重学 - 22 假设检验与AB测试

这是 数学重学路线图 阶段四的子页面

假设检验与AB测试:用数据说话,而不是拍脑袋


直觉入门:假设检验像法庭审判

想象你在法庭上

被告(H₀):默认无罪——“没有变化”、”没有差异”、”没有效果”

检察官(你):要拿出足够强的证据来推翻”无罪”

陪审团标准(α):多强的证据才够?通常 α=0.05(5%的冤枉概率我们能接受)

判决

证据足够强 → 拒绝 H₀(”有罪”)

证据不够 → 不拒绝 H₀(注意:不是”证明无罪”,是”证据不足”)

类比后端开发:

H₀ = “这次代码优化没有提升性能”

你需要用数据证明它确实提升了,而不是看了几次就下结论


假设检验完整步骤

Step 1:提出假设

H₀(原假设 / 零假设):默认状态,”没有效果”

H₁(备择假设):你想证明的事,”有效果”

例子:

H₀:新算法的平均响应时间 = 旧算法

H₁:新算法的平均响应时间 < 旧算法

Step 2:选择显著性水平 α

α = 你愿意接受的”误报率”

常用值:0.05(5%)、0.01(1%)、0.001(0.1%)

安全领域可能用 0.001(宁可放过不能冤枉)

互联网 AB 测试常用 0.05

Step 3:计算检验统计量

把你的数据转换成一个标准化的数字

不同场景用不同统计量(t值、z值、χ²值等)

Step 4:求 p 值

p 值 = 如果 H₀ 为真,观察到当前结果(或更极端结果)的概率

p = 0.03 意味着:”如果真的没效果,只有 3% 的可能看到这么大的差异”

p 值不是 H₀ 为真的概率!(常见误解)

Step 5:做决定

p < α → 拒绝 H₀,结果”统计显著”

p ≥ α → 不拒绝 H₀,证据不足


p 值的深入理解

正确理解

p 值回答的问题是:”假设没有任何真实效果,我看到这种数据的概率有多大?”

p 很小 → “这数据在’没效果’的假设下太不可能了” → 所以可能有效果

常见误解

❌ “p=0.03 说明 H₀ 为真的概率是 3%”

❌ “p=0.06 说明没有效果”(可能只是样本不够大)

❌ “p<0.05 就一定有实际意义”(统计显著 ≠ 实际显著)

效应量 Effect Size

p 值只告诉你”有没有差异”,不告诉你”差异有多大”

10 万用户的 AB 测试,0.01% 的提升也可能 p<0.05

所以还要看效应量:实际差了多少?值得改吗?


两类错误:冤枉好人 vs 放过坏人

Type I 错误(假阳性,α)

H₀ 其实是对的,你却拒绝了它

比喻:冤枉好人、误报火警

后端场景:新版本其实没提升,你却上线了

安全场景:正常用户被标记为攻击者

概率 = α(你自己设的)

Type II 错误(假阴性,β)

H₀ 其实是错的,你却没拒绝它

比喻:放过坏人、没报火警

后端场景:新版本确实更好,但你没检测出来

安全场景:真正的攻击被当成正常流量

概率 = β

统计功效 Power = 1 - β

真的有效果时,你能检测出来的概率

一般要求 Power ≥ 0.8(80%)

提高 Power 的方法:增大样本量、增大 α、效应量本身就大

权衡关系

α 和 β 是跷跷板:降低假阳性 → 增加假阴性

唯一同时降低两者的方法:增大样本量

Python 代码——两类错误可视化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

# 假设检验可视化:H0分布 vs H1分布
mu0, mu1 = 0, 2 # H0均值=0, H1均值=2
sigma = 1
alpha = 0.05

# 临界值
z_crit = stats.norm.ppf(1 - alpha, mu0, sigma)

x = np.linspace(-4, 6, 500)

plt.figure(figsize=(12, 6))

# H0 分布
y0 = stats.norm.pdf(x, mu0, sigma)
plt.plot(x, y0, 'b-', linewidth=2, label='H₀ 分布 (无效果)')

# H1 分布
y1 = stats.norm.pdf(x, mu1, sigma)
plt.plot(x, y1, 'r-', linewidth=2, label='H₁ 分布 (有效果)')

# Type I 错误区域(α)
x_alpha = np.linspace(z_crit, 6, 200)
plt.fill_between(x_alpha, stats.norm.pdf(x_alpha, mu0, sigma),
alpha=0.3, color='blue', label=f'Type I 错误 (α={alpha})')

# Type II 错误区域(β)
x_beta = np.linspace(-4, z_crit, 200)
plt.fill_between(x_beta, stats.norm.pdf(x_beta, mu1, sigma),
alpha=0.3, color='red', label=f'Type II 错误 (β)')

beta = stats.norm.cdf(z_crit, mu1, sigma)
power = 1 - beta

plt.axvline(x=z_crit, color='green', linestyle='--',
label=f'临界值 = {z_crit:.2f}')
plt.title(f'两类错误可视化 | β={beta:.3f}, Power={power:.3f}')
plt.xlabel('检验统计量')
plt.ylabel('概率密度')
plt.legend(loc='upper right')
plt.show()

常用检验方法

t 检验:比较均值

单样本 t 检验:样本均值是否等于某个值

独立双样本 t 检验:两组均值是否相同

配对 t 检验:同一组前后对比

适用条件:数据近似正态,或样本量足够大(CLT)

Python 代码——t 检验实战:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from scipy import stats
import numpy as np

np.random.seed(42)

# 场景:旧版API响应时间 vs 新版API响应时间
old_api = np.random.normal(loc=200, scale=30, size=100) # 旧版:均值200ms
new_api = np.random.normal(loc=185, scale=28, size=100) # 新版:均值185ms

# 独立双样本 t 检验
t_stat, p_value = stats.ttest_ind(old_api, new_api)

print("=== 独立双样本 t 检验 ===")
print(f"旧版均值: {old_api.mean():.1f} ms")
print(f"新版均值: {new_api.mean():.1f} ms")
print(f"差值: {old_api.mean() - new_api.mean():.1f} ms")
print(f"t 统计量: {t_stat:.4f}")
print(f"p 值: {p_value:.6f}")
print(f"结论: {'拒绝H₀,新版显著更快' if p_value < 0.05 else '证据不足'}")

# 单侧检验(只关心新版是否更快)
t_stat_one, p_value_one = stats.ttest_ind(old_api, new_api, alternative='greater')
print(f"\n单侧 p 值: {p_value_one:.6f}")

卡方检验 χ²:分类数据

检验观察频数与期望频数是否有显著差异

适用场景:分类数据、频率表

例子:用户从5个入口进来,实际分布是否和预期一致?

Python 代码——卡方检验实战:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from scipy import stats
import numpy as np

# 场景:检查请求来源分布是否异常
# 正常时期各来源占比:Web 40%, Mobile 30%, API 20%, Other 10%
expected_ratio = np.array([0.40, 0.30, 0.20, 0.10])
total_requests = 1000
expected_counts = expected_ratio * total_requests

# 今天实际观察到的
observed_counts = np.array([350, 280, 250, 120])

# 卡方检验
chi2, p_value = stats.chisquare(observed_counts, expected_counts)

print("=== 卡方拟合优度检验 ===")
sources = ['Web', 'Mobile', 'API', 'Other']
for src, obs, exp in zip(sources, observed_counts, expected_counts):
print(f" {src:8s}: 观察={obs:.0f}, 期望={exp:.0f}, 差异={obs-exp:+.0f}")
print(f"\nχ² = {chi2:.4f}")
print(f"p 值 = {p_value:.6f}")
print(f"结论: {'分布异常!可能有爬虫/攻击' if p_value < 0.05 else '分布正常'}")

Z 检验:大样本比例

两组比例是否有显著差异

样本量大时用 Z 检验(AB 测试最常用)

公式:$Z = \frac{p_1 - p_2}{\sqrt{p(1-p)(\frac{1}{n_1}+\frac{1}{n_2})}}$


AB 测试实战

什么是 AB 测试?

将用户随机分成 A 组和 B 组

A 组看旧版(对照组),B 组看新版(实验组)

比较两组的关键指标(转化率、点击率、响应时间等)

用假设检验判断差异是否显著

完整 AB 测试流程

  1. 明确指标(如转化率)和最小检测效应(如提升2%)

  2. 计算所需样本量

  3. 随机分组,运行实验

  4. 收集数据,做假设检验

  5. 做决策:上线 / 回滚 / 继续实验

样本量计算

需要知道:当前转化率、最小可检测提升、α、Power

样本太少 → 检测不出真实差异(低 Power)

样本太多 → 浪费时间和流量

Python 代码——AB 测试样本量计算器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from scipy import stats
import numpy as np

def ab_test_sample_size(p1, p2, alpha=0.05, power=0.8):
"""
计算AB测试每组所需样本量
p1: 对照组转化率
p2: 实验组期望转化率
alpha: 显著性水平
power: 统计功效
"""
z_alpha = stats.norm.ppf(1 - alpha / 2)
z_beta = stats.norm.ppf(power)

p_bar = (p1 + p2) / 2

n = ((z_alpha * np.sqrt(2 * p_bar * (1 - p_bar)) +
z_beta * np.sqrt(p1*(1-p1) + p2*(1-p2))) / (p2 - p1)) ** 2

return int(np.ceil(n))

# 场景:当前转化率10%,希望检测到提升至12%
p1, p2 = 0.10, 0.12
n = ab_test_sample_size(p1, p2)
print(f"当前转化率: {p1:.0%}")
print(f"目标转化率: {p2:.0%}")
print(f"最小提升: {p2-p1:.0%}")
print(f"每组需要样本量: {n:,}")
print(f"总共需要: {2*n:,}")
print(f"如果日均UV=10000, 需要运行约 {2*n/10000:.0f} 天")

# 不同提升幅度对样本量的影响
print("\n--- 不同提升幅度对样本量的影响 ---")
for lift in [0.01, 0.02, 0.03, 0.05, 0.10]:
p2_test = p1 + lift
n_test = ab_test_sample_size(p1, p2_test)
print(f" 提升{lift:.0%}: 每组需要 {n_test:>8,} 样本")

AB 测试结果分析

Python 代码——完整 AB 测试分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from scipy import stats
import numpy as np
import matplotlib.pyplot as plt

def analyze_ab_test(visitors_a, conversions_a, visitors_b, conversions_b,
alpha=0.05):
"""完整的AB测试分析"""
p_a = conversions_a / visitors_a
p_b = conversions_b / visitors_b

# 合并比例
p_pool = (conversions_a + conversions_b) / (visitors_a + visitors_b)

# Z 检验
se = np.sqrt(p_pool * (1 - p_pool) * (1/visitors_a + 1/visitors_b))
z_stat = (p_b - p_a) / se
p_value = 2 * (1 - stats.norm.cdf(abs(z_stat)))

# 置信区间
se_diff = np.sqrt(p_a*(1-p_a)/visitors_a + p_b*(1-p_b)/visitors_b)
z_crit = stats.norm.ppf(1 - alpha/2)
ci_low = (p_b - p_a) - z_crit * se_diff
ci_high = (p_b - p_a) + z_crit * se_diff

# 相对提升
lift = (p_b - p_a) / p_a

print("=" * 50)
print(" AB 测试分析报告")
print("=" * 50)
print(f" 对照组 A: {conversions_a}/{visitors_a} = {p_a:.4%}")
print(f" 实验组 B: {conversions_b}/{visitors_b} = {p_b:.4%}")
print(f" 绝对提升: {p_b - p_a:+.4%}")
print(f" 相对提升: {lift:+.2%}")
print(f" Z 统计量: {z_stat:.4f}")
print(f" p 值: {p_value:.6f}")
print(f" 95% CI: [{ci_low:+.4%}, {ci_high:+.4%}]")
print("-" * 50)
if p_value < alpha:
print(f" 结论: 统计显著 (p={p_value:.4f} < α={alpha})")
if p_b > p_a:
print(" 建议: 上线新版本 B")
else:
print(" 建议: 保留旧版本 A")
else:
print(f" 结论: 不显著 (p={p_value:.4f} >= α={alpha})")
print(" 建议: 继续收集数据或放弃此改动")

return p_a, p_b, p_value, ci_low, ci_high

# 模拟数据
np.random.seed(42)
results = analyze_ab_test(
visitors_a=5000, conversions_a=500,
visitors_b=5000, conversions_b=560
)

置信区间可视化

Python 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats

# 多次AB测试结果的置信区间可视化
experiments = [
("按钮颜色", 0.012, 0.008),
("标题文案", 0.005, 0.007),
("布局调整", -0.003, 0.006),
("价格展示", 0.018, 0.009),
("推荐算法", 0.008, 0.010),
]

fig, ax = plt.subplots(figsize=(10, 6))

for i, (name, effect, se) in enumerate(experiments):
ci_low = effect - 1.96 * se
ci_high = effect + 1.96 * se
color = 'green' if ci_low > 0 else ('red' if ci_high < 0 else 'gray')
ax.errorbar(effect, i, xerr=1.96*se, fmt='o', color=color,
capsize=5, markersize=8, linewidth=2)
ax.text(effect + 1.96*se + 0.002, i, f'{effect:+.1%}', va='center')

ax.axvline(x=0, color='black', linestyle='--', alpha=0.5)
ax.set_yticks(range(len(experiments)))
ax.set_yticklabels([e[0] for e in experiments])
ax.set_xlabel('转化率差异 (B - A)')
ax.set_title('AB测试结果汇总\n绿色=显著正向 | 灰色=不显著 | 红色=显著负向')
plt.tight_layout()
plt.show()

应用场景

安全方向

异常行为检测

H₀:该用户的登录频率正常

H₁:该用户的登录频率异常偏高

用 t 检验或 Z 检验比较个体 vs 群体均值

入侵检测

流量特征分布是否显著偏离历史基线

卡方检验:请求类型分布是否异常

安全策略评估

新的 WAF 规则是否显著降低了攻击成功率

代码示例——安全异常检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np
from scipy import stats

np.random.seed(42)

# 正常用户每天登录次数的历史数据
normal_logins = np.random.poisson(lam=3, size=1000) # 平均3次/天

# 某可疑用户最近7天的登录次数
suspect_logins = np.array([12, 15, 8, 14, 11, 13, 16])

# 方法1:Z检验
mu_normal = normal_logins.mean()
sigma_normal = normal_logins.std()
z_score = (suspect_logins.mean() - mu_normal) / (sigma_normal / np.sqrt(len(suspect_logins)))
p_value = 2 * (1 - stats.norm.cdf(abs(z_score)))

print("=== 可疑用户登录行为检测 ===")
print(f"正常用户平均登录: {mu_normal:.1f} 次/天")
print(f"可疑用户平均登录: {suspect_logins.mean():.1f} 次/天")
print(f"Z-score: {z_score:.2f}")
print(f"p-value: {p_value:.10f}")
print(f"结论: {'异常!建议进一步调查' if p_value < 0.01 else '正常范围'}")

大数据方向

AB 测试平台设计

流量分桶、指标计算、统计检验自动化

多指标修正(Bonferroni、FDR)

多臂老虎机 MAB

AB 测试的进化版:边测试边优化

Epsilon-Greedy:大部分流量给当前最优,小部分探索

Thompson Sampling:基于贝叶斯后验概率分配流量

优势:更快收敛、减少”损失”

MAB 简单实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import numpy as np

class EpsilonGreedy:
"""Epsilon-Greedy 多臂老虎机"""
def __init__(self, n_arms, epsilon=0.1):
self.n_arms = n_arms
self.epsilon = epsilon
self.counts = np.zeros(n_arms)
self.values = np.zeros(n_arms)

def select_arm(self):
if np.random.random() < self.epsilon:
return np.random.randint(self.n_arms) # 探索
return np.argmax(self.values) # 利用

def update(self, arm, reward):
self.counts[arm] += 1
n = self.counts[arm]
self.values[arm] += (reward - self.values[arm]) / n

# 模拟:3个版本的按钮,真实转化率分别为 5%, 8%, 6%
true_rates = [0.05, 0.08, 0.06]
mab = EpsilonGreedy(n_arms=3, epsilon=0.1)

total_reward = 0
for t in range(10000):
arm = mab.select_arm()
reward = 1 if np.random.random() < true_rates[arm] else 0
mab.update(arm, reward)
total_reward += reward

print("MAB 结果:")
for i in range(3):
print(f" 版本{i}: 选中{mab.counts[i]:.0f}次, "
f"估计转化率={mab.values[i]:.4f}, 真实={true_rates[i]}")
print(f"总转化: {total_reward} / 10000 = {total_reward/10000:.4f}")

后端方向

性能优化验证

优化前后响应时间用 t 检验比较

注意要控制变量(同一时段、同一负载)

灰度发布评估

新版本的错误率是否显著高于旧版本

用二项比例 Z 检验

容量测试

某配置下的吞吐量是否达到预期

代码示例——灰度发布评估:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from scipy import stats
import numpy as np

def gray_release_check(requests_old, errors_old, requests_new, errors_new,
threshold=0.05):
"""灰度发布:新版本错误率是否显著升高"""
p_old = errors_old / requests_old
p_new = errors_new / requests_new

# 单侧检验:H1是新版本错误率更高
p_pool = (errors_old + errors_new) / (requests_old + requests_new)
se = np.sqrt(p_pool * (1-p_pool) * (1/requests_old + 1/requests_new))
z = (p_new - p_old) / se
p_value = 1 - stats.norm.cdf(z) # 单侧

print("=== 灰度发布错误率检查 ===")
print(f"旧版错误率: {p_old:.4%} ({errors_old}/{requests_old})")
print(f"新版错误率: {p_new:.4%} ({errors_new}/{requests_new})")
print(f"Z = {z:.4f}, p = {p_value:.6f}")

if p_value < threshold:
print("⚠️ 新版本错误率显著升高,建议回滚!")
return False
else:
print("新版本错误率正常,可以继续扩量")
return True

# 测试
gray_release_check(
requests_old=50000, errors_old=150,
requests_new=10000, errors_new=45
)

多重检验问题

同时做很多检验时,假阳性会膨胀

做 20 个检验(α=0.05),期望有 1 个假阳性

解决方法:

Bonferroni 校正:α’ = α/m(m=检验个数),简单但保守

**FDR (False Discovery Rate)**:控制假阳性比例而非概率,更宽松

Python 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from scipy import stats
import numpy as np

# 模拟:同时测试20个指标
np.random.seed(42)
n_tests = 20
# 其中只有3个指标真的有差异
p_values = []
for i in range(n_tests):
if i < 3:
# 真实差异
a = np.random.normal(0, 1, 100)
b = np.random.normal(0.5, 1, 100)
else:
# 无差异
a = np.random.normal(0, 1, 100)
b = np.random.normal(0, 1, 100)
_, p = stats.ttest_ind(a, b)
p_values.append(p)

p_values = np.array(p_values)
alpha = 0.05

# 不做校正
sig_raw = p_values < alpha
print(f"不校正: {sig_raw.sum()}个显著 (真有差异的前3个: {sig_raw[:3]})")

# Bonferroni 校正
sig_bonf = p_values < (alpha / n_tests)
print(f"Bonferroni: {sig_bonf.sum()}个显著 (前3个: {sig_bonf[:3]})")

# BH (Benjamini-Hochberg) FDR 校正
from scipy.stats import false_discovery_control
# 手动实现BH
sorted_idx = np.argsort(p_values)
sorted_p = p_values[sorted_idx]
bh_threshold = np.arange(1, n_tests+1) / n_tests * alpha
sig_bh_mask = sorted_p <= bh_threshold
# 找最大的满足条件的rank
if sig_bh_mask.any():
max_rank = np.max(np.where(sig_bh_mask)[0])
sig_bh = np.zeros(n_tests, dtype=bool)
sig_bh[sorted_idx[:max_rank+1]] = True
else:
sig_bh = np.zeros(n_tests, dtype=bool)
print(f"BH FDR: {sig_bh.sum()}个显著 (前3个: {sig_bh[:3]})")

练习题

题目1:基础假设检验

旧版首页平均加载时间 3.2 秒,优化后抽样 50 次,均值 2.9 秒,标准差 0.8 秒

(a) 写出 H₀ 和 H₁

(b) 做单样本 t 检验,α=0.05,是否显著?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from scipy import stats
import numpy as np

# H₀: μ = 3.2 (没有优化效果)
# H₁: μ < 3.2 (优化有效)

mu_0 = 3.2
sample_mean = 2.9
sample_std = 0.8
n = 50

t_stat = (sample_mean - mu_0) / (sample_std / np.sqrt(n))
p_value = stats.t.cdf(t_stat, df=n-1) # 单侧

print(f"t = {t_stat:.4f}")
print(f"p = {p_value:.6f}")
print(f"结论: {'显著,优化有效' if p_value < 0.05 else '不显著'}")

题目2:AB 测试

A 组 3000 人,转化 240 人;B 组 3000 人,转化 285 人

(a) 两组转化率分别是多少?

(b) 差异是否统计显著?(α=0.05)

(c) 如果要检测 1% 的绝对提升,需要每组多少样本?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from scipy import stats
import numpy as np

# (a)
p_a = 240 / 3000
p_b = 285 / 3000
print(f"(a) A组: {p_a:.2%}, B组: {p_b:.2%}, 差异: {p_b-p_a:+.2%}")

# (b) Z检验
p_pool = (240 + 285) / 6000
se = np.sqrt(p_pool*(1-p_pool)*(1/3000 + 1/3000))
z = (p_b - p_a) / se
p_val = 2 * (1 - stats.norm.cdf(abs(z)))
print(f"(b) Z={z:.4f}, p={p_val:.4f}")
print(f" {'显著' if p_val < 0.05 else '不显著'}")

# (c) 样本量(复用前面的函数逻辑)
z_alpha = stats.norm.ppf(0.975)
z_beta = stats.norm.ppf(0.8)
p1, p2 = 0.08, 0.09
p_bar = (p1+p2)/2
n_needed = ((z_alpha*np.sqrt(2*p_bar*(1-p_bar)) +
z_beta*np.sqrt(p1*(1-p1)+p2*(1-p2))) / (p2-p1))**2
print(f"(c) 检测1%提升,每组需要: {int(np.ceil(n_needed)):,}")

题目3:卡方检验

某 API 的错误码分布预期为:400(50%), 403(20%), 404(20%), 500(10%)

今天观察到 200 个错误:400:80, 403:35, 404:45, 500:40

这个分布正常吗?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
from scipy import stats
import numpy as np

observed = np.array([80, 35, 45, 40])
expected = np.array([0.50, 0.20, 0.20, 0.10]) * 200

chi2, p = stats.chisquare(observed, expected)
print(f"观察: {observed}")
print(f"期望: {expected}")
print(f"χ²={chi2:.4f}, p={p:.6f}")
print(f"500错误占比: {40/200:.0%} vs 预期 10% → 异常偏高!")
print(f"结论: {'分布异常' if p < 0.05 else '正常'}")

题目4:错误类型辨析

判断以下哪个是 Type I 错误,哪个是 Type II 错误:

(a) 新版本其实没有更快,但 AB 测试说显著更快,于是上线了

(b) 某用户确实在暴力破解,但安全系统没有告警

(c) WAF 把正常爬虫识别为攻击并封禁

(d) 代码有内存泄漏,但性能测试没检测出来

参考答案:

(a) Type I(假阳性)——没效果说有效果

(b) Type II(假阴性)——有攻击没检出

(c) Type I(假阳性)——正常被判为异常

(d) Type II(假阴性)——有问题没检出


本节小结

假设检验的核心:用概率量化”这个差异是不是碰巧的”

p 值是在 H₀ 为真时看到当前数据的概率,不是 H₀ 为真的概率

AB 测试 = 假设检验在产品迭代中的实战应用

关键公式记忆:样本量 ∝ 1/(效应量)²——想检测更小的差异就需要更多数据

下一节 23-回归与预测 将从”比较差异”进阶到”预测趋势”


上一章 目录 下一章
21-概率分布 数学重学路线图 23-回归与预测