1. 从零散笔记到体系化知识库的整理思路
1.1 为什么选择在这个时间点做完整梳理
机器学习这门课,很多人都是“看的时候懂,合上书就忘”。我自己在学这门课的时候,前前后后刷了三遍讲义,第一遍是跟着视频走马观花,第二遍是配合作业硬啃公式,第三遍才真正把概率图模型、指数族分布、广义线性模型这些概念串起来。但真正让我产生“必须把笔记整理出来”这个念头的,是某次给一个刚入门的同学讲梯度下降的推导,讲到一半发现自己对矩阵求导的某个细节记忆模糊了,翻回原来的笔记,发现当时只记了结论,没记推导过程。
这件事让我意识到,零散的笔记和体系化的知识库之间,差的不只是排版,而是知识之间的连接关系。你记下“牛顿法收敛快但计算量大”,和你能说清楚“为什么牛顿法在海森矩阵接近奇异时会失效、这和条件数的关系是什么”,完全是两个层次的理解。所以这次整理,我的目标不是把讲义抄一遍,而是把每个算法背后的动机、假设、推导链条、适用边界全部打通。
另一个触发点是,我发现网上很多笔记要么是纯公式堆砌,要么是纯直觉解释,很少有把“数学推导”和“工程直觉”结合得比较好的。比如逻辑回归的损失函数,从最大似然推导出来是对数损失,从信息论角度是交叉熵,从凸优化角度它又是光滑可导的——这三个视角其实说的是同一件事,但分开看就容易割裂。我想做的,就是把这些视角在笔记里显式地连起来。
1.2 整体架构:三层结构的设计逻辑
整理之前我先定了一个框架,把整个课程内容分成三层:
- 基础层:线性代数回顾、概率论基础、凸优化入门。这部分不是课程重点,但如果不扎实,后面寸步难行。
- 核心层:监督学习(线性回归、逻辑回归、广义线性模型、生成学习算法、核方法、SVM、决策树、集成方法)、无监督学习(K-means、GMM、EM算法、PCA、ICA)、学习理论(偏差方差分解、VC维、正则化)。
- 进阶层:强化学习基础、概率图模型、变分推断初步。
这个分层的好处是,复习的时候可以按需跳转。比如你只想搞懂SVM,那就直接从核心层进去,遇到拉格朗日对偶不懂再回基础层补。而不是像看视频那样必须从头到尾线性推进。
每一层的笔记我采用统一的模板:
算法名称→解决什么问题→核心假设→数学推导→优化方法→复杂度分析→常见变体→踩坑记录
这个模板是我试了好几种之后定下来的。最早我按“定义-公式-例子”来记,后来发现缺少“解决什么问题”这一环,导致过段时间就忘了为什么要学这个算法。加上“核心假设”之后,很多算法的局限性就一目了然了——比如线性回归假设噪声是高斯分布且同方差,一旦数据有异方差性,普通最小二乘就不是最优的了。
1.3 工具选型:为什么最终用了Markdown加LaTeX
工具这块我折腾了不少时间。最早用Word,公式编辑慢,版本管理混乱;后来试过Notion,在线编辑方便但导出PDF时公式渲染经常出问题;再后来用Jupyter Notebook,代码和公式能混排,但纯数学推导的排版不够清爽。
最终定下来用Markdown + LaTeX的组合,理由有几个:
- 纯文本存储:Git可以管理版本,每次修改都有记录,不怕丢。
- 公式渲染质量高:LaTeX的数学排版是学术界标准,矩阵、多行公式、对齐环境都很成熟。
- 跨平台:任何编辑器都能打开,不依赖特定软件。
- 导出灵活:用Pandoc可以转PDF、HTML、Word,分享给不同需求的人。
具体配置上,我用VS Code加Markdown All in One插件,配合LaTeX Workshop做预览。数学公式用$...$行内和$$...$$块级,矩阵用\begin{bmatrix}...\end{bmatrix}。这里有个小技巧:多行公式对齐用\begin{aligned}...\end{aligned}而不是eqnarray,后者在部分渲染器里会有间距问题。
注意:如果你也用Markdown记数学笔记,建议在文件开头加一个
\newcommand的宏定义区,把常用的符号(比如\R表示实数集、\E表示期望、\argmin等)统一定义,后面写起来会快很多,而且全局替换也方便。
2. 核心内容的深度拆解与实操要点
2.1 监督学习部分的推导链条怎么记才不忘
监督学习是这门课的重头戏,也是笔记量最大的部分。我的经验是,不要孤立地记每个算法,而是抓住“从假设到损失到优化”这条主线。
以线性回归为例,完整的推导链条是这样的:
- 假设:$y = \theta^T x + \epsilon$,其中$\epsilon \sim \mathcal{N}(0, \sigma^2)$。
- 似然:$p(y|x;\theta) = \frac{1}{\sqrt{2\pi}\sigma}\exp\left(-\frac{(y-\theta^Tx)^2}{2\sigma^2}\right)$。
- 最大似然:取对数后,最大化似然等价于最小化$\sum(y^{(i)} - \theta^Tx^{(i)})^2$。
- 优化:对$\theta$求导令为零,得到正规方程$\theta = (X^TX)^{-1}X^Ty$。
- 概率解释:为什么最小二乘是合理的?因为高斯噪声假设下,最大似然就是最小二乘。
这条链条记清楚了,后面逻辑回归就是换一个假设:$y \in {0,1}$,用Sigmoid函数把线性输出映射到概率,然后从伯努利分布的最大似然推出交叉熵损失。再往后,广义线性模型就是把这两者统一起来:指数族分布 + 自然参数 + 链接函数。
我在笔记里专门画了一张表,把线性回归、逻辑回归、Softmax回归放在一起对比:
| 算法 | 输出类型 | 分布假设 | 链接函数 | 损失函数 |
|---|---|---|---|---|
| 线性回归 | 连续值 | 高斯分布 | 恒等函数 | 均方误差 |
| 逻辑回归 | 二分类 | 伯努利分布 | Sigmoid | 交叉熵 |
| Softmax回归 | 多分类 | 多项式分布 | Softmax | 交叉熵 |
这张表一摆出来,广义线性模型的“广义”在哪里就清楚了——分布假设不同,链接函数不同,但优化框架是一样的。
2.2 生成学习算法与判别学习算法的本质区别
这部分是我当时学的时候最困惑的地方。高斯判别分析(GDA)和逻辑回归,明明都是分类,为什么一个叫生成一个叫判别?
后来我想明白了一个类比:判别模型是直接学“边界”,生成模型是先学“每个类别长什么样”,再用贝叶斯定理推边界。
具体来说,GDA假设$p(x|y)$是高斯分布,$p(y)$是伯努利分布,然后通过贝叶斯定理计算$p(y|x)$。而逻辑回归直接建模$p(y|x)$。有趣的是,GDA的假设(高斯且协方差相同)比逻辑回归更强,所以当假设成立时,GDA需要更少的样本就能达到同样的效果;但当假设不成立时,逻辑回归更稳健。
我在笔记里记了一个实操心得:如果你对数据的分布有很强的先验知识,用生成模型;如果你只关心分类边界,用判别模型。比如文本分类里,朴素贝叶斯(生成)在小样本上往往比逻辑回归表现好,就是因为词分布的假设(条件独立)虽然不准确,但在小样本下反而起到了正则化的作用。
2.3 核方法与SVM:从对偶到核技巧的完整推导
SVM是这门课里数学最密集的部分之一。我的笔记分了四步来记:
第一步:最大间隔的原始问题。我们要找一个超平面,使得最近的点到它的距离最大。这个“距离”就是函数间隔除以$|\theta|$,得到几何间隔。优化目标写成:
$$\max_{\theta,b} \min_i \frac{y^{(i)}(\theta^Tx^{(i)}+b)}{|\theta|}$$
第二步:转化为凸优化问题。通过缩放约束(令函数间隔为1),问题变成最小化$\frac{1}{2}|\theta|^2$,约束是$y^{(i)}(\theta^Tx^{(i)}+b) \geq 1$。
第三步:拉格朗日对偶。引入拉格朗日乘子$\alpha_i$,写出对偶问题。这里的关键是KKT条件,特别是互补松弛条件:只有当样本点在间隔边界上时,对应的$\alpha_i$才非零。这些点就是支持向量。
第四步:核技巧。在对偶问题中,目标函数和决策函数都只涉及内积$\langle x^{(i)}, x^{(j)} \rangle$。把内积替换成核函数$K(x^{(i)}, x^{(j)})$,就实现了非线性映射,而且不需要显式计算高维特征。
踩坑记录:我一开始不理解为什么核函数要满足Mercer条件。后来查了资料才明白,Mercer条件保证核矩阵是半正定的,这样对偶问题才是凸的,才能用二次规划求解。常用的多项式核、高斯核都满足这个条件。
2.4 无监督学习:EM算法与高斯混合模型的收敛性分析
EM算法是另一个让我卡了很久的地方。K-means好理解,就是不断分配点到最近的中心,然后更新中心。但GMM的EM算法,E步和M步的公式是怎么来的?
我的笔记里记了完整的推导:
E步:计算每个点属于每个簇的后验概率$\gamma_{ik} = \frac{\pi_k \mathcal{N}(x_i|\mu_k,\Sigma_k)}{\sum_j \pi_j \mathcal{N}(x_i|\mu_j,\Sigma_j)}$。
M步:用这些后验概率作为权重,更新$\pi_k$、$\mu_k$、$\Sigma_k$。
关键问题是:为什么这样迭代能保证似然函数单调不减?这涉及到Jensen不等式和ELBO(证据下界)的推导。简单说,EM算法是在优化一个下界,E步固定参数优化下界,M步固定下界优化参数,两者交替,似然函数就会单调上升。
我在笔记里特别标注了:EM算法收敛到的是局部最优,不是全局最优。所以实际使用时,通常要跑多次随机初始化,取似然最大的那次。另外,如果协方差矩阵出现奇异(某个簇只有一个点),似然会趋于无穷,这时候需要加正则项或者重新初始化。
3. 实操过程与核心环节的完整记录
3.1 从讲义到笔记:我的具体整理流程
整个整理过程我分了五个阶段,每个阶段都有明确的产出:
阶段一:快速通读,标记重点。我先把所有讲义和视频过一遍,用不同颜色的高亮标记:黄色是核心定义,蓝色是推导关键步骤,红色是没看懂的地方。这个阶段不求甚解,只求建立全局感。
阶段二:逐章精读,手写推导。对于标记为红色的部分,我会在纸上手推一遍。手推的好处是强迫自己关注每一步的细节,比如矩阵求导时的维度变化、概率积分时的变量替换。推完之后,再把推导过程用LaTeX敲进笔记。
阶段三:代码验证,加深理解。对于每个算法,我会用Python实现一个最小版本。比如线性回归用正规方程和梯度下降两种方法实现,对比结果;逻辑回归用牛顿法实现,观察收敛速度。代码不追求效率,只追求逻辑清晰。
阶段四:交叉引用,建立连接。这一步是在笔记之间加链接。比如在逻辑回归的笔记里,链接到广义线性模型的笔记;在SVM的笔记里,链接到拉格朗日对偶的笔记。这样复习时就能顺着链接跳转,而不是孤立地看。
阶段五:输出总结,查漏补缺。最后我会为每个大章节写一个“一页纸总结”,只包含最核心的公式和直觉解释。如果某个算法的总结写不出来,说明还没真正理解,需要回去补。
3.2 关键公式的推导细节与常见错误
这里挑几个我踩过坑的公式推导,详细说一下。
第一个坑:矩阵求导的维度匹配。在线性回归的正规方程推导中,需要对$\theta$求导:
$$\nabla_\theta \frac{1}{2}(X\theta - y)^T(X\theta - y) = X^T(X\theta - y)$$
我一开始总是搞不清什么时候转置。后来总结了一个规则:标量对向量求导,结果的维度要和被求导的向量一致。这里$\theta$是$n \times 1$,所以结果也应该是$n \times 1$。$X^T(X\theta - y)$中,$X^T$是$n \times m$,$(X\theta - y)$是$m \times 1$,乘起来是$n \times 1$,维度对上了。
第二个坑:Sigmoid函数的导数。逻辑回归的梯度推导中,需要用到$\sigma'(z) = \sigma(z)(1-\sigma(z))$。这个公式看起来简单,但推导时容易漏掉负号。我的记忆方法是:Sigmoid的导数等于自身乘以(1减自身),这个形式很对称,不容易忘。
第三个坑:高斯分布的积分。在GDA的推导中,需要计算高斯分布的最大似然估计。这里涉及到对$\Sigma$求导,用到了矩阵求导的迹技巧:
$$\nabla_\Sigma \log|\Sigma| = \Sigma^{-T}$$
$$\nabla_\Sigma \text{tr}(\Sigma^{-1}S) = -\Sigma^{-T}S^T\Sigma^{-T}$$
这两个公式我当时推了很久,后来发现记住结论就行,推导过程在笔记里附上参考链接即可。
3.3 代码实现中的向量化技巧
在实现这些算法时,向量化是绕不开的。我举几个例子:
K-means的向量化:计算每个点到每个中心的距离,可以用矩阵运算一次性完成。假设数据是$X \in \mathbb{R}^{m \times n}$,中心是$C \in \mathbb{R}^{k \times n}$,则距离矩阵$D$的每个元素$D_{ij} = |X_i - C_j|^2$。利用$|a-b|^2 = |a|^2 + |b|^2 - 2a^Tb$,可以写成:
D = np.sum(X**2, axis=1, keepdims=True) + np.sum(C**2, axis=1) - 2 * X @ C.T这样比双重循环快几十倍。
逻辑回归的梯度下降:梯度$\nabla_\theta J = \frac{1}{m}X^T(\sigma(X\theta) - y)$,其中$\sigma$是逐元素的Sigmoid。用NumPy一行就能写出来:
grad = (1/m) * X.T @ (sigmoid(X @ theta) - y)EM算法的E步:计算后验概率时,需要对每个簇计算高斯密度。可以用广播机制:
for k in range(K): diff = X - mu[k] exponent = -0.5 * np.sum(diff @ np.linalg.inv(Sigma[k]) * diff, axis=1) coef = 1 / np.sqrt((2*np.pi)**n * np.linalg.det(Sigma[k])) pdf[:, k] = coef * np.exp(exponent) gamma = pdf * pi / np.sum(pdf * pi, axis=1, keepdims=True)实操心得:向量化代码写完之后,一定要用一个小数据集和循环版本对比结果,确保逻辑正确。我在这上面吃过亏,向量化写错了但没发现,跑出来的结果和预期差很多,排查了半天才发现是广播维度的问题。
4. 常见问题与排查技巧实录
4.1 数学推导中的典型卡点与突破方法
在整理笔记的过程中,我遇到了不少卡点,这里列几个有代表性的:
卡点一:拉格朗日对偶的强对偶性条件。SVM的推导中,原问题是凸的且满足Slater条件,所以强对偶成立。但Slater条件具体是什么?我查了资料才搞清楚:存在一个严格满足不等式约束的点。对于SVM,因为约束是线性的,只要数据不是完全不可分,就存在这样的点。
卡点二:EM算法的Jensen不等式推导。ELBO的推导中,关键一步是利用Jensen不等式把对数里的求和拿出来。Jensen不等式说的是:对于凸函数$f$,$f(\mathbb{E}[X]) \leq \mathbb{E}[f(X)]$。因为$\log$是凹函数,所以不等号方向反过来。这个细节我当时搞混了,导致下界的方向搞反了。
卡点三:PCA的两种推导视角。PCA可以从最大化投影方差推导,也可以从最小化重构误差推导。这两种视角得到的解是一样的,但直觉不同。我在笔记里把两种推导都写了一遍,并标注了它们等价的条件:当投影向量是正交基时,最大化方差和最小化重构误差是等价的。
4.2 笔记整理中的效率陷阱与应对策略
整理笔记本身也是个技术活,我踩过几个效率陷阱:
陷阱一:追求完美排版。一开始我花了很多时间调整公式的间距、对齐、字体,结果进度很慢。后来我定了一个规则:先写内容,排版最后统一处理。公式只要渲染正确就行,美观度放在第二位。
陷阱二:过度摘抄。有段时间我几乎把讲义原封不动地抄了一遍,结果笔记厚是厚,但复习时抓不住重点。后来我改成用自己的话重述,每段内容都问自己:“这句话的核心是什么?我能不能用更简单的语言说清楚?”
陷阱三:缺少索引。笔记多了之后,找某个知识点很麻烦。后来我在每个文件开头加了一个目录,用Markdown的锚点链接跳转。另外,我用标签系统给每个笔记打了标签,比如#监督学习、#优化、#概率图,这样可以用搜索快速定位。
4.3 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 梯度下降不收敛 | 学习率过大 | 打印损失曲线 | 减小学习率或使用自适应方法 |
| 正规方程矩阵不可逆 | 特征共线性 | 计算条件数 | 加正则项或删除相关特征 |
| SVM对偶问题求解慢 | 样本量大 | 检查支持向量比例 | 使用SMO算法或线性核近似 |
| EM算法似然下降 | 实现错误 | 检查E步和M步公式 | 确保E步归一化、M步更新正确 |
| PCA投影后方差很小 | 未中心化 | 检查均值 | 先减去均值再计算协方差 |
| 逻辑回归过拟合 | 特征过多 | 对比训练和验证误差 | 加L2正则或减少特征 |
避坑技巧:对于EM算法,我建议在每次迭代后打印似然值,如果出现下降,一定是E步或M步的实现有问题。最常见的是E步的后验概率没有归一化,或者M步更新协方差时忘了除以权重和。
4.4 从笔记到分享:如何让内容对别人也有价值
整理完之后,我试着把笔记分享给几个同学,发现了一些问题:
问题一:假设读者有背景知识。我写“由KKT条件可得”,但没解释KKT条件是什么。后来我在每个专业术语第一次出现时都加了简短解释或链接。
问题二:缺少动机说明。我直接写“我们定义核函数为...”,但没说明为什么要定义核函数。后来我在每个算法开头都加了一段“为什么需要这个”。
问题三:公式太多,直觉太少。纯公式的笔记读起来很累。后来我在每个公式后面加了一句“人话解释”,比如“这个公式说的是,后验概率正比于先验乘以似然”。
经过这几轮修改,笔记的可读性好了很多。我的体会是:好的笔记不仅是给自己看的,还要能经得起别人的审视。当你试图向别人解释一个概念时,往往会发现自己理解中的漏洞。
5. 一些额外的经验与建议
5.1 关于学习节奏的安排
这门课的内容密度很大,我建议不要试图一次性学完。我的节奏是:每周集中攻克一个主题,周末做总结和代码实现。比如第一周线性回归和逻辑回归,第二周广义线性模型和生成学习算法,第三周SVM和核方法,以此类推。
每个主题的学习分三步:看视频建立直觉 → 读讲义补推导 → 写代码验证。三步走完,基本就能掌握得比较扎实。如果时间紧,至少要走完前两步。
5.2 关于数学基础的补充
如果数学基础不够,我建议先补三块:线性代数(矩阵运算、特征值分解、SVD)、概率论(贝叶斯定理、常见分布、期望方差)、微积分(偏导数、链式法则、拉格朗日乘子法)。这三块是这门课的基础,不补的话后面会很吃力。
补的方式不一定要系统学教材,可以用到什么补什么。比如学到SVM时发现拉格朗日对偶不懂,就专门去查对偶的资料,这样学起来有针对性,记忆也更深。
5.3 关于笔记的维护和更新
笔记不是写完就完了,后续的维护也很重要。我的做法是:
- 定期回顾:每个月抽时间翻一遍笔记,看看有没有新的理解可以补充。
- 版本控制:用Git管理,每次修改都有记录,方便回溯。
- 开放分享:把笔记放在公开仓库里,接受别人的反馈和纠错。
最后再分享一个小技巧:在笔记里留一个“疑问区”,把当时没想明白的问题记下来,后面如果解决了就回来更新。我有很多重要的理解,都是在回顾这些疑问时突然想通的。