ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

罗纳德·费舍尔:从最大似然到AB实验,统计推断的奠基与工程实践

罗纳德·费舍尔:从最大似然到AB实验,统计推断的奠基与工程实践 如果你做过数据分析一定遇到过这样的场景想比较三种优化算法在相同数据集上的效果差异于是老老实实做了三次两两 t 检验结果被审稿人或同事质疑“多重比较问题”。又或者你费了半天劲收集了一堆实验数据却不知道样本量够不够、分组是否随机、结论是否经得起推敲。这些今天被称为“统计学基本功”的方法源头都指向同一个人——罗纳德·费舍尔Ronald Aylmer Fisher。他是最大似然估计的提出者是方差分析ANOVA的开创者是现代实验设计的奠基人同时还是把达尔文进化论与孟德尔遗传学统一起来的群体遗传学之父。更让人印象深刻的是他通过一杯奶茶就设计出了现代统计学中最经典的教学案例。这篇文章不想写成人物传记。我想从一个数据工程师和算法工程师的视角把费舍尔留下的几件核心工具拆开看它们到底解决什么问题为什么一百年后的今天依然没有过时以及你在做数据分析、AB实验和建模时应该怎么正确使用这些思想。读完这篇文章你会得到三样东西对最大似然估计、ANOVA、实验设计、群体遗传学这四块内容的系统性理解以及它们之间的逻辑关联。可以直接运行的 Python 示例代码演示最大似然和 ANOVA 的落地用法。一套在实际项目中避免统计误用的排查思路和最佳实践。费舍尔的伟大不在于他发明了某个公式而在于他确立了“从数据中做推断”这件事的科学规则。这些规则直到今天仍是数据科学的地基。1. 这篇文章真正要解决的问题先说一个最容易踩进去的认知误区很多人学统计学是零散地学学了 t 检验不知道它和 ANOVA 是什么关系学了最大似然不知道它和回归有什么关系做了 AB 实验也不知道为什么一定要随机化。这种“点状知识”在应付考试时没问题一旦进入真实项目就会露馅。举个例子。你在做特征工程时发现某个特征在不同类别下的均值有差异于是直接下结论“这个特征有效”。但如果组内波动远大于组间差异这个结论就是错的。这不是业务问题而是统计推断问题——你根本没有区分“组间差异”和“组内噪声”。再举个例子。你想拟合一个用户付费金额的预测模型选了最小二乘法但不知道为什么最小二乘在误差服从正态分布时等价于最大似然估计。一旦误差分布变了比如出现长尾数据你就不知道该怎么调整目标函数。这些问题的共同根源是我们多数学到的“方法”是碎片化的而费舍尔当年恰恰是在构建一套完整的推断体系。他不是先有公式再去证明公式而是先想清楚“从有限样本推断总体到底意味着什么”再推导出具体的数学工具。因此这篇文章要站在费舍尔的问题框架里重新串联这些方法最大似然估计告诉你如何从数据中找到最可能的参数。ANOVA告诉你如何判断组间差异是否显著而不是被数据噪声骗了。实验设计告诉你如何从源头保证数据质量让后续分析有意义。群体遗传学告诉你同样的推断思想如何被扩展到生物进化这种大规模问题中。适合阅读这篇文章的读者包括正在学习统计学的数据分析师、做 AB 实验的算法工程师、需要设计用户研究或性能对比实验的开发者以及所有对“数据结论怎么才可信”有好奇心的人。不适合的读者只有一种想要快速抄公式而不想理解原理的人。2. 费舍尔的核心思想从育种数据到推断科学费舍尔 1890 年生于伦敦1919 年进入 Rothamsted 农业实验站工作。那里存储着几十年的小麦育种数据。正是面对这些数据时他意识到传统描述统计的局限你不仅要描述数据还要做判断。育种实验里有一个非常现实的问题你只有一小块试验田种了几个品种收成有高有低。怎么判断哪个品种真正更好产量差异有可能是品种差异也可能是土壤肥力差异、水分差异、偶然波动。费舍尔要做的事情是把“偶然波动”定量化再把“真实差异”从噪声中分离出来。这就是现代统计推断的起点。费舍尔的核心思想可以用一句话概括一切结论必须包含不确定性的度量。你不能只说“A 比 B 好”你要说“A 比 B 好的概率有多大或者这种差异在随机波动下出现的概率有多小”。为了实现这个目标他做了三件奠基性工作提出“似然”likelihood的概念并在 1922 年的经典论文中系统阐述了最大似然估计。发展出方差分析ANOVA把总变异分解为主要效应、交互效应和误差项。确立随机化、重复、区组三大实验设计原则让数据本身变得更可靠。这三件事有一个清晰的递进关系如果你想做出正确的推断既需要正确的估计方法最大似然也需要正确的比较框架ANOVA还需要正确的数据来源实验设计。缺少任何一环推断就可能失真。这里需要澄清一个常见混淆最大似然估计和最小二乘是什么关系费舍尔证明了当误差服从正态分布时最大似然估计的结果和最小二乘法完全一致。这解释了为什么线性回归里最小二乘如此强大——它不只是几何上的“距离最小化”它在统计上还有坚实的理论支撑。一旦理解了这层关系很多问题就通了为什么逻辑回归用最大似然而不是最小二乘因为逻辑回归的误差是二项分布不是正态分布。目标函数必须跟着分布走。费舍尔体系的另一个关键贡献是“充分统计量”。简单说当我们估计一个参数时并非所有数据都同等重要有些统计量已经包含了全部相关信息。比如估计正态分布的均值样本均值就是总体均值的充分统计量其他数据细节不再提供额外信息。这个概念为现代机器学习中的“特征提取”提供了理论基础——好的特征就是关于目标的充分统计量。3. 最大似然估计原理、直觉与 Python 实现最大似然估计Maximum Likelihood EstimationMLE是费舍尔对统计学最核心的理论贡献之一也是今天机器学习中使用最广泛的参数估计方法。3.1 最大似然的直观理解假设我们有一组观测数据我们相信它来自某个概率分布但这个分布的参数未知。最大似然的思想非常朴素选择一组参数使得在这组参数下观察到当前数据这一事件的概率即似然最大。翻译成人话让“已经发生的事”看起来尽可能“合理”。如果观测数据是独立同分布的那么联合概率就是每个样本概率的乘积称似然函数。由于连乘容易造成下溢通常取对数变成对数似然。求解最大对数似然对应的参数就是参数估计值。3.2 用最大似然实现正态分布均值估计我们先用一个最小示例演示如何用最大似然估计正态分布的均值和方差然后对比样本均值和最小二乘拟合。# 文件路径mle_normal_demo.py import numpy as np from scipy.optimize import minimize_scalar np.random.seed(42) # 生成模拟数据真实均值为 10标准差为 2 data np.random.normal(loc10.0, scale2.0, size200) def neg_log_likelihood(mu): 正态分布对数似然函数去掉常数项后取负便于最小化 公式sum((x - mu)^2) / (2 * sigma^2) n * ln(sigma) 这里先用已知 sigma2 的情况演示 mu 的估计 sigma 2.0 n len(data) return n * np.log(sigma) np.sum((data - mu) ** 2) / (2 * sigma ** 2) # 通过一维搜索找到使负对数似然最小的 mu result minimize_scalar(neg_log_likelihood, bounds(0, 20), methodbounded) print(fMLE 估计的均值: {result.x:.4f}) print(f样本均值: {np.mean(data):.4f})这段代码演示了一个重要事实当误差服从正态分布且方差固定时最大似然估计的结果与样本均值一致。这不奇怪因为平方损失函数就是由正态分布的对数似然推导出来的。3.3 同时估计均值和方差更一般的场景是均值和方差都未知。这时我们可以用scipy.optimize.minimize同时优化两个参数。# 文件路径mle_normal_full.py import numpy as np from scipy.optimize import minimize np.random.seed(42) data np.random.normal(loc10.0, scale2.0, size200) def neg_log_likelihood(params): mu, sigma params if sigma 0: return 1e10 # 标准差必须为正 n len(data) return n * np.log(sigma) np.sum((data - mu) ** 2) / (2 * sigma ** 2) result minimize(neg_log_likelihood, x0[0.0, 1.0], methodNelder-Mead) mu_mle, sigma_mle result.x print(fMLE 均值: {mu_mle:.4f}, MLE 标准差: {sigma_mle:.4f}) print(f样本均值: {np.mean(data):.4f}, 样本标准差(总体): {np.std(data):.4f}) print(f总体标准差的无偏估计: {np.std(data, ddof1):.4f})注意这里np.std(data, ddof1)比np.std(data)更接近真实的 2.0。因为方差的最大似然估计有偏费舍尔后来也参与讨论了无偏修正的问题。这个细节在实际项目中很关键如果样本量小直接用 MLE 估计的方差会低估波动性。3.4 最大似然与最小二乘的关系在线性回归中如果假设误差服从正态分布那么最大似然估计的目标函数就是最小化残差平方和。这就是最小二乘回归的统计根基。# 文件路径ols_vs_mle.py import numpy as np from scipy.optimize import minimize np.random.seed(123) x np.linspace(0, 10, 100) true_intercept, true_slope 1.0, 2.5 y true_intercept true_slope * x np.random.normal(0, 1.0, sizelen(x)) def neg_log_likelihood_linear(params): intercept, slope, sigma params if sigma 0: return 1e10 y_pred intercept slope * x n len(x) return n * np.log(sigma) np.sum((y - y_pred) ** 2) / (2 * sigma ** 2) # 用最小二乘解 A np.vstack([np.ones_like(x), x]).T beta_ols np.linalg.lstsq(A, y, rcondNone)[0] # 用 MLE 解 result minimize(neg_log_likelihood_linear, x0[0.0, 0.0, 1.0], methodNelder-Mead) beta_mle result.x[:2] print(fOLS 截距: {beta_ols[0]:.4f}, 斜率: {beta_ols[1]:.4f}) print(fMLE 截距: {beta_mle[0]:.4f}, 斜率: {beta_mle[1]:.4f})两种方式得到的系数几乎一致。理解这一点你就理解了为什么许多回归算法的目标函数是平方损失——它背后对应的是正态误差假设。4. ANOVA方差分析为什么是均值比较的主流方案方差分析Analysis of VarianceANOVA是费舍尔 20 世纪 20 年代在 Rothamsted 期间发展的一套方法。它解决的问题是当我们要比较两个以上组的均值时如何避免两两 t 检验带来的多重比较风险。4.1 为什么不能只用 t 检验如果你有 5 个组两两比较需要做 C(5,2)10 次 t 检验。就算每次检验的显著性水平是 0.0510 次都通过的概率也会明显下降。这就是多重比较的膨胀问题。ANOVA 的思路是先做一个全局检验判断组间是否存在任何显著差异再做事后比较。4.2 ANOVA 的核心思想分解总变异ANOVA 的思路很直观将总变异分解为组间变异和组内变异两个部分。组间变异反映的是不同处理之间的差异。组内变异反映的是随机误差。然后用 F 统计量表示二者的比值。如果组间差异足够大、大于随机波动就认为处理效应显著。用费舍尔的表述方式一组观测数据的总平方和可以分解为两个独立部分的平方和之和。这个思想后来成为实验设计中进行方差分解的基本语言。4.3 Python 实现单因素 ANOVA下面用scipy.stats和statsmodels演示单因素 ANOVA。# 文件路径anova_oneway.py import numpy as np import pandas as pd from scipy import stats import statsmodels.api as sm from statsmodels.formula.api import ols np.random.seed(2024) # 模拟三组实验数据A、B、C 三种优化策略 group_a np.random.normal(loc100, scale10, size30) group_b np.random.normal(loc105, scale10, size30) group_c np.random.normal(loc112, scale10, size30) df pd.DataFrame({ value: np.concatenate([group_a, group_b, group_c]), group: [A] * 30 [B] * 30 [C] * 30 }) # 方法一scipy.stats 的 f_oneway f_stat, p_value stats.f_oneway(group_a, group_b, group_c) print(fscipy F 统计量: {f_stat:.4f}, p 值: {p_value:.6f}) # 方法二statsmodels 的线性回归 ANOVA 表 model ols(value ~ C(group), datadf).fit() anova_table sm.stats.anova_lm(model, typ2) print(anova_table)运行结果中如果 p 值小于 0.05说明三组均值存在显著差异。但 ANOVE 只能告诉你“不全相同”不能告诉你是哪两组不同。此时需要事后检验比如 Tukey HSD。# 文件路径anova_posthoc.py from statsmodels.stats.multicomp import pairwise_tukeyhsd tukey pairwise_tukeyhsd(endogdf[value], groupsdf[group], alpha0.05) print(tukey)4.4 ANOVA 的前提条件ANOVA 并不是无条件的。它的有效运行依赖三个核心假设假设含义违反时的风险正态性每组数据来自正态分布F 统计量可能失真p 值不可靠方差齐性各组的总体方差大致相等显著性检验被夸大或缩小独立性样本之间相互独立伪重复可能导致假阳性在实际项目中我会建议先做 Levene 检验检查方差齐性再画 Q-Q 图检查正态性。如果假设严重违背可以考虑 Welch 方差分析或非参数方法如 Kruskal-Wallis 检验。5. 实验设计从“女士品茶”到今天的 AB 实验“女士品茶”是费舍尔最著名的故事。一位女士声称自己能分辨出奶茶是先加茶还是先加奶费舍尔设计了一个随机化实验来验证这个说法。他准备了 8 杯茶随机选取 4 杯先加奶、4 杯先加茶且不告诉女士哪些是哪些。结果这位女士全部答对。费舍尔计算了全部正确概率[ P \frac{1}{\binom{8}{4}} \frac{1}{70} \approx 0.014 ]这个概率小于 0.05因此有较强证据认为她的判断并非偶然。这个故事在技术圈广为流传是因为它包含了实验设计的所有核心原则随机化、重复、区组控制、显著性检验。今天的 AB 实验本质上就是“女士品茶”实验在互联网行业的大规模翻版。5.1 随机化为什么重要在费舍尔之前许多“实验”并没有严格的随机化。比如育种试验中研究者可能习惯性把长势较好的品种种在水肥条件更好的地块然后得出这个品种更优的结论。随机化的作用是消除系统偏差把混杂因素均匀地分配到各组。放到互联网 AB 实验环境中随机化意味着用户被分到实验组和对照组时不能由业务方挑选而是通过哈希或随机数分配。这样年龄、活跃度、设备类型等因素在各组间的分布才会相近。5.2 重复和区组区组blocking设计是费舍尔另一个重要贡献。它的思想是如果存在已知的干扰因素比如土壤肥力差异或用户活跃度差异就把样本先按这个因素分成若干个区组在每个区组内再随机分配处理。这样做能有效降低背景噪声对实验灵敏度的干扰。在 AB 实验中区组化的典型做法是“分层采样”。例如按用户城市、用户等级分层在每层内部均匀分配实验组与对照组。它比纯随机化在大部分场景下更敏感能更快地检测出小效应。5.3 实验设计中的陷阱做 AB 实验时最容易忽略的三个问题样本量不足效应量很小样本不够实验跑一周依然没有统计显著性。多重比较同时看几十个指标总会有几个碰巧显著。提前停止每天看 p 值看到“显著”就停止实验会导致结论膨胀。这些问题在费舍尔的框架中都有对应原则实验前确定分析计划、在必要的时候进行多重比较校正、在固定样本量下进行推断。现代增长团队常犯的错误恰恰是拿费舍尔的思想去解释但解释得不完整。6. 群体遗传学用统计推断统一进化论与孟德尔遗传学费舍尔 1930 年出版了《自然选择的遗传理论》The Genetical Theory of Natural Selection这本书把达尔文的自然选择理论和孟德尔的遗传学统一在同一个数学框架下。这个贡献在当时的生物学界是革命性的因为它证明了遗传的离散性孟德尔颗粒遗传和多基因连续变异生物统计学派观察到的现象并不矛盾。6.1 基础概念等位基因频率群体遗传学研究的核心变量是等位基因频率。假设某个基因位点有两个等位基因 A 和 a在一个种群中A 的频率记为 pa 的频率记为 qpq1。只要知道等位基因频率就能计算基因型频率的预期值这就是 Hardy-Weinberg 平衡。Hardy-Weinberg 平衡是一种“零模型”当种群无限大、随机交配、无选择、无突变、无迁移时基因型频率保持稳定。现实中的种群会偏离这个平衡而偏离方向恰好揭示了进化力量的作用。比如如果某个纯合子的频率显著高于平衡预期可能说明存在近交如果杂合子频率偏高可能说明杂合优势。这种逻辑和假设检验完全一致先假设“没有效应”平衡再观察数据与预期的偏差偏差大到一定程度就拒绝原假设。这就是费舍尔统计思想在生物学中的延伸。6.2 Python 模拟 Hardy-Weinberg 平衡下面用一个简单的 Python 代码演示等位基因频率如何影响基因型频率。# 文件路径hardy_weinberg_demo.py import numpy as np import pandas as pd def simulate_hwe(p, n_individuals1000): 根据等位基因频率 p 模拟随机交配下的基因型频率 q 1 - p genotypes np.random.choice( [AA, Aa, aa], sizen_individuals, p[p**2, 2*p*q, q**2] ) counts {g: np.sum(genotypes g) for g in [AA, Aa, aa]} frequencies {g: c / n_individuals for g, c in counts.items()} return frequencies for p in [0.1, 0.5, 0.9]: freq simulate_hwe(p, n_individuals2000) print(fp{p}, 基因型频率: {freq})6.3 费舍尔的进化统计思想为什么自然选择可以用数学描述费舍尔提出了“基础定理”种群适应度的增长率正比于其加性遗传方差。这个定理可以理解为自然选择之所以能够在多基因系统中发挥作用是因为遗传变异提供了足够的原材料而选择作用像“最大似然更新”一样一代一代地朝最优方向移动。这种思想的工程意义在于它把进化从“哲学叙事”转化为“可验证的数学模型”。现代进化计算、遗传算法、甚至某些强化学习算法都借鉴了类似的搜索逻辑——用变异产生候选用选择保留优势用遗传积累进步。这也是费舍尔与其他统计学家最大的不同他不仅关心数据推断还试图用统计力学的方式去理解生命演化本身。一个统计学家凭一己之力开创了群体遗传学这门学科这本身就是统计学强大解释力的证明。7. 常见统计误用与排查思路费舍尔创立的这些方法已经有一百多年历史但今天在许多项目里它们的正确率依然低得惊人。以下是我在开发和评审中常见的问题整理成一张排查表。问题现象可能原因排查方式解决方案两两比较结果与 ANOVA 不一致多重比较未校正p 值膨胀查看比较次数与显著性阈值先做全局 F 检验再做 Tukey HSD 或 Bonferroni 校正回归系数不显著但模型 R² 很高特征共线性或样本量不足查看 VIF 值和置信区间宽度检查相关性矩阵考虑删除冗余特征MLE 估计结果与样本矩估计差异大数据分布并非假设的正态分布画 QQ 图、观察偏度和峰度更换分布假设如 t 分布、Gamma 分布小样本下方差估计偏低MLE 中方差分母为 n 而非 n-1比较 ddof0 与 ddof1 的结果使用无偏修正的样本方差AB 实验 p 值反复震荡样本量不足或过早停止实验绘制累积 p 值检查效应量采用序贯检验或预先固定样本量实验组与对照组基线差异明显随机化失效或分层变量缺失比较分组前后关键协变量分布引入分层随机化或重跑实验这张表的价值在于它把零散的错误模式归纳成一套检查清单。遇到统计相关的问题先对照这张表定位通常能节省不少排查时间。这里要特别强调一个观念p 值不是“结论正确概率”。很多项目把 p 值当作裁判其实它是“假设检验”框架下的一个条件概率。费舍尔自己后来也承认p 值在解释性研究和探索性分析中容易被误读。在决策场景中推荐同时关注效应量和置信区间避免只看 p 值。8. 费舍尔思想对现代数据科学的启示一百年过去费舍尔的核心思想依然渗透在今天的技术栈里。如果你用 Apache Spark 做大规模线性回归底层目标函数是平方损失加正则化如果你用 TensorFlow 训练分类模型损失函数是交叉熵——这其实是最大似然的另一种形式。如果你在字节、阿里做 AB 实验平台层面对照组和实验组的分配逻辑就是随机化原则的产品化如果你用 scikit-learn 做单因素特征筛选用 F 检验计算特征与标签的相关性其实就是在做 ANOVA。换个角度看费舍尔留给工程界的不是几个孤立公式而是一整套“如何从数据做出可信判断”的工程原则。我把它们拆成下面几条任何参数估计都应带有不确定性度量。没有标准误的模型系数无法支撑决策。好的实验设计优于复杂的统计纠偏。设计阶段把随机化和分层做好后续分析轻松一半。模型不是越复杂越好分布假设要对。线性回归在长尾数据上表现不佳不是因为线性回归不行而是正态分布假设不成立。不要把探索性分析当成验证性分析。数据挖掘发现的相关性需要用新数据或设计好的实验去验证。这些原则本质上都是费舍尔在一百年前反复强调的。再补一个今天特别热门的话题机器学习和传统统计的关系。费舍尔的最大似然思路在今天依然是深度学习的基础。深度学习训练时我们通常最小化某个损失函数。对于分类问题损失函数是交叉熵它的统计含义就是最大似然对于回归问题损失函数是 L2 或 L1 损失它们的统计含义分别对应正态分布和拉普拉斯分布下的最大似然。理解了这层关系你会发现深度学习调参时选择损失函数本质上是在为数据选择分布假设。当你的回归数据包含大量异常值时L2 损失会过度惩罚离群点换用 L1 损失对应拉普拉斯分布更稳健。这正是费舍尔统计思想的现代延伸。9. 最佳实践与工程建议这部分完全是实践导向。如果你在自己的项目里应用费舍尔的思想建议遵循以下几条9.1 做实验前先写分析计划在启动 AB 实验或对比实验之前明确以下几个问题主要指标是什么最小可检测的效应量是多少显著性水平和统计功效是多少样本量是否够如果这些参数没有提前确定实验跑完再分析很容易陷入 p 值操纵。9.2 最大化数据质量优于后期纠偏与费舍尔同时代的统计学家有句说法“实验设计的成本远比事后弥补更低。” 这意味着该随机化的字段一定要随机化。该分层的维度要提前定义好。该排除的异常样本要在实验前定义标准。对已知干扰因素优先使用区组设计。9.3 区分探索性分析与验证性分析在日常工作中我倾向于把分析分为两类探索性分析目的是发现假设可以用多种统计方法反复看数据。验证性分析目的是检验假设需要严格设定显著性水平、固定分析流程。如果混淆这两类分析最常见的后果是把“碰巧显著”当成“真实有效”上线后效果回吐。费舍尔的实验设计哲学正是为了避免这种混乱。9.4 正确报告结果在博客、周报或论文中报告分析结果时推荐包含以下信息实验设计类型完全随机设计 / 随机区组设计 / 析因设计。样本量及各组基础统计量。使用的检验方法及前提条件验证结果。效应量及置信区间而不只是 p 值。数据采集范围和排除标准。这样写的报告即使旁人无法复现代码也能判断你的结论是否可信。10. 总结与继续学习的路线费舍尔在统计学史上的地位不是因为他写下了某个公式而是因为他把“从数据做推断”变成了一套统一的、可检验的科学方法论。最大似然估计提供了参数估计的通用框架ANOVA 提供了多组比较的统计工具实验设计保证了数据的可信来源群体遗传学则展示了同一套思想如何跨越学科边界。对今天的开发者来说理解费舍尔思想的最大价值不是去手推公式而是建立一种正确的统计直觉任何数据结论都只是“在某种假设和误差框架下的估计”真正重要的是模型假设是否合理、数据过程是否受控、推断方法是否匹配。如果你想继续深入几个值得研究的方向阅读费舍尔 1925 年《Statistical Methods for Research Workers》重点看他对实验设计和显著性检验的表述。学习线性回归和方差分析之间的形式关系参考统计学教材中的一般线性模型章节。学一学 R 语言或 Python 中实验设计的经典包比如pyDOE2、statsmodels中的ANOVA模块。深入研究 p 值误用的历史讨论从费舍尔到 Neyman-Pearson 学派的分歧与补充。在真实 AB 实验平台上实践随机化与分层设计体会“设计-分析-验证”的完整闭环。费舍尔当年面对的是小麦试验田今天你面对的是海量用户行为数据、模型训练任务和算法迭代。工具变了数据规模变了但“如何从数据中得出可靠结论”这个根本问题没有变。理解费舍尔就是理解数据科学的底层公理。如果你手头正好有一个即将开展的 AB 实验或者一个正在调参的回归模型不妨停下来问一句如果费舍尔拿到这些数据他会怎么设计、怎么分析、怎么判断想清楚这个问题比多调十几个超参数有用得多。
返回列表