最大似然估计Maximum Likelihood Estimation, MLE大概是统计学里最常用、也最容易被误解的一个估计方法。很多人学了公式会套但真换一个场景就懵了不知道为什么这么做更不知道结果为什么可信。这篇我想从直觉讲起带你一步步把MLE的底层逻辑、推导过程和实战细节捋清楚。不管你是做数据分析、机器学习还是搞科研处理实验数据只要涉及“根据观测数据反推参数”MLE都是绕不开的核心工具。我会用大量具体例子和可直接复现代码来讲尽量少堆数学符号但关键的推导过程会保留——毕竟只看结论的话遇到问题你还是不会举一反三。1. 从抛硬币说起MLE到底在干什么1.1 概率和似然同一个式子不同的读法MLE的核心就一句话在给定观测数据的前提下找到一组参数使得这组数据出现的“可能性”最大。听起来很好理解但很多人卡在“可能性”这三个字上因为概率和似然在数学形式上几乎一样含义却完全不同。举个例子。你手里有一枚硬币不知道它是否公平假设正面朝上的概率是 θ。你抛了 10 次结果 7 次正面、3 次反面。那么概率视角如果 θ 0.5问“抛10次出现7次正面”的概率是多少。这个好算二项分布公式套进去就行。似然视角已知结果就是“7正3反”问“θ 是多少时这个结果最可能发生”。这时θ是未知的我们要把它当成一个变量去看不同θ下同一个结果的概率值。看出来区别了吗概率是已知参数、预测结果似然是已知结果、反推参数。严格一点说似然函数写作 L(θ | data)读作“给定数据下参数θ的似然”本质上就是在 θ 已经给定的条件下得到这组数据的概率密度或质量函数值但我们关注的是它作为 θ 的函数时在哪个位置取得最大值。记住这个视角转换后面所有公式都不会再让你头晕。1.2 最大化似然我们猜的那个参数继续抛硬币的案例。假设我们设正面概率为 θ抛了 n 次其中 k 次正面。似然函数为L(θ) θ^k · (1 - θ)^(n-k)这个函数在 θ 取不同值时对应的“出现该数据”的可能性大小不同。我们要找的估计值 θ̂ 就是让 L(θ) 最大的那个 θ。直接把 L(θ) 对 θ 求导等于0就行了。但是这里有个实操小技巧乘积形式求导非常麻烦所以通常对似然函数取对数变成求和形式。因为对数函数是单调递增的取对数不会改变最大值的位置但能大幅简化计算。这就是“对数似然”的由来。对数似然写出来是ℓ(θ) k·ln(θ) (n-k)·ln(1-θ)求导dℓ/dθ k/θ - (n-k)/(1-θ) 0解出来就是 θ̂ k/n。也就是说根据MLE我们对正面概率的最佳估计就是样本中正面的比例。这个结果非常直观也是 MLE 的第一个“让你觉得废话”的例子。但别小看它这个最简单的框架是一切复杂模型的起点。注意这里隐含了一个假设——每次抛硬币是独立的且正面概率恒定。如果硬币被人动了手脚或者抛掷方式不同导致概率不固定那模型假设本身就需要重新思考。MLE 再强大也是建立在模型假设之上的。2. 经典推导实战从离散到连续的完整流程2.1 离散案例伯努利分布下的MLE上面已经用二项分布算了一次但实际建模中更常见的是把每次抛硬币当成伯努利试验也就是对每一个观测值 x_i0表示反面、1表示正面写出它的概率质量函数P(x_i | θ) θ^(x_i) · (1-θ)^(1-x_i)然后 n 个独立样本的联合概率就是每个样本概率的乘积L(θ) ∏ θ^(x_i) · (1-θ)^(1-x_i)取对数ℓ(θ) Σ [x_i·ln(θ) (1-x_i)·ln(1-θ)]这个求和形式特别好用因为它把指数变成了乘积、把乘积变成了求和。求导dℓ/dθ Σ x_i/θ - (n - Σ x_i)/(1-θ) 0设 Σx_i k解出来的结果还是 θ̂ k/n。你可能觉得绕了一大圈结果没变但这个推导过程很重要因为它是“从样本概率乘积到对数求和再求导”的标准范式。以后遇到任何分布只要写出似然函数走这套流程就不会出错。2.2 连续案例正态分布均值和方差的MLE离散情况解决后我们把同样思路用到连续分布。最常见、也最实用的是正态分布 N(μ, σ²) 的参数估计。设我们有 n 个独立同分布的样本 x_1, x_2, ..., x_n来自正态分布。每个样本的概率密度为f(x_i | μ, σ²) (1/√(2πσ²)) · exp(-(x_i-μ)²/(2σ²))联合密度也就是似然函数是这些密度的乘积。取对数后ℓ(μ, σ²) -n/2·ln(2π) - n/2·ln(σ²) - (1/(2σ²))·Σ(x_i-μ)²现在有两个参数需要分别求偏导并令其为0。对 μ 求偏导∂ℓ/∂μ (1/σ²)·Σ(x_i-μ) 0解得 μ̂ (1/n)·Σx_i也就是样本均值。对 σ² 求偏导∂ℓ/∂σ² -n/(2σ²) (1/(2σ⁴))·Σ(x_i-μ)² 0解得 σ̂² (1/n)·Σ(x_i-μ̂)²。看出来了吗正态分布下均值的MLE是样本均值方差的MLE是“除以n”的样本方差而不是教科书里常说的除以(n-1)的无偏方差。这个细节每年能坑一批人。MLE 给出的是渐近无偏的估计量在小样本下方差估计会系统性偏低。所以如果你在做小样本统计推断直接用 σ̂² 做 t 检验、F 检验误差会偏大。实际处理中小样本场景通常会改用无偏修正版本但你要清楚两者来源的区别一个是 MLE 优化结果一个是无偏性要求下的修正。提示在做正态分布参数估计时均值直接用 MLE 没问题方差记得考虑样本量大小。n 很大时两者几乎没差别n 只有十几个时优先用无偏修正版本。3. 为什么MLE值得信任三个关键性质3.1 不变性估计出来的参数换形式也方便这是 MLE 一个非常实用的优点如果 θ̂ 是 θ 的MLE那么对于任意函数 g(θ)g(θ̂) 就是 g(θ) 的MLE。换句话说你不需要重新推导直接把估计值代进变换函数就行。举个例子。你在做方差分析假设数据服从指数分布参数是 λ率参数。MLE 估计出来 λ̂ 1/x̄。如果你关心的是均值 μ 1/λ那么 μ 的MLE 直接就是 x̄不需要再搞一套优化。另一个例子是逻辑回归中的几率odds。如果模型估计出概率 p̂那么 odds 的MLE 就是 p̂/(1-p̂)你直接代入计算即可不用再跑一次优化。这个性质在实战中特别省事因为很多业务指标是参数的复杂函数比如金融领域的 VaR在险价值、可靠性工程中的MTBF平均故障间隔时间。只要参数估计出来了这些指标的MLE就是直接把估计值代入函数。3.2 相合性与渐近正态性相合性说的是当样本量 n 趋于无穷大时MLE 估计值会依概率收敛到参数的真实值。也就是“数据越多估计越准”这个性质让 MLE 在理论上成为可靠的大样本方法。渐近正态性更实用当 n 足够大时θ̂ 的分布近似为正态分布均值是真实 θ方差是 Cramér-Rao 下界也就是所有无偏估计量中可能达到的最小方差。这意味着在大样本下MLE 不仅准而且“有效率”——它的方差逼近理论极限。这个性质也直接导出了实践中常用的置信区间构造方法θ̂ ± z_(α/2) · se(θ̂)其中标准误 se(θ̂) 可以通过 Fisher 信息量来计算。Fisher 信息量就是对数似然函数二阶导数的负期望值直观理解是“似然函数在峰值附近有多尖锐”——越尖锐说明参数辨识度越高标准误越小。这就是为什么我们在论文里经常看到“基于MLE的置信区间”因为只要样本量够大不需要做复杂的模拟直接用正态近似就能给出足够准确的区间。3.3 为什么效率高与 Cramér-Rao 下界的关系Cramér-Rao 下界是一个理论基准。它告诉我们在满足一定正则条件时任何无偏估计量的方差都不可能低于某个值而这个值由 Fisher 信息量决定。MLE 在样本量趋于无穷时可以逼近这个下界也就是说在大样本场景下你很难找到比 MLE 方差更小的无偏估计量。这解释了为什么很多高级模型的默认估计方法都是MLE——不是因为它计算简单而是因为它的统计性质确实优秀。当然这不是说 MLE 在所有场景下都最好。如果你的样本量很小MLE 可能方差大、偏差大这时候贝叶斯估计加上先验或者收缩估计比如 James-Stein 估计可能表现更好。但如果你没有特别强的先验信息样本量又还可以MLE 是一个非常稳妥的选择。4. 当解析解不存在时数值求解方法4.1 logistic回归的MLE为什么没有闭式解到目前为止我们举的例子都能解出解析解但现实模型很多不行最典型的就是 logistic 回归。logistic 回归中每个样本属于类别1的概率为p_i 1/(1exp(-(β₀β₁x_i)))似然函数是各样本概率的乘积L(β) ∏ p_i^(y_i) · (1-p_i)^(1-y_i)取对数后你需要对 β 求导并令其为0。但问题是 p_i 本身是 β 的非线性函数求导之后得到的方程包含多项 exp 项不可能写成 β 某个表达式的形式。所以必须用数值方法迭代求解。这是一个在应用时经常让人困惑的点理论上MLE没问题但实际执行要靠优化算法。理解这点你就明白为什么机器学习库中训练逻辑回归模型底层都跑的是迭代优化而不是像线性回归那样直接算闭式解。4.2 牛顿法和梯度上升法求解MLE的数值方法有很多常用的两种是梯度上升法和牛顿法。梯度上升法适用于任何可微的似然函数。思路是沿着对数似然函数的梯度方向迭代更新参数β_new β_old α · ∇ℓ(β_old)其中 α 是学习率需要手动调节。太大容易震荡甚至发散太小收敛很慢。这个方法实现简单但收敛速度一般尤其接近最优值时可能出现来回震荡。牛顿法利用了二阶导数信息迭代公式是β_new β_old - [H(β_old)]⁻¹ · ∇ℓ(β_old)其中 H 是 Hessian 矩阵对数似然的二阶偏导。因为利用了曲率信息牛顿法通常收敛更快而且不需要调学习率。但代价是每步都要计算和求逆 Hessian 矩阵样本量大、特征多时计算成本很高。实际工程中还有各种变体拟牛顿法BFGS、L-BFGS、随机梯度下降SGD、Adam 等。它们本质上是在收敛速度、每步计算成本和稳定性之间做权衡。实操心得我平时做MLE数值求解首选 BFGS 或 L-BFGS因为它们不需要精确 Hessian 矩阵但保留了超线性收敛速度。除非是深度学习那种百万级参数的场景否则 L-BFGS 通常够用且稳定。4.3 实操注意事项数值求解MLE时有几个细节值得留意。第一加负号变最小化。大多数优化库默认做的是最小化所以你需要定义负对数似然函数然后交给优化器去最小化。这个过程在代码里很容易漏漏了结果会发散到无穷。第二梯度计算建议解析推导不要用数值差分。向前差分、中心差分在参数维度高时误差大、速度慢还可能因为步长选择不当导致优化过程不稳定。能用解析梯度就用解析梯度推导有困难时可以用自动微分比如 PyTorch、TensorFlow、JAX 自带的能力。第三对数域计算防止下溢。当样本量大时似然函数的值可能小到超出浮点数精度下限。比如一万个概率相乘每个0.5结果就是 0.5^10000 ≈ 10^(-3010)这在双精度浮点数下直接变成0。但取对数后就是 -3010完全没问题。所以永远不要直接乘概率必须取对数。第四初值选择很关键。多参数模型的目标函数往往有多个局部极值不同初值可能收敛到不同结果。常见做法是从多个随机初值启动优化选择似然最大的那个结果。也可以用矩估计作为MLE迭代的初值这样可以少走弯路。5. 用Python亲手实现一个MLE5.1 模拟数据与模型设定理论说再多不如跑一次代码。我们模拟一个正态分布数据然后用MLE反推出真实参数。假设真实分布是 N(μ2, σ²9)我们抽取 n200 个样本。然后写下负对数似然函数用 scipy.optimize.minimize 求解参数。import numpy as np from scipy.optimize import minimize # 设定随机种子结果可复现 np.random.seed(42) # 真实参数 mu_true 2.0 sigma_true 3.0 # 生成样本 n 200 data np.random.normal(mu_true, sigma_true, sizen) # 负对数似然函数 def neg_log_likelihood(params): mu, sigma params # 防止sigma出现非正数 if sigma 0: return 1e10 n len(data) ll -n/2 * np.log(2*np.pi) - n * np.log(sigma) - np.sum((data - mu)**2) / (2 * sigma**2) return -ll # 初值 init_params np.array([0.0, 1.0]) # 优化求解 result minimize(neg_log_likelihood, init_params, methodBFGS) print(真实参数mu , mu_true, sigma , sigma_true) print(MLE估计mu , result.x[0], sigma , result.x[1])这段代码跑出来的结果应该非常接近 mu2.0, sigma3.0。因为 BFGS 默认是拟牛顿法一般几十步就能收敛。5.2 构建负对数似然与优化步骤拆分一下。第一步定义参数向量。这里有两个参数 μ 和 σ我们把它们打包成一个向量 params。第二步写负对数似然函数。注意两点一是用 np.log 而不是 math.log因为后者不支持数组运算二是要加 sigma 0 的判断否则优化器可能在搜索过程中尝试负的sigma导致log(σ²)出错。第三步交给 minimize。method 我用的是 BFGS如果你用 L-BFGS-B 也可以后者还能加边界约束比如把sigma限制在(0, ∞)区间这样更保险。5.3 结果解读与对比跑完代码后你可以看看估计值和真实值的偏差并计算出标准误。标准误可以从优化结果的 hess_inv 中得到。# 用Hessian矩阵的逆估计标准误 hessian_inv result.hess_inv # scipy的BFGS结果中hess_inv是近似逆Hessian # 标准误 sqrt(diag(H^-1)) 的对角线 se np.sqrt(np.diag(hessian_inv)) print(标准误mu , se[0], sigma , se[1])这里的理论依据就是前面讲的渐近正态性在大样本下MLE估计量的协方差矩阵可以用负对数似然的Hessian矩阵的逆来近似。如果你求的是对数似然的最大化那对应的是负对数似然最小化所以 Hessian 的逆就是协方差矩阵的估计。在实际项目中我习惯把这段封装成一个函数输入数据和概率密度函数或对数概率密度函数自动返回参数估计、标准误和收敛信息。这样后续换模型只需要改密度函数就行。实操心得不要盲目相信 optimize 的默认收敛判断。我的习惯是同时输出梯度范数和迭代次数如果梯度范数接近0且迭代次数不多才算收敛正常。如果优化器达到最大迭代次数还没收敛先检查是不是初值问题再检查似然函数是否有 NaN。6. 实战中容易踩的坑6.1 多个极值点与初值敏感性前文提过很多模型的似然函数不是单峰的。以混合模型为例两个高斯混合的似然函数通常有多个局部极大值其中某些峰值对应的参数组合本身没有实际意义。解决思路有几种。一是全局优化方法比如模拟退火、遗传算法但速度慢。二是多重起点法随机生成几十组初值分别优化取似然最大的。这是实用中性价比最高的方案。三是为初值提供领域知识比如先用 KMeans 聚类结果作为混合模型参数的初始估计这会极大提升收敛到全局最优的概率。6.2 参数不可识别这是一个容易被忽略的陷阱。当不同参数组合产生完全相同的似然值时MLE 无法从数据中区分它们这时候估计结果没有唯一性。典型例子是线性回归中两个特征完全相关多重共线性。此时系数 β₁ 和 β₂ 可以一个增大一个减小但预测值完全不变似然也完全不变。这种情况下数值优化会变得不稳定Hessian矩阵接近奇异标准误会爆炸。处理方法通常是对参数施加约束、合并特征或者使用正则化如岭回归。在纯MLE框架下也可以用 profile likelihood把感兴趣的参数固定在一个网格上其他参数最大化观察似然剖面是否平坦来判断可识别性。6.3 样本量带来的偏差问题MLE 的优良性质大多基于大样本。小样本时MLE 可能偏差明显。拿正态分布方差估计来说n10 时 MLE 方差平均会低估约10%。如果 n5这个偏差会到约20%。所以当你手头样本量很小时可以考虑用无偏修正版本或者用 Bootstrap 来评估 MLE 的不确定性。Bootstrap 的做法是重采样数据对每个重采样样本重新做MLE得到估计值的分布。这比单纯依赖渐近正态区间更靠谱。6.4 数值稳定性技巧最后分享几个数值计算上的实用技巧。一是所有概率密度计算统一在 log 域操作最后如果需要概率再 exp 回去但通常不需要。二是在密度函数中加上极小值保护。比如计算正态分布密度时尾部的 exp 项可能下溢到0此时 log 会变成负无穷需要直接舍弃这项或返回一个非常大的负数。三是浮点数精度问题。当数据量很大时对数似然的值可能是 -1e6 量级优化器对这样大的值的梯度可能失去精度。常见做法是对对数似然除以样本量变成平均对数似然再优化。这样数值量级更稳定也不影响最优参数的位置。7. MLE与矩估计的选择我的一点使用体会7.1 矩估计是什么矩估计Method of Moments, MoM是比 MLE 更古老的方法。思路粗暴用样本矩代替总体矩然后解方程。比如正态分布总体一阶矩是 μ样本一阶矩是 x̄所以 μ 的矩估计就是 x̄。总体二阶矩是 σ²μ²样本二阶矩是 (Σx_i²)/n解方程得到 σ² 的矩估计。你看对正态分布来说矩估计和MLE结果一致。但对很多分布两者结果不一样。比如均匀分布 U(0, θ)矩估计解出来 θ̂_MoM 2·x̄而MLE解出来 θ̂_MLE max(x_i)。哪个更合理显然是 θ̂_MLE因为你观测到的最大样本值一定小于θ用它作为边界估计更贴近真实而2倍均值在数据分布偏斜时误差往往很大。7.2 两者对比| 维度 | 最大似然估计 (MLE) | 矩估计 (MoM) | |------|-------------------|-------------| | 求解难度 | 可能需要数值优化 | 通常直接解方程计算简单 | | 统计效率 | 大样本下逼近最优方差 | 一般效率较低 | | 小样本表现 | 可能有偏差 | 无偏性要看具体分布 | | 适用场景 | 数据分布明确、需要精确推断 | 快速估计、分布不明确时 | | 模型约束 | 需要写出似然函数 | 只需要写出矩条件 |7.3 选择建议我的经验是如果数据生成机制相对明确优先用MLE如果只是做初步探索或者分布假设难以验证矩估计能更快给出一个不算太离谱的结果。有时候两者可以结合比如用矩估计结果作为MLE迭代的初值既能利用矩估计的快速又能获得MLE的高效。这种做法在估计混合模型参数时特别有用。另外一个容易被忽视的点矩估计在某些分布下可能根本无解。比如 t 分布的自由度参数矩估计依赖于高阶矩存在性如果自由度小于2连方差都不存在矩估计直接失效。这时候MLE反而更稳健。最后再分享一个小技巧当你构建好一个MLE模型并准备用于业务决策可以顺手做一个简单的模型诊断。把估计参数代回模型重新模拟一批数据看模拟数据的关键统计量均值、方差、分位数是否与真实数据接近。如果差距明显说明模型假设可能有问题这时候问题往往不在MLE本身而是在于你选错了概率分布。MLE这个工具入门容易用明白需要时间。多动手推导几个分布多跑几次数值实验踩过几次坑之后你对它的理解会比看十篇教程都深刻。