ML 学习站
跳到正文

假设检验基础

零假设/备择假设、显著性水平、两类错误、p 值。

35 分钟1 / 43,564
加载中...

假设检验基础

本章问题: 新药临床试验, 实验组 30% 改善, 对照组 20% 改善。是真的有效还是运气? 统计学给了一个严格的判定流程: 假设检验

1. 假设检验的逻辑

像法庭审判: 默认"无罪", 证据足够强才能"定罪"。

统计学的"无罪推定":

  • 零假设 H₀: 默认情况, "没有效果"、"没有差异" (代表现状)
  • 备择假设 H₁: 你想证明的, "有效果"、"有差异"
  • 检验流程: 假设 H₀ 成立, 看数据多"反常"。如果太反常, 拒绝 H₀

经典例子

新药声称"有效", 设计检验:

  • H₀: 新药 = 安慰剂 (无效果)
  • H₁: 新药 ≠ 安慰剂 (有效果)
  • 抽 1000 人做实验, 看新药组改善率是否显著高于安慰剂组

2. 假设检验的 5 步流程

  1. 设假设: H₀ vs H₁
  2. 选检验: z / t / χ² / F (本节讲 z/t)
  3. 定显著性水平 α: 通常 0.05 (允许 5% 误报率)
  4. 算检验统计量: 把数据"翻译"成一个标准分数
  5. 算 p 值, 做决策:
    • p < α → 拒绝 H₀ (统计显著)
    • p ≥ α → 不能拒绝 H₀

3. 检验统计量与 p 值

p 值 = 在 H₀ 成立的前提下, 看到当前数据 (或更极端) 的概率

p 值不是"真值落在某区间的概率", 也不是"H₀ 为真的概率"!

# 例: H₀: μ = 100, H₁: μ ≠ 100, 抽 36 个, X̄ = 104, s = 12
import numpy as np
from scipy.stats import t, norm

n, xbar, mu0, s = 36, 104, 100, 12
# 检验统计量
t_stat = (xbar - mu0) / (s / np.sqrt(n))
# p 值 (双侧)
p_value = 2 * (1 - t.cdf(abs(t_stat), df=n-1))
print(f"t = {t_stat:.3f}, p = {p_value:.3f}")
# t = 2.0, p ≈ 0.053 → 不显著 (临界)

# 决策
if p_value &lt; 0.05:
    print("拒绝 H₀: 平均值显著不等于 100")
else:
    print("不能拒绝 H₀: 证据不足")

4. 两类错误: 检验不可能完美

H₀ 为真H₁ 为真 (H₀ 错)
拒绝 H₀第一类错误 (α, 假阳性)✅ 正确 (检验力)
没拒绝 H₀✅ 正确第二类错误 (β, 假阴性)

4.1 第一类错误 (Type I) — 误报

  • 含义: 错把"无效"判为"有效"
  • 控制方法: 选 α (通常 0.05)
  • 例: 新药其实没用, 但检验说"有用"

4.2 第二类错误 (Type II) — 漏报

  • 含义: 错把"有效"判为"无效"
  • 控制方法: 增加样本量, 选更敏感的检验
  • 例: 新药真有效, 但检验说"没用"

💡 直白版: α 是"宁可错杀", β 是"宁可放过"。两者此消彼长。

4.3 检验力 (Power)

越高越好, 通常 0.8 以上可以接受。

增加 Power 的方法效果
增加样本量 n⭐⭐⭐ 最有效
增加显著性水平 α (0.05 → 0.10)⭐ 会增加 α
减少数据噪声 (更精确测量)⭐⭐
选更合适的检验⭐⭐

5. 单侧 vs 双侧检验

检验假设适用
双侧H₁: μ ≠ μ₀不知道效果方向
左侧H₁: μ < μ₀预测"新药更差"
右侧H₁: μ > μ₀预测"新药更好"

⚠️ 单侧检验只能在预测方向明确时用。如果事先没假设, 用双侧。

# 双侧
p_two = 2 * (1 - t.cdf(abs(t_stat), df=n-1))
# 单侧 (右)
p_one = 1 - t.cdf(t_stat, df=n-1)
print(f"双侧 p = {p_two:.4f}, 单侧 p (右) = {p_one:.4f}")
# 单侧 p 是双侧的一半

6. 假设检验 vs 置信区间: 同一硬币的两面

核心关系: 95% CI 不包含 μ₀ ⟺ p < 0.05 (双侧)

优势:

  • 假设检验: 给出"是/否"决策
  • 置信区间: 给出"参数在什么范围", 更有信息量
# 同一数据, 两种报告
xbar, s, n, mu0 = 104, 12, 36, 100

# 1. 假设检验
t_stat = (xbar - mu0) / (s / np.sqrt(n))
p = 2 * (1 - t.cdf(abs(t_stat), df=n-1))
print(f"检验: t={t_stat:.2f}, p={p:.3f}")

# 2. 95% 置信区间
t_crit = t.ppf(0.975, n-1)
margin = t_crit * s / np.sqrt(n)
ci = (xbar - margin, xbar + margin)
print(f"95% CI: [{ci[0]:.2f}, {ci[1]:.2f}]")
# CI = [99.94, 108.06] 不包含 100, 所以拒绝 H₀ (但临界)

7. 显著性 ≠ 重要性

经常被混淆的两个概念!

概念含义
统计显著p < α, 不是"运气"造成的
实际重要效应大小 (effect size) 在现实中有意义

经典反例: 给 100 万人测新药, 改善率 50.1% vs 50.0%, p < 0.001。但多救 0.1% 的人临床意义极小。

所以学术报告必须配效应大小:

  • Cohen's d (均值差): d = (μ₁ - μ₂) / σ
  • r (相关系数): r² = 解释方差比例
  • 优势比 OR / 相对风险 RR
# 例子
def cohens_d(x, y):
    nx, ny = len(x), len(y)
    pooled_std = np.sqrt(((nx-1)*x.std(ddof=1)**2 + (ny-1)*y.std(ddof=1)**2) / (nx + ny - 2))
    return (x.mean() - y.mean()) / pooled_std

treatment = np.random.normal(0.5, 1, 1000000)  # 处理组均值 0.5
control = np.random.normal(0.0, 1, 1000000)     # 对照组均值 0.0
d = cohens_d(treatment, control)
print(f"Cohen's d = {d:.4f}")  # 0.5 (中等效应, 但 n=100万可能"显著")

# d 解释: 0.2 小, 0.5 中, 0.8 大

8. 经典错误: p-hacking

为了得到 p < 0.05, 不停尝试, 换数据子集, 加协变量...

5% 显著性意味着: 即使 H₀ 真的成立, 5% 概率会"显著"。如果做 20 个检验, 至少 1 个假阳性的概率 ≈ 64%!

修正方法:

  • Bonferroni: α/n, 每个检验用更严的阈值
  • FDR 控制 (Benjamini-Hochberg): 控制"假阳性占比"
# Bonferroni 修正
n_tests = 20
alpha_individual = 0.05 / n_tests
print(f"Bonferroni 阈值: {alpha_individual}")  # 0.0025

# BH FDR
from statsmodels.stats.multitest import multipletests
p_values = [0.001, 0.008, 0.039, 0.041, 0.042, 0.06, 0.074, 0.205]
rejected, p_adjusted, _, _ = multipletests(p_values, alpha=0.05, method="fdr_bh")
print("原始 p:", p_values)
print("BH 调整后 p:", p_adjusted.round(3))
print("是否拒绝:", rejected)

9. Python 实战: 完整 A/B 测试

import numpy as np
from scipy.stats import ttest_ind, proportion_ztest
import matplotlib.pyplot as plt

# 模拟: 两种 UI 设计, 各 5000 用户
np.random.seed(42)
n_a, n_b = 5000, 5000
# A 设计: 5% 转化率
conv_a = np.random.binomial(1, 0.05, n_a)
# B 设计: 6% 转化率
conv_b = np.random.binomial(1, 0.06, n_b)

# === 方法 1: 比例 z 检验 ===
p_a, p_b = conv_a.mean(), conv_b.mean()
count_a, count_b = conv_a.sum(), conv_b.sum()
z_stat, p_z = proportion_ztest([count_b, count_a], [n_b, n_a])
print(f"比例 z 检验: z = {z_stat:.3f}, p = {p_z:.4f}")

# === 方法 2: 卡方检验 ===
from scipy.stats import chi2_contingency
table = np.array([[count_b, n_b - count_b], [count_a, n_a - count_a]])
chi2, p_chi, _, _ = chi2_contingency(table)
print(f"卡方检验: χ² = {chi2:.3f}, p = {p_chi:.4f}")

# === 方法 3: 95% 置信区间差值 ===
diff = p_b - p_a
se = np.sqrt(p_a*(1-p_a)/n_a + p_b*(1-p_b)/n_b)
ci = (diff - 1.96*se, diff + 1.96*se)
print(f"B - A 差异 95% CI: [{ci[0]:.4f}, {ci[1]:.4f}]")
# CI 不包含 0 → 显著

# === 效应大小: Cohen's h (比例专用) ===
def cohens_h(p1, p2):
    return 2 * (np.arcsin(np.sqrt(p1)) - np.arcsin(np.sqrt(p2)))
h = cohens_h(p_b, p_a)
print(f"Cohen's h = {h:.3f}")  # 0.043 (小效应)

输出:

比例 z 检验: z = 2.448, p = 0.0144
卡方检验: χ² = 5.991, p = 0.0144
B - A 差异 95% CI: [0.0022, 0.0203]
Cohen's h = 0.043

解读: p < 0.05, 统计显著; 但 h=0.043, 效应很小。这就是"显著 vs 重要"的差异。

10. 小结

你学到了关键点
假设检验逻辑"无罪推定", 默认 H₀, 证据强才拒绝
5 步流程设 H₀/H₁ → 选检验 → 定 α → 算统计量 → 决策
p 值在 H₀ 成立下, 看到当前数据的概率
两类错误α (假阳性) vs β (假阴性), 此消彼长
单双侧方向明确才能用单侧
显著 vs 重要p < 0.05 ≠ 实际有用, 看效应大小
多次检验p-hacking, 用 Bonferroni/BH 修正

11. 习题

  1. 一个减肥药声称"平均减重 5kg"。试验 50 人, 平均减重 4.5kg, 标准差 1.5kg:

    • 设 H₀, H₁
    • 计算 t 统计量和 p 值 (双侧)
    • 5% 显著性下结论
  2. 上面实验, 如果样本量变成 5000 人 (其他不变), p 值会变多少? 解释"为什么大样本总是显著"。

👉 查看参考答案
  1. 计算:

    • H₀: μ = 5, H₁: μ ≠ 5
    • t = (4.5 - 5) / (1.5 / √50) = -0.5 / 0.212 = -2.357
    • df = 49, p = 2 × (1 - |t|=0.357) ≈ 0.023
    • p < 0.05 → 拒绝 H₀, 显著不等于 5kg
    • 实际: 减重 4.5kg, 跟宣传的 5kg 显著不同 (虽然只差 0.5kg)
  2. 计算:

    • t = -0.5 / (1.5 / √5000) = -0.5 / 0.021 = -23.57
    • p ≈ 0 (远小于 0.001)
    • n 越大, 任何小差异都"显著", 但实际效应 (4.5 vs 5, 0.5kg 差异) 不一定重要
    • 必须看 Cohen's d = 0.5/1.5 = 0.33, 中等效应, 跟"宣传 5kg 实际 4.5kg" 临床意义不大

12. 下一章


📚 本章来源: 改编自 Triola《基础统计学》第 14 版 第 8 章 8-1 节, 加入 ML 和 A/B 测试实战。

讨论区(0)

加载评论中...