
第一次接触极大似然估计时很多人都会有同样的困惑公式里又是连乘又是取对数最后还要求导看起来像一道复杂的微积分应用题。但等你真正走进这个场景会发现它本质上解决的是一个特别朴素的问题——我手上有一堆观测数据我想反推这些数据最可能来自什么样的规律。“极大似然估计”Maximum Likelihood Estimation简称 MLE就是做这件事的标准框架。而 manim 这个数学动画引擎最近在学习和教学圈子里被反复提起是因为它特别适合把抽象推导变成“看得见的过程”。两个东西放在一起刚好能组成一条从“知道公式”到“真正理解”的路径。这篇文章我不打算只复述数学课本上的推导而是想带你重新走一遍从抛硬币问题出发理解似然的含义再慢慢拆开为什么取对数、为什么求导、为什么有时候解不出来最后讲清楚怎么用 manim 做辅助教学或自学动画。这是一套从概念到落地的方式不是单纯讲公式。1. 极大似然估计到底在做什么它不是公式是一次反推1.1 从一个硬币实验开始理解“谁最有可能”假设我拿了一枚不知道是否公平的硬币连续抛了 10 次结果是 8 次正面、2 次反面。你现在要猜这枚硬币正面朝上的概率 p 是多少。直觉上大多数人会说 p 大概等于 0.8。为什么因为在独立重复实验里观察到的频率是 8/10那么真实概率就应该接近这个比例。但这里面其实暗含了一个很重要的判断逻辑我们不是先知道概率再去预测结果而是先有了结果再反过去找“最能解释这个结果的概率”。这个过程就是似然。更具体地说对于某个假设的 p比如 p0.5计算出“抛 10 次出现 8 正 2 反”的概率是多少再假设 p0.8同样计算这个概率是多少。哪个 p 让当前结果出现的概率更大哪个 p 就更“像”真实概率。极大似然估计做的就是在所有可能的 p 取值里找出那个让当前观测数据出现概率最大的值。这个思维框架跟平时概率题不一样。概率题通常是“已知 p算某件事发生的概率”而极大似然估计是“已知观测结果反推 p 是多少”。一个是正向的计算一个是反向的推断。方向一变整个问题的性质就不同了。1.2 似然函数和概率函数字母相同含义不同课本里经常会写 L(θ|x) 或者 L(θ)有时候还会和概率质量函数 P(x|θ) 混在一起。其实这两个函数写出来可能都是同一个式子但看它的视角完全不同。概率函数 P(x|θ) 是把参数 θ 当作固定已知去描述随机变量 x 取不同值时的概率分布。比如 θ0.5那么 x 出现 4 次正面的概率是多少这就是概率函数。而似然函数 L(θ|x) 刚好反过来它把观测到的 x 当作固定已知然后去看不同 θ 取值下这个固定 x 出现的可能性大小。这里用“可能性”而不是“概率”是因为 θ 不是随机变量它只是一个未知的固定参数我们不能说 θ0.8 的概率是多少只能说在这个参数下观测数据出现的相对可能程度。这个区别不是咬文嚼字。它决定了我们后面怎么使用这个函数。极大似然估计的目标就是找一个 θ让 L(θ|x) 最大。因为 L 和 P 在数值上共享同一个形式所以求极大似然估计实质上是在参数空间里搜索一个点让这个点对应的概率值最大。明白了这一层再看公式就不会觉得只是符号游戏。它是在用一个非常反直觉的方式处理不确定性概率是给结果打分似然是给参数打分。2. 从零推导极大似然估计为什么求导取对数会成立2.1 写似然函数把样本独立事件的概率乘起来先从一个最简单的例子出发。假设我们有 n 个独立同分布的样本 x1, x2, ..., xn都服从某个分布 f(x;θ)其中 θ 是未知参数。因为样本独立所以同时观察到这一组样本的概率密度或概率质量就是每个样本概率的乘积这就是似然函数L(θ) ∏ f(xi;θ), i1..n如果是伯努利分布比如抛硬币正面概率是 p那么一次观察出现正面就对应 f(1;p)p出现反面就是 f(0;p)1-p。于是一组 8 次正面、2 次反面的观测似然函数就是L(p) p^8 · (1-p)^2这个时候你其实已经有了一个关于 p 的函数。你可以画一条曲线横轴是 p 从 0 到 1纵轴是 L(p)。你会发现曲线在某个点达到最高这个最高点对应的 p 就是我们要找的极大似然估计。这里有一个容易忽略的前提我们为什么可以连乘因为样本独立。如果样本之间不独立连乘就不成立后面的推导也全部作废。所以写似然函数之前最好先确认“独立同分布”这个假设是不是合理。2.2 取对数不是为了“方便求导”这么简单实际推导时我们通常不会直接对 L(θ) 求导而是先取对数得到对数似然函数ℓ(θ) log L(θ) ∑ log f(xi;θ)很多教材只解释一句“为了把连乘变成连加方便求导”。这个说法没错但不完整。取对数真正重要的一点是它保持了函数的凹凸性不变。因为对数函数是严格单调递增函数所以能让 L(θ) 最大的 θ 值也一定能让 log L(θ) 最大。也就是说极大化对数似然和极大化原始似然是等价的。那为什么要换成对数原因有三个层面数学生成上乘法的求导法则是加法求导法则不能比的。对数把连乘转成了连加求导时就可以把每一项分别处理公式复杂度显著下降。数值上连乘几十个 0.1 级别的概率会得到一个极小的数比如 10^{-10} 甚至更小。计算机处理这种数容易出现浮点数下溢在对数空间里这个值会变成 -10 甚至 -20完全在安全范围。优化上很多数值优化算法是基于梯度做搜索。对数似然的梯度形式往往比原始似然更稳定尤其在观测样本比较多的场景里。不过要提醒一点如果你只是手算推导可能感觉不到取对数的好处甚至觉得多此一举。但一旦到了用代码实现参数估计的时候取对数基本是必须的。这是数学表达和工程实现的共同需求。2.3 求导找极值什么时候用闭式解什么时候用数值优化取完对数后接下来的标准操作是求导令导数为零解出参数估计值。以我们的硬币例子为例ℓ(p) 8 log p 2 log(1-p)对 p 求导并令其为零8/p - 2/(1-p) 0解得 p 8/10 0.8。这刚好和直觉一致。这种能直接通过解析表达式求出的解叫闭式解closed-form solution。但很多实际分布做不到这一点。比如正态分布的两个参数 μ 和 σ²虽然单参数情况下可以用偏导数解出来但如果是更复杂的模型比如逻辑回归中的参数就没有闭式解只能靠梯度下降、牛顿法等数值优化方法去逼近极大值。所以学极大似然估计不能只学“求导解方程”这一个套路更要建立起一个判断这个模型能不能直接解出来如果解不出来用什么样的优化方法在工程中很多所谓的“极大似然估计”其实是通过优化器迭代出来的近似解。这里又引出一个边界如果似然函数本身只有一个峰那么数值优化很容易找到最大值但如果有多个局部极大值优化结果就依赖于初始值选择甚至可能收敛到错误的地方。所以不能只看最终输出值还要检查似然函数区域的整体形状。3. 用 manim 把“似然函数变化”画出来教学设计而非炫耀3.1 为什么用 manim它把静态公式变成动态推理manim 是一个用于制作数学动画的 Python 引擎最出名的是的数学解释视频。你不需要成为动画设计师只需要用 Python 代码描述数学对象的移动、变换、出现和消失manim 就会渲染成一段流畅的视频。最近想学 manim 的人越来越多因为它确实能把很多“只可意会”的数学推导外化成视觉过程。但我更想强调的是manim 的价值不是做酷炫特效而是帮助你或你的观众真正看见“变化”。在极大似然估计这个例子里有非常多的动态关系随着 p 从 0 变化到 1似然函数 L(p) 的曲线如何起伏当样本量 n 从 10 变成 100曲线峰值位置是否稳定当观测结果从 8 正 2 反变成 50 正 50 反估计值如何向 0.5 靠近取对数前后的函数形状有什么不同极大点是否保持一致。这些关系用文字写出来很容易但看文字不会留下直觉。让曲线动起来看它随着参数变化而变化这个直觉就会慢慢长出来。3.2 一个最小可用的动画脚本结构manim 脚本有固定结构。以当前官方文档的写法为例通常是定义一个继承自 Scene 的类然后在 construct 方法里写画布上的对象。如果我做一个类似“不同 p 值对应的伯努利似然函数曲线”的动画结构会大致长这样from manim import * class LikelihoodCurve(Scene): def construct(self): # 坐标轴 axes Axes( x_range[0, 1, 0.1], y_range[0, 0.05, 0.01], x_length6, y_length4 ) # 先画一个函数曲线后面再让它变化 graph axes.plot(lambda p: p**8 * (1-p)**2) self.play(Create(axes), Create(graph)) self.wait()这只是一个示意不是可以直接抄走跑的完整代码。因为我不能确定你本地的 manim 版本、Python 版本和渲染后端。但你可以从这种最小结构出发先画静态曲线确认坐标轴和函数没画错再做动态参数改变。如果你抓到了某段上游项目标题里的源代码材料建议以实际代码为准。因为 manim 的 API 在不同版本里变化较大比如 Lower 级别函数名、坐标轴参数写法、甚至类名都调整过。写代码前先锁定版本能少踩很多坑。3.3 从单参数到多参数动画要突出什么单参数 p 的动画相对简单因为横轴就是参数纵轴就是似然值。你可以在动画里同时展示样本点分布和似然曲线让观众看到数据生成过程与参数搜索过程的对应关系。多参数的情况要复杂很多比如正态分布的 μ 和 σ。你没办法同时直观展示三维曲面和样本变化。这时我建议不要画完整三维曲面而是采用“切片法”固定 σ画出关于 μ 的对数似然曲线然后让 σ 取不同值再画一组曲线叠加。观众会看到不同 σ 下曲线的峰值位置和尖锐程度在变化这比一张三维图更容易理解。另外一个很实用的动画策略是“对比展示”。把原始似然曲线和取对数后的曲线放在同一坐标系里用不同颜色区分。虽然两者的纵轴量纲不一样但你可以把两个函数各自做归一化让观众看到它们的最高点在同一直线上。这个动画能非常直观地解决很多人的疑问为什么 log 不影响极值点。注意manim 动画制作的常见问题不是“不够炫”而是“重点太多”。一段动画里只讲一个关系不要既画样本、又画曲线、又叠加积分区域。视觉过载会让理解变慢而不是变快。4. 真正要讲透极大似然估计还需要补上这些边界4.1 MLE 不是万能的模型选择与过拟合很多人学完极大似然估计会觉得它是个“安全万能”的参数估计方法。但实际使用中它的成立需要几个假设支撑样本独立、分布假设正确、参数可识别、样本量不能太小。先说分布假设。如果真实数据分布根本不是你所猜测的伯努利分布或正态分布那么极大似然估计只能在你设定的模型类里找到一个“相对最好”的参数它无法告诉你这个模型本身就错了。举个例子如果数据其实是双峰的你却用单峰正态分布去做估计MLE 算出的 μ 可能落在两个峰之间完全反映不了真实结构。这不是 MLE 的计算问题而是模型设定问题。再说样本量。虽然极大似然估计在很多情况下具有一致性——样本量越大估计得越准——但小样本时它可能出现明显偏差。比如抛 2 次硬币都是正面MLE 给出的 p 就直接是 1.0。这个结果很符合极大似然的逻辑却完全不合常理。如果有先验知识你可能更愿意用贝叶斯估计或加入正则化而不是直接接受 p1.0。所以在实际项目中MLE 只解决参数估计问题它不解决模型选型问题。你需要自己保证分布假设、数据质量和样本代表性。这一点和机器学习里的过拟合很像如果模型太灵活MLE 会完全拟合噪声如果模型太简单又会有系统性偏差。4.2 常见误区不是每个 MLE 都有唯一解极大似然估计存在几个现实中经常遇到的坑。第一无解。当似然函数在参数空间内没有最大值时比如参数趋向边界导致似然函数单调上升MLE 可能收敛到边界值而不是一个内部点。前面硬币例子里 p 的上界是 1如果观测全是正面MLE 就是 p1这本身合理但往往不是我们想要的估计。第二多解。有些模型的似然函数存在多个峰值比如混合模型中不同成分参数交换会给出同样的似然值。这时 MLE 的结果不代表唯一可识别而是多个参数组合在似然意义上等价。要做可识别性分析或通过约束让参数唯一化。第三数值不稳定。当参数数量很多或者对数似然面很平坦时优化算法可能走得很慢或者停在某个平坦区域。这不是求导公式错了而是数值优化的固有挑战。解决办法包括更精细的初始化、正规化、或者改用带约束的优化方法。这些边界听起来有点打击人但很必要。因为单学理论推导时公式总是按要求成立但真实数据从来没有严格按照假设生成过。学会 MLE不只是会套公式还要会判断什么时候可以用什么时候它给出的结果要打问号。4.3 从理论到落地极大似然在真实项目中的位置在真实系统里极大似然估计不是一个独立步骤而是概率模型建模链条中的一环。常见的落地流程是这样定义问题你想预测的变量是什么选择概率分布模型根据变量类型和生成过程确定用伯努利、正态、泊松还是更复杂的模型。写出对数似然函数。用优化算法求参数极大值。检查结果的合理性参数是否在预期范围似然值是否稳定做模型评估用验证集或交叉验证看预测能力。很多机器学习的算法本质就是在做极大似然估计。比如逻辑回归的损失函数是被设计成负对数似然的最小化损失就是最大化对数似然。理解了 MLE再去看那些损失函数你会觉得它们的来历清楚很多。但要注意落地时还有一个工程点数据量太大时完整计算所有样本的对数似然会非常费时于是出现了小批量随机梯度下降、增量估计等方法。它们本质上不是在计算精确的 MLE而只是在近似它的方向。你写的日志里如果能区分“完整似然”和“近似似然”排查问题时会更容易精准定位异常。5. 从“看懂”到“讲透”一套可复用的教学/学习框架5.1 四步讲透法场景、公式、动画、边界如果你要用 manim 去讲极大似然估计或者只是自己做一个学习总结我推荐一套四步框架。这个框架同样适用于其他数学概念。第一步用真实场景建立直觉。不要一上来就甩定义。先给出一个硬币实验或者一个“根据历史订单量估计需求增长率”的问题。让观众先能猜答案比如猜 p 大概是 0.8。这样他们会主动想知道怎么用数学证明这个直觉。第二步用公式把直觉形式化。从似然函数开始逐步推到对数似然和求导。过程中不断强调每一步的操作都没有改变“最大点”的位置只是在换个更舒服的计算方式。这里适合用 manim 把曲线和公式同步显示当函数曲线移动到最高点时把“估计值”标注出来。第三步用动画把时间维加进来。把参数变化引起的曲线变化展示出来比如样本量从 10 变成 1000 时峰值位置越来越稳定。这是静态图无法传达的信息也是 manim 最有价值的地方。第四步用反例划定边界。告诉观众 MLE 什么时候会失灵比如小样本、模型错误、多峰似然。可以把一个错误模型下的估计结果画出来让他们看到 MLE 也会给出很离谱的答案。这套框架可以广泛应用于生成模型、衡量指标、优化目标等主题。核心原则只有一个先给直觉再给公式再给动态过程最后给边界。5.2 制作 manim 动画时最容易踩的坑我见过很多人刚开始学 manim 时第一个项目就想直接复刻复杂视频结果被渲染时间、版本兼容和代码结构劝退。这里有几个非常实际的提醒。不要一上来追求 3D 和复杂变换。极大似然估计这种主题更适合 2D 坐标轴动画3D 曲面反而会增加视角转换的认知负担。确认你用的是哪个 manim 发行版。社区版community edition和原来的 3b1b 版 API 差别很大网上很多教程代码不能直接照抄。最稳妥的办法是先跑官方示例确认环境正常再改代码。动画帧率不要设置过高。常用的 30 FPS 或 60 FPS 都行但高帧率会让渲染时间成倍增加。对讲解类视频30 FPS 已经完全足够。文字和函数同时出现时注意留白。不要让公式覆盖曲线也不要在同一画面塞太多信息。manim 的优势是让对象按顺序出现而不是全部同时堆积。如果你在写代码时遇到函数形状不对先检查坐标轴范围。很多初学的错误不是函数本身错了而是 x 轴和 y 轴刻度范围设置不当导致曲线被拉伸得看起来很怪。把范围先设成和参数取值范围一致曲线形状通常就正常了。5.3 排查思路动画不报错但效果不对怎么办这一步专门给那些已经开始写 manim 脚本、但渲染出来的视觉效果和预期不符的开发者。排查顺序比直接找代码问题更重要。第一先区分是数学错误还是动画错误。你可以在动画代码之外单独用 matplotlib 或直接在终端打印函数在几个关键点的取值确认这个函数本身的形状。如果函数是对的再去看 manim 的坐标变换。第二检查坐标轴与函数的空间映射。manim 的坐标轴默认有自己的渲染坐标你在 axes.plot() 里传的是数学函数但实际显示结果依赖坐标轴范围。如果函数峰值在 p0.8但画面里曲线的最高点不在 0.8 附近大概率是 x_range 设置不对或者数据点采样太稀疏。第三检查 Play 语句的逻辑顺序。manim 是按时间播放一系列动画动作的如果你希望“曲线随 p 变化”而不是“一条静态曲线出现”你需要用 ParametricFunction 或者不断更新函数对象而不是一次 plot。不同版本更新方式不同建议先查文档再动手。第四看渲染日志和输出帧。manim 渲染时会产生很多日志有时警告信息里藏着原因。比如采样点不足、对象超出画面范围都会有提示。切忌只看最终 mp4 就问“为什么不灵动”先看日志定位具体是哪一帧出问题。排查时先缩小问题范围先画一条静态直线再画一条静态曲线再让它动起来。每步都确认正常后再合并功能能节省大量调试时间。6. 写在最后从这条曲线里学到的不只是参数回过头来看极大似然估计这条曲线——似然函数之峰——其实刻画了一种非常底层的思维方式所有模型都是我们对世界的猜测数据则是裁判。我们不是在“找到真相”而是在数据给定的条件下挑选一个最不坏的解释。manim 的价值则在于把思考过程显形。当你看到曲线随样本量收敛、随参数变化起伏时你会有一种确凿的直观而这种直观会在你写代码、调模型、看损失曲线时反复回响。所以如果你是学生先别急着背推导过程试着用这个框架理解一次。如果你是开发者试着做一个最小的 manim 动画把自己最近理解的一个模型参数可视化。你是可以看到的那个“由数据倒推规律”的过程其实不太不抽象。