费舍尔统计遗产:从最大似然估计到实验设计的现代应用
2026/9/4 1:43:31 网站建设 项目流程

这次我们聊的不是一个新模型,而是现代统计学的奠基人之一:罗纳德・费舍尔。今天机器学习训练用的交叉熵损失,本质上就是最大似然估计的负对数形式;AB 实验里的显著性检验和实验分组,最早也能追溯到费舍尔在农业试验站设计的那套随机化框架;生物信息学里做群体遗传分析时,等位基因频率的随机波动模型同样绕不开他的名字。这篇文章会先把费舍尔的核心贡献整理成一张速览表,然后用具体代码分别演示最大似然估计、方差分析、Fisher 精确检验和遗传漂变模拟,最后聊一聊这些方法在当下数据分析工作流里的适用边界,以及最常见的使用误区。

如果你是一个数据分析师、算法工程师、生物信息学方向的学生,或者正在做 AB 实验但总觉得“p 值”用得不踏实,这篇文章值得看完。即使你已经有统计基础,从“费舍尔为什么这样设计实验”的角度重新过一遍这些方法,也能帮你把很多“默认在用的东西”重新理解清楚。本文不涉及任何环境部署,所有代码只需要 Python 和几个常见科学计算包就可以运行。

1. 费舍尔核心贡献速览

费舍尔一生横跨统计学、遗传学和进化生物学,很难用一个身份去定义他。对今天的技术读者来说,最实用的入口是:他创造了我们今天仍然高频使用的一整套统计工具。下面这张表先把核心内容对齐一下。

维度说明
人物罗纳德・艾尔默・费舍尔(Ronald Aylmer Fisher,1890-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, alternative="greater") 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 Estimation,MLE)要解决这样的问题:给定一组观测数据,应该选择哪个参数,使得在这组参数下观测到当前数据的概率最大。假设数据独立同分布,似然函数写成:

[ L(\theta) = \prod_{i=1}^{n} P(x_i \mid \theta) ]

由于连乘容易下溢,实际更常用对数似然:

[ \ell(\theta) = \sum_{i=1}^{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(loc=mu_true, scale=sigma_true, size=200) 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], method="Nelder-Mead") print("MLE 估计结果:", res.x) print("样本均值和样本标准差:", data.mean(), data.std(ddof=0))

运行这段代码会看到,优化得到的参数和样本直接计算出的均值和标准差非常接近。这是因为正态分布的最大似然估计有解析解,数值优化只是验证。换到神经网络这类没有解析解的模型,迭代式最小化负对数似然就成了唯一可行的路径。这也解释了为什么 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(loc=3.0, scale=0.5, size=20) group2 = rng.normal(loc=4.0, scale=0.5, size=20) group3 = rng.normal(loc=5.0, scale=0.5, size=20) 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, repeat=3)) 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(p0=0.5, population_size=20, generations=50, n_trails=1000) 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(loc=5.0, scale=2.0, size=sample_size) res = minimize(neg_ll, x0=[0.0, 1.0], args=(data,), method="Nelder-Mead") mu_hat = res.x[0] errors.append(mu_hat - 5.0) errors = np.array(errors) print("估计偏差均值:", errors.mean()) print("估计误差标准差:", errors.std(ddof=1))

从结果看,偏差均值会接近 0,说明 MLE 在这个场景下是无偏的;标准差会随着样本量增大而下降,这正是大样本性质的表现。做这类批量模拟时,最好同时记录每次迭代的负对数似然值,观察是否单调下降,这比单独看最终参数更有诊断价值。

8.3 计算性能观察

费舍尔时代没有计算机,ANOVA 和最大似然运算都靠手算,所以方法设计特别强调“统计充分性”:尽量把数据压缩成少量统计量,不必保留原始数据。今天跑 ANOVA 的 F 统计量在几十万行数据上几乎没有任何压力,真正压力出现在频繁迭代的 MLE 和群体遗传模拟里。批量模拟时,建议先用小规模循环验证正确性,再扩大到完整模拟;生成随机数时固定seed,保证结果可复现。用time.perf_counter记录每一轮耗时,可以更直观地观察到样本量、参数个数对计算成本的影响。

顺便说一句:这篇内容不涉及接口 API 服务,统计方法更多以scipystatsmodelsnumpy等库函数的形式暴露给调用方。如果要在团队内共享分析能力,通常也是把分析方法封装成普通 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,也不需要安装复杂环境,一台普通电脑就能跑完,但能把本文的核心概念串起来。

之后可以把视角扩展到“费舍尔之后”的方法:贝叶斯统计、因果推断、非参数检验、生存分析。这些方法解决的是费舍尔时代计算条件不足或假设过强的问题。把经典框架和现代扩展放在一起看,才能真正形成自己的统计判断力。

这篇内容建议收藏备用,遇到实验设计、显著性检验、模型损失函数这类问题的时候,重新回来对照一遍。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询