ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

费舍尔统计遗产:从最大似然估计到实验设计的现代应用

费舍尔统计遗产:从最大似然估计到实验设计的现代应用 这次我们聊的不是一个新模型而是现代统计学的奠基人之一罗纳德・费舍尔。今天机器学习训练用的交叉熵损失本质上就是最大似然估计的负对数形式AB 实验里的显著性检验和实验分组最早也能追溯到费舍尔在农业试验站设计的那套随机化框架生物信息学里做群体遗传分析时等位基因频率的随机波动模型同样绕不开他的名字。这篇文章会先把费舍尔的核心贡献整理成一张速览表然后用具体代码分别演示最大似然估计、方差分析、Fisher 精确检验和遗传漂变模拟最后聊一聊这些方法在当下数据分析工作流里的适用边界以及最常见的使用误区。如果你是一个数据分析师、算法工程师、生物信息学方向的学生或者正在做 AB 实验但总觉得“p 值”用得不踏实这篇文章值得看完。即使你已经有统计基础从“费舍尔为什么这样设计实验”的角度重新过一遍这些方法也能帮你把很多“默认在用的东西”重新理解清楚。本文不涉及任何环境部署所有代码只需要 Python 和几个常见科学计算包就可以运行。1. 费舍尔核心贡献速览费舍尔一生横跨统计学、遗传学和进化生物学很难用一个身份去定义他。对今天的技术读者来说最实用的入口是他创造了我们今天仍然高频使用的一整套统计工具。下面这张表先把核心内容对齐一下。维度说明人物罗纳德・艾尔默・费舍尔Ronald Aylmer Fisher1890-1962核心领域统计学、实验设计、进化生物学、群体遗传学统计贡献最大似然估计、方差分析、F 检验、实验设计、Fisher 精确检验、充分统计量、Fisher 信息、线性判别分析遗传贡献用数学模型统一孟德尔遗传学与达尔文自然选择理论参与奠定群体遗传学基础经典案例女士品茶实验开创随机化实验设计现代映射机器学习损失函数、AB 实验、临床试验设计、GWAS 群体遗传分析、判别模型适合阅读人群数据分析师、算法工程师、生物信息学从业者、实验设计相关岗位需要说明一点群体遗传学并不是费舍尔一个人完成的。他和霍尔丹、赖特三人共同奠定了这个学科的数学基础只是费舍尔的工作更早、更系统影响力也最深远。说“一个人开创”是标题的简化表达真正理解时还是要看到多位科学家的共同贡献。2. 为什么今天还要读费舍尔很多算法工程师刚接触统计时会觉得费舍尔是教科书中的人物离日常工作很远。但实际拆开现代机器学习框架会发现大量基础组件都来自他的贡献。先看最大似然估计。BERT、GPT 这类模型的训练目标本质是最大化训练数据在参数下的似然。为了计算方便通常取负对数并最小化也就是交叉熵损失。费舍尔在 1922 年发表《论理论统计学的数学基础》时把最大似然估计的框架、一致性和有效性讲清楚了这套框架至今仍是参数估计的第一性原理。再看实验设计。互联网公司的 AB 实验并不是产品经理发明的它沿用的是费舍尔在 20 世纪 20 年代提出的随机化、重复、区组和析因设计思想。第三是方差分析。多组实验数据之间到底有没有差异直接跑两两 t 检验会产生多重比较问题而 ANOVA 把总变异拆成组间变异和组内变异用 F 检验一次性回答“组间是否存在差异”。但也要说清楚边界。费舍尔时代没有现代计算机他的很多方法是为小样本、农业和生物学实验设计的。直接照搬到海量 A/B 实验和因果推断场景会产生问题比如 p 值误用、多重比较修正缺失、忽略混淆变量等。所以读费舍尔不是“考古”而是理解方法为什么被设计成这样再决定什么时候能沿用什么时候要扩展。3. 从“女士品茶”看实验设计与 Fisher 精确检验3.1 实验背后的统计逻辑“女士品茶”是费舍尔实验设计思想最出名的案例。故事大致是这样一位女士声称自己可以分辨出奶茶是先倒茶还是先倒奶。费舍尔没有直接说“那试试看”而是设计了一个随机化实验准备 8 杯茶其中 4 杯先倒奶再倒茶另外 4 杯先倒茶再倒奶然后把杯子随机递给女士让她判断每一杯属于哪种类型。这个设计的价值不是“测试超能力”而是用随机化消除了潜在偏差如果杯子不是随机排列女士可能根据杯壁残留、颜色等外部线索判断而不是真的靠味觉。同时实验通过设计好的“4 杯 vs 4 杯”结构让纯靠运气猜中的概率变得可计算。这就是实验设计三原则的雏形随机化、重复、对照。3.2 Fisher 精确检验的 Python 实现女士要正确判断全部 8 杯等价于从 8 个杯子中准确挑出那 4 杯“先奶后茶”。在零假设下她只是随机猜测那么全对的概率是[ P \frac{\binom{4}{4} \binom{4}{4}}{\binom{8}{4}} \frac{1}{70} \approx 0.0143 ]用scipy.stats.fisher_exact可以立刻验证这个概率from scipy.stats import fisher_exact # 2x2 列联表 # 行真实类型列判断类型 # 左上角真实先奶后茶判断为先奶后茶 table [[4, 0], [0, 4]] odds_ratio, p_value fisher_exact(table, alternativegreater) print(odds ratio:, odds_ratio) print(p-value:, p_value)运行结果里p 值约 0.0143。这个概率的含义是如果女士只是随机猜测那么“8 杯全部正确”这样极端的结果出现概率不到 2%。费舍尔用这个小概率作为拒绝零假设的依据。今天做 AB 实验算显著性时逻辑仍然一致先建立“没有差异”的零假设然后看当前数据有多极端极端程度用 p 值衡量。3.3 这个故事给我们的启发现代 AB 实验里经常犯的错误是做完实验才想到要随机分组或者组间样本量差距很大。费舍尔的设计思路告诉我们实验设计必须发生在数据收集之前。随机化解决的是“不可观测的混杂因素”重复解决的是“样本量不足以估计噪声”对照解决的是“没有基准就无法解释效果”。这个顺序不能反。4. 最大似然估计从概率模型到机器学习训练4.1 最大似然的核心思想最大似然估计Maximum Likelihood EstimationMLE要解决这样的问题给定一组观测数据应该选择哪个参数使得在这组参数下观测到当前数据的概率最大。假设数据独立同分布似然函数写成[ L(\theta) \prod_{i1}^{n} P(x_i \mid \theta) ]由于连乘容易下溢实际更常用对数似然[ \ell(\theta) \sum_{i1}^{n} \log P(x_i \mid \theta) ]对机器学习工程师来说重点在于最小化交叉熵损失其实就是最大化对数似然。模型在训练时不断调整参数让“当前这批样本出现的概率”越来越高。这就是为什么我们常说“训练就是拟合数据的分布”。4.2 用 Python 实现一次完整的 MLE下面用正态分布做一个完整演示。假设真实分布是均值 3.0、标准差 1.5我们生成 200 个样本然后再用数值优化反推参数。import numpy as np from scipy.optimize import minimize rng np.random.default_rng(42) mu_true, sigma_true 3.0, 1.5 data rng.normal(locmu_true, scalesigma_true, size200) def neg_log_likelihood(params): mu, sigma params if sigma 0: return np.inf n len(data) return 0.5 * n * np.log(2 * np.pi * sigma ** 2) np.sum((data - mu) ** 2) / (2 * sigma ** 2) res minimize(neg_log_likelihood, x0[0.0, 1.0], methodNelder-Mead) print(MLE 估计结果:, res.x) print(样本均值和样本标准差:, data.mean(), data.std(ddof0))运行这段代码会看到优化得到的参数和样本直接计算出的均值和标准差非常接近。这是因为正态分布的最大似然估计有解析解数值优化只是验证。换到神经网络这类没有解析解的模型迭代式最小化负对数似然就成了唯一可行的路径。这也解释了为什么 PyTorch、TensorFlow 里的损失函数大多是“取负数、取对数”的形式。4.3 Fisher 信息估计量的不确定性费舍尔还提出了 Fisher 信息的概念用来衡量数据携带的参数信息量。直观理解是似然函数在最大值附近的曲率越大说明参数估计越可靠曲率越小说明很多参数值都能解释数据估计不确定性高。在现代机器学习中Fisher 信息被用于自然梯度、参数重要性估计、贝叶斯近似等领域是一个被低估的重要概念。5. 方差分析与 F 检验多组比较的标准方法5.1 为什么不能直接做多次 t 检验比较两组均值用 t 检验比较三组以上均值能不能两两比较可以但会引入多重比较问题。假设显著性水平是 0.05做 10 次独立检验至少出现一次假阳性的概率约是 40%。组数越多问题越严重。方差分析ANOVA的思路是不逐个比较而是先看整体。它把总变异拆成两部分组间变异和组内变异。如果组间变异明显大于组内变异说明分组变量确实解释了差异。这个“明显大于”的判断标准就是 F 检验。5.2 单因素 ANOVA 的 Python 示例这里用三组模拟数据演示比如三批不同工艺的样本测试结果import numpy as np from scipy import stats rng np.random.default_rng(7) group1 rng.normal(loc3.0, scale0.5, size20) group2 rng.normal(loc4.0, scale0.5, size20) group3 rng.normal(loc5.0, scale0.5, size20) f_stat, p_value stats.f_oneway(group1, group2, group3) print(F 统计量:, f_stat) print(p 值:, p_value)如果 p 值远小于 0.05说明三组均值存在显著差异。但注意ANOVA 的零假设是“所有组均值相等”拒绝这个假设只说明“至少有一组不同”不能告诉我们具体是哪几组不同。这时候需要用事后检验比如 Tukey HSD。5.3 使用 ANOVA 前的假设检查ANOVA 依赖几个前提条件观测独立、各组数据近似正态、各组方差齐性。实际数据分析中完全满足这些条件的情况很少。样本量较大时中心极限定理会缓解正态性要求但方差齐性问题仍然要关注。遇到明显的方差不齐可以考虑 Welch 方差分析或者先做变量变换。这里要特别提醒一点现在很多 Python 代码随手跑一个 ANOVA 就下结论但实验设计本身是否有随机化、有没有控制批次效应、样本是否独立这些信息不会体现在 F 统计量里。数据收集环节的问题后期统计方法救不回来。6. 实验设计方法论随机化、重复、区组与析因设计6.1 从农业试验到 AB 实验费舍尔在 Rothamsted 试验站工作时面对的是农业试验的经典问题不同肥料、不同品种、不同地块怎么设计试验才能得出可靠结论他给出的答案是随机化分配处理重复设置实验单元用区组控制地块差异。这套思想今天依然适用于互联网实验。以 AB 实验为例随机化把用户随机分配到实验组和对照组理论上保证两组在不可观测特征上也是可比的。重复每个组需要有足够的样本量否则效应量再大也检验不出来。区组如果存在明显的分层干扰因素比如新老用户、不同客户端版本可以先分层再随机这就是区组设计。6.2 析因设计同时研究多个因素和交互效应现实中很少只关心一个因素。比如做模型上线实验同时变了特征、阈值、采样比例怎么判断哪个变化起了作用逐个“单因子轮换”会遗漏交互效应而且实验次数多、效率低。费舍尔引入的析因设计同时改变多个因素用较少的实验次数估计主效应和交互效应。一个最简单的 (2^3) 全因子设计一共 3 个因素每个因素取两个水平共有 8 种组合。可以用 Python 生成完整设计矩阵from itertools import product factors [-1, 1] # 低水平和高水平 design list(product(factors, repeat3)) print(x1 x2 x3) for row in design: print(f{row[0]:3} {row[1]:3} {row[2]:3})输出会得到 8 行x1 x2 x3 -1 -1 -1 -1 -1 1 -1 1 -1 -1 1 1 1 -1 -1 1 -1 1 1 1 -1 1 1 1这 8 次实验可以用来估计 3 个主效应、3 个二阶交互效应和 1 个三阶交互效应。相比之下如果每个因素单独做实验需要更多轮次而且无法估计交互项。工业场景里因素数量一多全因子设计会爆炸于是又出现了部分因子设计牺牲一部分高阶交互信息换取实验成本的大幅下降。这些方法论都源自费舍尔时代的开拓。7. 群体遗传学用数学连接孟德尔与达尔文7.1 费舍尔在遗传学中的位置达尔文的自然选择理论解释了“物种如何变化”孟德尔的遗传学解释了“性状如何传递”但两者在数学上一直没有统一。费舍尔的工作就是用统计模型证明孟德尔式的遗传机制配上自然选择压力能够产生达尔文式的进化结果。这个框架后来和霍尔丹、赖特的工作一起构成群体遗传学的基石。种群遗传学里最重要的概念之一是遗传漂变在小种群中等位基因频率会因为随机抽样而波动这种波动不是自然选择导致的。费舍尔和赖特都为此建立了数学模型。今天做全基因组关联分析GWAS时如果不考虑群体结构和遗传漂变很容易得到假关联。理解这个基础模型对生物信息学方向的读者很有帮助。7.2 模拟一次遗传漂变下面用一个简单的 Wright-Fisher 模型模拟遗传漂变。假设一个二倍体种群中某个等位基因的初始频率是 0.5每个世代通过二项分布随机抽样产生下一代import numpy as np def wright_fisher(p0, population_size, generations, n_trails): end_freqs [] for _ in range(n_trails): p p0 for _ in range(generations): allele_count np.random.binomial(2 * population_size, p) p allele_count / (2 * population_size) end_freqs.append(p) return np.array(end_freqs) freqs wright_fisher(p00.5, population_size20, generations50, n_trails1000) print(平均最终频率:, freqs.mean()) print(最终频率标准差:, freqs.std()) print(固定为0或1的比例:, np.mean((freqs 0.01) | (freqs 0.99)))多次运行会看到群体越大最终频率的波动越小群体越小等位基因越容易丢失或固定。这就是遗传漂变的核心表现。遗传漂变是随机过程不是“选择”导致的但它在进化中同样重要。做群体遗传分析时这种随机波动必须作为基线模型才能把“真正的选择信号”分离出来。8. 现代应用边界、批量模拟与计算性能观察8.1 费舍尔方法的适用边界费舍尔的方法设计于小样本农业实验和遗传学场景直接搬到大数据场景时需要小心。几个典型边界要记住。第一p 值只能衡量“数据与零假设的兼容程度”不能衡量效应大小更不能代表“差异是否实际重要”现代 AB 分析要同时看效应量和置信区间。第二单次显著性检验可能假阳性大规模比较时要考虑多重检验修正比如 Bonferroni、FDR尤其在基因分析和特征筛选时。第三观察性数据没有随机化保障ANOVA 和回归控制不了未知混淆变量费舍尔自己也强调随机化实验的价值。第四MLE 在模型错误设定下依然会给出一个参数估计但这个估计未必有意义只看损失收敛是不够的。8.2 批量模拟验证统计方法稳定性的标准做法统计方法测试和算法测试不一样不能只跑一个例子。更好的做法是批量模拟用已知参数生成大量模拟数据观察估计结果是否无偏、检验功效是否足够。下面是一个批量模拟 MLE 的示例重复 500 次每次生成 100 个样本再记录估计误差import numpy as np from scipy.optimize import minimize rng np.random.default_rng(2024) errors [] n_repeat 500 sample_size 100 def neg_ll(params, data): mu, sigma params if sigma 0: return np.inf return 0.5 * len(data) * np.log(2 * np.pi * sigma ** 2) np.sum((data - mu) ** 2) / (2 * sigma ** 2) for _ in range(n_repeat): data rng.normal(loc5.0, scale2.0, sizesample_size) res minimize(neg_ll, x0[0.0, 1.0], args(data,), methodNelder-Mead) mu_hat res.x[0] errors.append(mu_hat - 5.0) errors np.array(errors) print(估计偏差均值:, errors.mean()) print(估计误差标准差:, errors.std(ddof1))从结果看偏差均值会接近 0说明 MLE 在这个场景下是无偏的标准差会随着样本量增大而下降这正是大样本性质的表现。做这类批量模拟时最好同时记录每次迭代的负对数似然值观察是否单调下降这比单独看最终参数更有诊断价值。8.3 计算性能观察费舍尔时代没有计算机ANOVA 和最大似然运算都靠手算所以方法设计特别强调“统计充分性”尽量把数据压缩成少量统计量不必保留原始数据。今天跑 ANOVA 的 F 统计量在几十万行数据上几乎没有任何压力真正压力出现在频繁迭代的 MLE 和群体遗传模拟里。批量模拟时建议先用小规模循环验证正确性再扩大到完整模拟生成随机数时固定seed保证结果可复现。用time.perf_counter记录每一轮耗时可以更直观地观察到样本量、参数个数对计算成本的影响。顺便说一句这篇内容不涉及接口 API 服务统计方法更多以scipy、statsmodels、numpy等库函数的形式暴露给调用方。如果要在团队内共享分析能力通常也是把分析方法封装成普通 Python 函数或内部工具包而不是单独部署在线服务。遇到大规模数据优先考虑抽样、并行化和增量计算。9. 常见误区、最佳实践与下一步9.1 高频误区自查下面这张表整理了使用费舍尔方法时最常踩的坑适合直接收藏。误区正确的理解p 值小于 0.05 就说明实验成功p 值不能度量效应量和实际业务价值需要结合置信区间和效应量三组数据两两跑 t 检验就行会放大假阳性概率应该先做 ANOVA再做事后检验最大似然估计只存在于统计教科书中机器学习交叉熵损失就是负对数似然模型训练就是 MLE 的批量实现只要样本量大ANOVA 就永远可信独立性、随机化、实验设计缺陷不会随样本量增大而消失遗传漂变是有方向的进化力量它是随机波动没有方向选择才有方向数据收集不理想用高级统计方法可以弥补实验设计问题无法靠统计方法事后补救9.2 工程化工作流建议如果要把费舍尔方法论应用到实际工作我建议从一开始就建立一个“实验设计文档”模板记录随机化方式、区组变量、样本量、预分析方法和成功标准。实验前写完实验后不修改。这不是形式主义而是对统计推断有效性的保障。配套的数据结构建议按照“一列一个因素一行一个样本”的形态准备方便用statsmodels的公式接口做 ANOVA 和回归。数据清洗时保留原始列不要在生产流程里直接覆盖否则后续要复核分组逻辑会很麻烦。模型训练时建议每次记录损失曲线、参数收敛轨迹、随机种子和训练数据版本。这些信息本质上是在给“最大似然估计是否收敛”提供证据。如果发现损失下降不稳定优先检查学习率、数据顺序和梯度计算而不是急着换模型结构。做 AB 实验或 A/B 测试时至少把三类指标分开核心指标、护栏指标、敏感指标。核心指标决定实验是否成功护栏指标防止优化核心指标时损害其他体验敏感指标用于判断数据量是否足够产生稳定信号。所有结果要提前确定显著性水平和最小可检测效应量。样本量计算应该在实验开始前而不是结束后。9.3 下一步可以做什么如果你刚接触这套内容建议按顺序完成三个小练习。第一用 Python 跑一遍女士品茶的 Fisher 精确检验理解“小样本精确概率”是如何计算的。第二用scipy.stats.f_oneway分析一份多组数据再用statsmodels做事后 Tukey 检验比较两种结果的差异。第三用 Wright-Fisher 模型模拟不同种群大小的漂变画出最终频率分布的直方图。这三个练习都不需要 GPU也不需要安装复杂环境一台普通电脑就能跑完但能把本文的核心概念串起来。之后可以把视角扩展到“费舍尔之后”的方法贝叶斯统计、因果推断、非参数检验、生存分析。这些方法解决的是费舍尔时代计算条件不足或假设过强的问题。把经典框架和现代扩展放在一起看才能真正形成自己的统计判断力。这篇内容建议收藏备用遇到实验设计、显著性检验、模型损失函数这类问题的时候重新回来对照一遍。
返回列表