这次我们来看一个不是模型、也不是工具,而是一套把“极大似然估计(MLE)”从零讲透的 manim 教学动画项目。它的价值在于:用动画把抽象的统计推导变成肉眼可见的推导过程,适合正在学机器学习和统计学的人,也适合想用 manim 做知识类动画的开发者。
这个项目最核心的几个特点如下:
- 使用 manim(3Blue1Brown 开源的教学动画引擎)编写,数学公式、函数图像、推导过程全部代码化,可复现、可修改。
- 讲解路径是“直觉 → 例子 → 公式 → 对数变换 → 求导 → 扩展”,不是直接扔出极大似然估计的定义。
- 渲染门槛低,不需要独立 GPU,普通办公电脑即可完成视频渲染。
- 支持命令行批量渲染多个场景,适合把一个完整教程拆成多个短视频分集。
- 输出为 MP4 视频,方便直接用于课程、B 站/公众号配图、个人知识库。
这篇文章会带你把 manim 环境装好、写出第一个 MLE 场景、把“硬币估计”这个经典例子拆成 6 个动画分镜、跑通渲染命令,最后解决中文字体、LaTeX、性能这些实际部署中一定会遇到的坑。看完之后,你既能复习一遍极大似然估计的核心推导,也能独立制作属于自己的数学动画教程。
1. 极大似然估计教学动画核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 数学概念可视化教学项目 |
| 动画引擎 | manim(3Blue1Brown 开源教学动画引擎) |
| 讲解主题 | 极大似然估计(MLE)从直觉到公式推导 |
| 运行环境 | 本地 Python 项目,全平台支持(Windows / macOS / Linux) |
| GPU 需求 | 无特殊要求,manim 默认使用 CPU 渲染动画 |
| 显存占用 | 无特定显存要求,主要看 CPU 与内存 |
| 启动方式 | manim 命令行渲染启动 |
| 接口 API | manim Python API,每个 Scene 类就是一个动画单元 |
| 批量任务 | 支持一次命令渲染多个 Scene |
| 输出格式 | MP4 视频(H.264 编码) |
| 适合场景 | 数学课教学、机器学习概念讲解、知识类视频制作 |
从规格表能看出来,这个项目最大的优势是门槛足够低:不需要高端显卡,只要 Python 环境和几个系统依赖就能跑。相比跑大模型动不动几十 GB 显存,manim 动画渲染几乎不构成硬件压力。
2. 适用场景与使用边界
极极大似然估计是机器学习和统计学的交汇点,理解 MLE 是理解线性回归、逻辑回归、朴素贝叶斯的基础。这个 manim 项目适合以下人群:
- 刚学机器学习,想搞清楚“为什么逻辑回归要最大化对数似然”的人。
- 正在做教学视频、课程 PPT,需要把推导过程做得足够直观的老师或内容创作者。
- 想学 manim 动画开发,拿一个相对完整的主题练手的人。
它能解决的问题也很明确:用视觉动画把“似然函数为什么长这样”“为什么要取对数”“为什么求导等于 0 就能得到估计值”这些问题讲清楚。动画的优势在于,你可以把一连串推导步骤分解成多个动效,观众不会被屏幕上同时出现的一大堆公式吓到。
使用边界同样需要说清楚:
- manim 输出的是预先渲染好的视频,不是交互式网页。如果你想做“可拖拽参数”的交互演示,应该去选 Plotly、Observable 或 Streamlit。
- 极大似然估计的严格数学证明,例如渐近正态性、一致性,动画并不能替代完整推导,只适合做直觉铺垫。
- 本项目的核心是“教学表达”,不是“算法库”。如果只是算 MLE,直接用 scipy.optimize 或 statsmodels 更高效,没必要渲染成视频。
版权与合规方面也提一句:manim 本身采用 MIT 协议,可自由使用与修改,但你自己制作教学视频时,使用的背景音乐、图标、字体、示例图片都要确认授权。教程中用到的示例数据,如果来自公开数据集,要保留原始来源说明。
3. manim 本地部署环境准备
在写完整分镜之前,先把环境搭好。这个步骤不需要太高理论门槛,照着清单检查一遍即可。
前置条件清单:
| 检查项 | 推荐配置 | 说明 |
|---|---|---|
| 操作系统 | Windows 10/11、macOS、主流 Linux 发行版 | manim 跨平台 |
| Python 版本 | 3.9 及以上 | 新版本 manim 要求 Python 3.9+ |
| 包管理器 | pip / conda | 二选一即可 |
| 系统依赖 | ffmpeg | 视频合成必须 |
| 可选依赖 | LaTeX(TeX Live / MiKTeX / MacTeX) | 渲染数学公式建议安装 |
| 磁盘空间 | 建议预留 5GB 以上 | 临时文件和输出视频需要空间 |
Python 环境准备建议使用虚拟环境,避免和其他项目的依赖冲突。在项目根目录执行:
python -m venv venvWindows 激活虚拟环境:
venv\Scripts\activatemacOS / Linux 激活虚拟环境:
source venv/bin/activate激活后安装 manim:
pip install manim安装完成后,确认 manim 是否可用:
manim --version如果输入manim提示命令找不到,但 pip 安装成功,说明当前 Python 的 Scripts 目录没有加入 PATH。这时可以改用模块方式调用:
python -m manim --versionffmpeg 的安装方式取决于操作系统。Windows 用户可以用 winget:
winget install ffmpegmacOS 用户用 Homebrew:
brew install ffmpegUbuntu / Debian 用户:
sudo apt update && sudo apt install ffmpegLaTeX 的安装体积比较大,但数学公式渲染离不开它。如果暂时不想装完整版,manim 允许你用纯文本Text替代MathTex,但效果会差不少。这里建议一步到位:公式动画是这个项目的核心,LaTeX 值得装。
装好之后,用下面的命令验证依赖是否齐全。先写一个最小可运行脚本opening_scene.py:
from manim import * class MaxLikelihoodOpening(Scene): def construct(self): title = Text("极大似然估计") self.play(Write(title)) self.wait(2)然后渲染:
manim -pql opening_scene.py MaxLikelihoodOpening-p表示渲染完成后自动打开预览,-ql表示低质量快速渲染,适合开发阶段调试。如果这个脚本能输出一个写着“极大似然估计”的视频,并且中文字体没有变方块,说明环境已经通了大半。下面就可以开始写正式的 MLE 分镜了。
4. 安装部署与第一个场景渲染
manim 渲染一个完整的教学视频,工程上建议把每个分镜拆成独立Scene类放在同一个文件里,然后用一条命令批量渲染。这样便于局部修改,不必整片重新渲染。
先看最小工作流的组成:
| 文件或目录 | 作用 |
|---|---|
mle_full.py | 存放所有分镜 Scene 类的 Python 脚本 |
media/ | manim 自动生成,存放输出视频和缓存 |
manim.cfg | 可选,统一配置输出目录和质量 |
一个最简单的 Scene 代码结构如下:
from manim import * class OpeningScene(Scene): def construct(self): title = Text("极大似然估计:从零讲透") subtitle = Text("Maximum Likelihood Estimation", color=BLUE) subtitle.next_to(title, DOWN) self.play(Write(title)) self.play(FadeIn(subtitle)) self.wait(2)渲染这个场景:
manim -pql mle_full.py OpeningScene如果要在项目根目录统一管理输出,可以创建manim.cfg:
[CLI] quality = medium output_dir = ./media/videos保存后再次运行渲染命令,产物会输出到media/videos/。再往后,你可以随时用-o参数指定输出文件名:
manim -qm mle_full.py OpeningScene -o 01_opening从部署角度看,manim 把“运行脚本 → 生成视频”封装成了非常标准的命令链路。你不需要关心Mobject内部是怎么逐帧绘制的,只需要把关注点放在每个分镜里画面的组织上。
5. 极大似然估计从零讲透:分镜与动画设计
整个教学项目的核心,是把极大似然估计的推导过程拆成分镜。下面这套分镜设计是经过教学实践验证的路径,覆盖了从直觉到公式再到扩展的完整过程。
| 分镜编号 | 场景类名 | 讲解主题 | 画面核心元素 |
|---|---|---|---|
| 1 | OpeningScene | 提出问题:抛硬币 10 次,7 次正面 | 硬币 Dot 布局、概率文字 |
| 2 | LikelihoodScene | 写出似然函数 L(p) = p^7(1-p)^3 | MathTex 公式 |
| 3 | LogLikelihoodScene | 为什么取对数 | 连乘 → 连加 的动画转换 |
| 4 | DerivativeScene | 求导、置零、求解 p=0.7 | 导数公式推导步骤 |
| 5 | GaussianScene | 从离散走向连续:正态分布均值的 MLE | 正态曲线 + 样本点 |
| 6 | SummaryScene | MLE 本质总结 | 核心结论逐条浮现 |
5.1 分镜 1:硬币实验开场
这个镜头要和观众建立直觉。画面上放一枚硬币,下面列出实验数据:抛 10 次,7 次正面,3 次反面。问题是在“不知道硬币是否公平”的前提下,你觉得正面概率 p 最可能是多少。
动画操作:
class OpeningScene(Scene): def construct(self): coin = Circle(radius=1, color=YELLOW) coin_label = MathTex("p").move_to(coin) experiment = Text("抛 10 次,正面 7 次", color=BLUE) experiment.next_to(coin, DOWN * 3) self.play(Create(coin)) self.play(Write(coin_label)) self.play(Write(experiment)) self.wait(2)这段代码不负责解决数学问题,只负责把“具体问题”摆在观众面前。为什么一定要从硬币开始?因为极大似然估计的“离散情形”最容易建立直觉。当观众接受了“正面概率是 p”这个设定,后面的似然函数水到渠成。
5.2 分镜 2:写出似然函数
这是整个项目的第一个关键推导点。从概率角度,如果正面概率是 p,那么“10 次中 7 次正面”的概率是:
class LikelihoodScene(Scene): def construct(self): formula = MathTex( r"L(p) = C_{10}^{7} p^{7} (1-p)^{3}" ) self.play(Write(formula)) self.wait(2) conclusion = Text("让这个概率最大的 p,就是最合理的估计") conclusion.next_to(formula, DOWN) self.play(FadeIn(conclusion)) self.wait(2)这里要注意一个细节:组合数 C(10,7) 在求导过程中是常数,不会影响最终的 argmax 结果。动画里可以先用完整的公式,然后在求导前做一个“常数项不影响最大化”的动作,把组合数淡化处理。这样逻辑链条更严谨。
5.3 分镜 3:为什么要取对数
这是极大似然估计教学中最容易略过、但最重要的动机部分。屏幕上先展示一般形式的似然函数:
class LogLikelihoodScene(Scene): def construct(self): likelihood = MathTex( r"L(\theta) = \prod_{i=1}^{n} f(x_i; \theta)" ) log_likelihood = MathTex( r"\ell(\theta) = \sum_{i=1}^{n} \log f(x_i; \theta)" ) log_likelihood.next_to(likelihood, DOWN) self.play(Write(likelihood)) self.wait(1) self.play( TransformMatchingTex(likelihood, log_likelihood) ) self.wait(2)TransformMatchingTex 是 manim 里处理公式变换的利器。它会把 L(θ) 变成 ℓ(θ),把连乘变成连加。观众能直接看到“log 把乘积变和”这个操作是怎样发生的。
6. 功能测试与渲染效果验证
项目写完后,不要急着直接渲染 1080p 正式版。先跑功能测试,确认每个分镜都符合预期。
功能测试维度:
| 测试项 | 测试目的 | 判定标准 |
|---|---|---|
| 基础渲染测试 | 验证 Slide 能否出片 | 生成 MP4 且长度符合预期 |
| 公式渲染测试 | 验证 LaTeX 表达是否正常 | 公式无解析错误、无缺失 |
| 中文字体测试 | 验证 Text 中文不乱码 | 中文正常显示,无方块 |
| 动画节奏测试 | 验证 wait 时长是否合适 | 画面不急促、不拖沓 |
| 批量渲染测试 | 验证多 Scene 一次出片 | 每个 Scene 独立生成视频 |
单个场景渲染测试命令:
manim -ql mle_full.py LikelihoodScene -o 02_likelihood渲染完成后,打开视频检查这几个点:
- 公式是否显示完整。如果出现
Missing character或空框,多半是 LaTeX 宏包缺失。 - 动画播放是否卡顿。如果画面有明显的瞬时跳变,可以加大
wait时间,或者把动画拆成更多步骤。 - 中文是否正常。如果中文显示为方块,需要用
Text(..., font=...)指定系统中文字体。
指定字体的示例:
title = Text("极大似然估计", font="Microsoft YaHei")macOS 可以换成font="PingFang SC",Linux 可以根据系统已安装字体调整。更通用的做法是:
from manim import Text title = Text("极大似然估计")配合系统字体配置,或者传入font_path指向具体字体文件:
title = Text("极大似然估计", font_path="/path/to/your/font.ttf")功能测试通过之后再跑正式渲染,能节省大量时间。
7. Python API 接口与多场景批量渲染
manim 的“接口”不同于 Web 服务的 HTTP API,它的编程接口是 Python 类和方法。这里有一个工程化思路:把每个分镜做成独立Scene,放在一个模块里,再用一条命令行批量产出。
批量渲染多个分镜:
manim -qm mle_full.py OpeningScene LikelihoodScene LogLikelihoodScene DerivativeScene GaussianScene SummaryScene命令执行后,manim 会按顺序渲染每一个 Scene,并在media/videos/mle_full/下生成对应名称的 MP4 文件。这样做的好处是,每个分镜视频都是独立文件,后续剪辑时可以自由组合顺序,不需要重新渲染。
如果你希望用 Python 脚本控制渲染过程,而不是依赖命令行,可以在Scene的construct中手动设置渲染参数:
from manim import config config.pixel_width = 1280 config.pixel_height = 720 config.frame_rate = 30然后编写一个更工程化的打包脚本render_all.py:
import subprocess scenes = [ "OpeningScene", "LikelihoodScene", "LogLikelihoodScene", "DerivativeScene", "GaussianScene", "SummaryScene", ] for scene in scenes: subprocess.run( ["manim", "-qm", "mle_full.py", scene], check=True )这种脚本方式适合把整个教程纳入 CI/CD 流程。比如每次修改脚本后自动批量渲染,或者通过配置文件维护分镜顺序。
8. 资源占用与渲染性能观察
manim 是 CPU 渲染为主的应用,对显卡没有硬依赖,这和跑深度学习模型完全是两回事。但它的性能瓶颈依然存在,主要集中在 CPU 峰值能力和内存占用上。
| 观察维度 | 观察方法 | 说明 |
|---|---|---|
| CPU 使用率 | Windows 任务管理器 / Linux top 或 htop | 渲染时多核会持续高负载 |
| 内存占用 | 任务管理器 /free -h | 场景 Mobject 越多,内存占用越高 |
| GPU 占用 | 基本可以忽略 | manim 默认不调用 CUDA |
| 渲染时长 | 对比-ql/-qm/-qh三档 | 质量越高耗时成倍增加 |
| 磁盘占用 | 查看media/videos/目录 | 长视频和 4K 渲染对磁盘压力更大 |
manim 的渲染质量参数:
| 参数 | 分辨率 | 帧率 | 适用阶段 |
|---|---|---|---|
| -ql | 854x480 | 15 | 开发和分镜预览 |
| -qm | 1280x720 | 30 | 日常测试、快速发布 |
| -qh | 1920x1080 | 60 | 正式教学视频 |
| -qk | 3840x2160 | 60 | 需要超清输出的场景 |
一个常用的经验是:开发阶段统一使用-ql或-qm,正式发布前才用-qh渲染一遍。如果你在manim.cfg里把 quality 设置为 high,每次测试都会很耗时,所以建议至少准备两套配置,或者在命令行显式指定质量参数。
如果 CPU 较弱,帧数高会导致渲染时长明显增加,可以考虑把config.frame_rate降到 30 来平衡画质与速度。画面中存在大量复杂几何运算的变换,渲染时间也会明显增长,这时可以把一个长动画拆成多个短动画。从教学效果来说,3 到 5 秒一个动效,观众理解压力也小得多。
9. 常见问题与排查方法
下面是使用 manim 制作极大似然估计教程时最容易遇到的问题,按概率从高到低排列。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
提示manim: command not found | manim 安装成功但 Scripts 目录不在 PATH | 执行python -m manim --version验证 | 改用python -m manim运行,或将 Python Scripts 目录加入 PATH |
渲染报错LaTex not found | 系统未安装 TeX 发行版 | 在命令行执行latex --version | 安装 TeX Live / MiKTeX / MacTeX,或者暂时用Text代替MathTex |
| 中文显示为方块 | 系统没有匹配的中文字体 | 检查Text渲染字体 | 在Text中指定font或font_path |
| 渲染速度慢 | 一直用-qh或-qk在调试 | 看耗时是否与质量参数强相关 | 开发用-ql,正式发布用-qh |
| 画面对象重叠混乱 | 没有组织 Mobject 位置 | 截图定位重叠对象 | 使用next_to、arrange、VGroup规划布局 |
| 视频无声音 | 数学动画默认不生成音轨 | 用播放器查看音轨信息 | 后期用剪辑软件加配音、音乐 |
| 批量渲染只出了一个视频 | 命令行 Scene 类名写错了 | 检查类名与 class 定义是否完全一致 | 用空格分隔多个 Scene 类名 |
| 公式渲染有缺失字符 | LaTeX 宏包缺失 | 查看终端日志中的 LaTeX 报错 | 在MathTex中使用tex_template补充宏包 |
最常踩的坑是环境和字体。如果你只是照着网上的旧教程装过 manim,记得确认版本不是始祖版。直接用新版pip install manim即可,不需要额外安装manimlib。
如果碰到公式解析问题,比如某个命令在 LaTeX 中无效,可以先把公式放到独立 MathTex 里测试,确认没问题再合并到复杂场景中。这种单点测试的思路,和大模型部署中“先最小化复现再排查”的思路是一致的。
10. 最佳实践与使用建议
最后给一套工程化建议,适合真正要把这个项目做成系列视频的人。
第一,先写分镜表再写代码。不要边写代码边想内容。分镜表至少包含“画面元素、旁白内容、屏幕文字、时长”四列。极大似然估计这个主题看似简单,但一旦拍脑袋开工,很容易出现公式顺序错乱的问题。先花半小时把 6 个分镜的文字稿写出来,再动手写 Scene,效率能提升一倍。
第二,固定使用低质量参数预览。开发阶段一直用-ql,把动画节奏和位置调准。确认没有问题后,最后用-qh渲染正式版。这样既能保证修改效率,也不会浪费大量 CPU 时间。
第三,场景文件按模块拆分。分镜多了以后,建议按目录组织:
mle_tutorial/ scenes/ __init__.py opening.py likelihood.py log_likelihood.py derivative.py gaussian.py summary.py media/ videos/ manim.cfg render_all.py代码维护方面,注意 manim 的 API 在不同版本间有变化。如果你在旧版本项目中看到TextMobject、VGroup等写法和新版本不同,直接按新版本语法调整即可。写完后运行一次渲染测试,确保每个场景都能稳定出片。
第四,批量任务一定要加日志和失败重试。manim 渲染虽然不像大模型推理那样容易随机失败,但批量渲染 20 个分镜时,场景中某个临时素材缺失会导致任务中断。render_all.py里给subprocess.run加异常捕获,渲染失败时记录日志并继续下一个场景。
import subprocess scenes = ["OpeningScene", "GaussianScene"] for scene in scenes: result = subprocess.run( ["manim", "-qm", "mle_full.py", scene], capture_output=True, text=True ) if result.returncode != 0: with open("render_error.log", "a", encoding="utf-8") as f: f.write(f"Scene {scene} failed\n") f.write(result.stderr) print(f"{scene} 渲染失败,已记录日志") else: print(f"{scene} 渲染完成")自动清理临时文件也有必要。manim 会在渲染过程中生成大量中间文件,学习测试阶段体积不大,但系列教程做到第十集以后,media/目录会膨胀到几十 GB。建议每完成一集,清空一次缓存,只保留最终视频。
第五,素材与授权要严格遵守边界。用 manim 做极大似然估计教学,一般不需要外部图片,但如果你计划给视频配背景音乐、使用特定字体,务必确认授权范围。涉及公开数据集的案例,也要在视频说明中标注来源。
最后,合规提醒必须放在心里反复强调:制作教学视频发布到公开平台,建议用自己独立绘制的图表素材;如果引用第三方材料,必须获得授权并注明出处。这既是版权要求,也是对读者负责。
对大多数人来说,这个项目最值得尝试的点在于:用一套代码同时完成“学 MLE”和“学 manim”两件事。最先验证的功能是-ql渲染链路和中文公式显示。最容易踩的坑是 LaTeX 缺失和字体丢失。后续想扩展,可以把线性回归、逻辑回归、朴素贝叶斯的推导也用同一套路做成系列动画,互相复用分镜模板和渲染配置。建议收藏备用,动手做一个属于自己的分镜时再翻回来对照。