
1. 从零到一为什么我选择死磕CS229这门课动手写这份笔记之前我翻了一下自己的学习记录从第一讲看到最后一讲前后跨度差不多四个月。这四个月里我白天要处理工作上的事情晚上和周末才能挤出时间啃讲义、推公式、写代码。说实话中途有好几次想放弃尤其是推导EM算法的收敛性那一块来来回回看了不下五遍才彻底理顺。但走完之后回头看CS229这门课给我的东西远不止几个机器学习算法的公式推导而是一整套面对未知问题时拆解、建模、验证的思维方式。CS229是斯坦福大学开设的机器学习课程由吴恩达教授主讲。它的定位和市面上大多数机器学习入门课不太一样——它不满足于让你会调库、会跑模型而是要求你从概率论、线性代数、凸优化这些数学工具出发亲手推导出每一个算法的来龙去脉。你学完线性回归不只是知道sklearn.linear_model怎么用而是能从头写出正规方程组的推导过程理解为什么最小二乘法在高斯噪声假设下是最大似然估计的特例。这种深度是CS229最核心的价值也是它一直被奉为机器学习“内功修炼”经典的原因。这份笔记适合谁看如果你已经有一些编程基础学过微积分和线性代数想真正搞懂机器学习算法背后的数学原理那这份笔记会对你有很大帮助。如果你只是想快速调个模型跑个结果那可能市面上的速成课更适合你。但如果你想在机器学习这条路上走得更远想具备独立推导新算法、读懂前沿论文的能力CS229是绕不过去的一关。我的笔记覆盖了从监督学习到无监督学习、从学习理论到强化学习的完整内容每一讲都包含核心公式推导、关键概念解释和我自己的理解补充。2. 笔记的整体架构与内容拆解2.1 监督学习从线性回归到支持向量机监督学习是CS229前半部分的重头戏也是整个机器学习体系的基础。我的笔记从线性回归开始逐步过渡到逻辑回归、广义线性模型、生成学习算法最后到支持向量机。这个顺序不是随便排的它背后有一条清晰的逻辑线先从最简单的连续值预测问题入手建立基本的建模思路然后引入概率解释把最小二乘法和最大似然估计联系起来接着推广到分类问题引出逻辑回归再通过指数族分布的统一框架把线性回归、逻辑回归、Softmax回归都纳入广义线性模型的范畴最后用最大间隔的思想引出SVM完成从概率模型到几何模型的跨越。线性回归部分我重点整理了最小二乘法的两种推导方式一种是从代数角度直接求导令梯度为零得到正规方程组另一种是从概率角度假设噪声服从高斯分布写出似然函数取对数后最大化发现结果和最小二乘法完全一致。这个“殊途同归”的推导过程非常关键它让你理解为什么最小二乘法这么常用——因为它等价于在高斯噪声假设下的最大似然估计。笔记里我手写了完整的矩阵求导过程包括标量对向量的导数、向量对向量的导数这些容易搞混的地方都做了详细标注。逻辑回归部分核心是Sigmoid函数的引入和交叉熵损失函数的推导。我特别强调了逻辑回归虽然叫“回归”但它解决的是分类问题。从广义线性模型的角度看逻辑回归假设响应变量服从伯努利分布自然参数就是对数几率所以Sigmoid函数不是拍脑袋想出来的而是从指数族分布推导出来的必然结果。这个视角转换很重要很多教程只告诉你逻辑回归用Sigmoid但不解释为什么用它我的笔记把这条逻辑链补全了。支持向量机是监督学习部分最抽象的内容也是我花时间最多的地方。从函数间隔和几何间隔的定义开始到最大化间隔的优化问题再到拉格朗日对偶性、KKT条件、核技巧每一步我都写了详细的推导。特别是对偶问题的转化为什么要转成对偶问题因为原问题在特征维度很高时计算量巨大而对偶问题只涉及样本之间的内积运算这就为核函数的引入铺平了道路。核技巧的本质是用核函数隐式地计算高维空间中的内积避免了显式映射带来的维度灾难。我在笔记里用多项式核和高斯核分别举了例子手算了一遍核矩阵帮助理解这个“隐式映射”到底是怎么回事。2.2 无监督学习聚类、降维与EM算法无监督学习部分我重点整理了K-means聚类、高斯混合模型和EM算法、主成分分析这三块内容。K-means是最直观的聚类算法但它的局限性也很明显硬分配、对初始点敏感、假设簇是球形的。高斯混合模型通过引入隐变量把硬分配变成软分配用概率密度来描述样本属于每个簇的可能性。但GMM的参数估计不能直接用最大似然因为对数似然函数里包含对求和项的对数无法求解析解。这时候EM算法就派上用场了。EM算法的核心思想是既然隐变量不知道那我就先猜一组参数根据这组参数计算隐变量的期望E步然后基于这个期望最大化似然函数来更新参数M步反复迭代直到收敛。我在笔记里完整推导了GMM的EM算法包括E步计算后验概率、M步更新均值、协方差和混合系数的公式。这里有一个容易踩的坑协方差矩阵的更新公式里分母是每个簇的样本权重之和而不是样本总数。这个细节很多教程一笔带过但实际写代码时如果搞错了结果会完全不对。主成分分析部分我从最大方差和最小重构误差两个角度分别推导了PCA。最大方差角度是找一个方向使得数据投影到这个方向后方差最大最小重构误差角度是找一个低维子空间使得数据投影后再重构回来的误差最小。两个角度最终导出的优化问题是一样的都是对协方差矩阵做特征值分解取最大的几个特征值对应的特征向量作为主成分。我在笔记里特别说明了PCA之前必须做均值归一化否则方差最大的方向会被均值大的特征主导导致降维结果没有意义。2.3 学习理论与强化学习从偏差方差到价值迭代学习理论部分我整理了偏差-方差分解、经验风险最小化、一致收敛、VC维这些概念。偏差-方差分解是理解模型泛化能力的核心工具偏差衡量模型预测的期望与真实值之间的差距方差衡量模型在不同训练集上的预测波动。高偏差对应欠拟合高方差对应过拟合。正则化方法如L2正则化本质上是通过增加偏差来换取方差的降低从而提升整体泛化能力。VC维则从理论层面回答了“一个模型类有多复杂”这个问题VC维越大模型类越复杂需要的训练样本就越多才能保证泛化性能。强化学习部分我整理了马尔可夫决策过程、值迭代、策略迭代、Q-learning这些内容。MDP的核心要素包括状态、动作、转移概率、奖励和折扣因子。值函数衡量的是从某个状态出发按照某个策略行动所能获得的累积折扣奖励的期望。贝尔曼方程是值函数的递归定义值迭代就是不断应用贝尔曼最优方程来更新值函数直到收敛。策略迭代则是交替进行策略评估和策略改进通常比值迭代收敛更快但每次迭代的计算量更大。我在笔记里用格子世界这个经典例子手算了一遍值迭代的过程帮助理解贝尔曼方程的迭代更新机制。3. 核心公式推导与代码实现对照3.1 线性回归的正规方程与梯度下降线性回归的假设函数是 ( h_\theta(x) \theta^T x )损失函数是均方误差 ( J(\theta) \frac{1}{2} \sum_{i1}^{m} (h_\theta(x^{(i)}) - y^{(i)})^2 )。写成矩阵形式就是 ( J(\theta) \frac{1}{2} (X\theta - y)^T (X\theta - y) )。对 (\theta) 求导令导数为零得到正规方程 ( \theta (X^T X)^{-1} X^T y )。这个推导看起来简单但有几个细节值得注意。第一( X^T X ) 不一定可逆当特征之间存在线性相关时矩阵是奇异的。这时候可以用伪逆或者加一个很小的正则项 ( \lambda I ) 来保证可逆。第二正规方程的计算复杂度是 ( O(n^3) )因为要对 ( n \times n ) 的矩阵求逆当特征维度很高时计算量很大。这时候梯度下降就更合适它的每次迭代复杂度是 ( O(mn) )适合大规模数据。我在笔记里用Python实现了两种方法并在一个简单的数据集上做了对比。正规方程一步到位结果精确梯度下降需要调节学习率迭代多次才能收敛但适合特征维度高的场景。代码里我特别标注了特征缩放的重要性如果不同特征的量纲差异很大梯度下降的收敛路径会呈“之”字形收敛很慢。做均值归一化后收敛速度明显加快。3.2 逻辑回归的梯度推导与牛顿法逻辑回归的假设函数是 ( h_\theta(x) \frac{1}{1 e^{-\theta^T x}} )损失函数是交叉熵 ( J(\theta) -\sum_{i1}^{m} [y^{(i)} \log h_\theta(x^{(i)}) (1-y^{(i)}) \log (1-h_\theta(x^{(i)}))] )。对 (\theta) 求导得到梯度 ( \nabla_\theta J(\theta) \sum_{i1}^{m} (h_\theta(x^{(i)}) - y^{(i)}) x^{(i)} )。这个梯度的形式和线性回归很像都是预测值减真实值再乘特征但 ( h_\theta(x) ) 换成了Sigmoid函数。牛顿法是用二阶导数信息来加速收敛的优化方法更新公式是 ( \theta : \theta - H^{-1} \nabla_\theta J(\theta) )其中 ( H ) 是海森矩阵。牛顿法通常比梯度下降收敛快得多但每次迭代要计算海森矩阵的逆复杂度是 ( O(n^3) )当特征维度高时开销很大。我在笔记里对比了梯度下降和牛顿法在逻辑回归上的收敛曲线牛顿法一般十几次迭代就能收敛到很高的精度梯度下降则需要上千次。但牛顿法对初始点敏感如果初始点离最优解太远可能会发散。3.3 SVM的对偶问题与核函数实现SVM的原始优化问题是 ( \min_{\theta,b} \frac{1}{2} |\theta|^2 )约束条件是 ( y^{(i)}(\theta^T x^{(i)} b) \geq 1 )。通过拉格朗日乘子法转化为对偶问题( \max_\alpha \sum_{i1}^{m} \alpha_i - \frac{1}{2} \sum_{i,j1}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \langle x^{(i)}, x^{(j)} \rangle )约束条件是 ( \alpha_i \geq 0 ) 且 ( \sum_{i1}^{m} \alpha_i y^{(i)} 0 )。对偶问题的好处是只涉及样本之间的内积把内积替换成核函数 ( K(x^{(i)}, x^{(j)}) ) 就得到了核SVM。常用的核函数包括多项式核 ( K(x,z) (x^T z c)^d ) 和高斯核 ( K(x,z) \exp(-\frac{|x-z|^2}{2\sigma^2}) )。高斯核对应的是无穷维的特征空间但通过核技巧我们不需要显式地计算这个映射只需要计算核函数的值。我在笔记里用Python实现了一个简化版的SMO算法来求解对偶问题。SMO的核心思想是每次选取两个乘子 ( \alpha_i ) 和 ( \alpha_j )固定其他乘子对这两个乘子做优化。选取的原则是违反KKT条件最严重的乘子优先。代码里我加了详细的注释解释了每一步的数学依据。实测下来在几百个样本的数据集上SMO能在几秒内收敛分类效果和sklearn.svm.SVC基本一致。4. 实操过程中踩过的坑与排查技巧4.1 矩阵求导的维度匹配问题矩阵求导是CS229笔记里最容易出错的地方。我一开始经常搞混标量对向量求导、向量对向量求导的维度。比如 ( \frac{\partial}{\partial \theta} \theta^T A \theta )如果 ( A ) 是对称矩阵结果是 ( 2A\theta )如果 ( A ) 不对称结果是 ( (A A^T)\theta )。这个细节在推导正规方程时很关键如果搞错了整个推导就全乱了。我的经验是每次求导前先明确输入和输出的维度。标量对 ( n ) 维向量求导结果是 ( n ) 维向量( m ) 维向量对 ( n ) 维向量求导结果是 ( m \times n ) 的矩阵。把维度写在旁边推导过程中随时检查能避免大部分错误。另外我习惯用分量形式先推导一遍再写成矩阵形式这样不容易出错。4.2 EM算法中协方差矩阵的更新GMM的EM算法里协方差矩阵的更新公式是 ( \Sigma_k \frac{\sum_{i1}^{m} w_{ik} (x^{(i)} - \mu_k)(x^{(i)} - \mu_k)^T}{\sum_{i1}^{m} w_{ik}} )其中 ( w_{ik} ) 是样本 ( i ) 属于簇 ( k ) 的后验概率。这里的分母是 ( \sum_{i1}^{m} w_{ik} )也就是簇 ( k ) 的有效样本数而不是总样本数 ( m )。我一开始就是这里搞错了导致协方差矩阵偏大聚类结果完全不对。排查这个问题的方法很简单检查每个簇的协方差矩阵是否合理。如果某个簇的协方差矩阵对角线元素特别大说明分母可能用错了。另外如果某个簇的有效样本数 ( \sum_{i1}^{m} w_{ik} ) 非常小协方差矩阵可能会接近奇异这时候可以加一个小的正则项 ( \epsilon I ) 来保证数值稳定性。4.3 梯度下降学习率的选取梯度下降的学习率 ( \alpha ) 选取是个经验活。太大容易震荡甚至发散太小收敛太慢。我一般从0.01开始试如果损失函数在迭代中震荡就减小到0.001如果收敛太慢就增大到0.03或0.1。更好的做法是画损失函数随迭代次数的变化曲线如果曲线平滑下降说明学习率合适如果曲线震荡说明学习率偏大如果曲线下降很慢说明学习率偏小。还有一个技巧是使用学习率衰减一开始用较大的学习率快速下降随着迭代进行逐渐减小学习率让模型在最优解附近精细调整。常见的衰减策略包括指数衰减 ( \alpha_t \alpha_0 e^{-kt} ) 和倒数衰减 ( \alpha_t \frac{\alpha_0}{1kt} )。我在笔记里用Python画了不同学习率下的收敛曲线直观展示了学习率对收敛速度和稳定性的影响。4.4 核函数参数的选择高斯核SVM有两个关键参数( C ) 和 ( \sigma )。( C ) 控制对误分类的惩罚程度( C ) 越大对误分类的惩罚越重模型越倾向于把所有训练样本都分对容易过拟合( C ) 越小允许更多的误分类模型更偏向于最大化间隔可能欠拟合。( \sigma ) 控制高斯核的宽度( \sigma ) 越小核函数越窄模型越复杂容易过拟合( \sigma ) 越大核函数越宽模型越简单可能欠拟合。我一般用网格搜索来选这两个参数( C ) 取 ( {0.01, 0.1, 1, 10, 100} )( \sigma ) 取 ( {0.01, 0.03, 0.1, 0.3, 1} )用交叉验证评估每组参数的性能选最好的那组。实测下来( C1 ) 和 ( \sigma0.1 ) 在大多数中小规模数据集上表现都不错可以作为初始值。5. 常见问题速查表问题现象可能原因排查方法解决方案损失函数震荡不收敛学习率过大画损失曲线观察减小学习率或使用衰减策略损失函数下降太慢学习率过小或特征未归一化检查特征量纲增大学习率做均值归一化正规方程报奇异矩阵错误特征线性相关计算特征相关系数矩阵删除相关特征或加正则项GMM聚类结果全为一类协方差矩阵更新分母错误检查每个簇的有效样本数分母改为簇权重之和SVM所有样本都是支持向量参数C过大或σ过小检查支持向量比例减小C或增大σPCA降维后信息损失大主成分数量太少计算累计方差贡献率增加主成分数量至85%以上逻辑回归准确率始终50%特征与标签无关或学习率问题检查特征与标签的相关性更换特征或调整学习率梯度下降出现NaN学习率过大导致数值溢出检查损失值变化大幅减小学习率6. 笔记之外一些学习方法和心态上的体会学CS229最大的感受是数学推导不能光看必须自己动手推一遍。我看第一遍的时候觉得都懂了合上笔记自己推发现很多步骤卡壳。特别是拉格朗日对偶和KKT条件那部分看别人推很流畅自己推就不知道下一步该往哪走。后来我养成了一个习惯每看完一讲拿一张白纸把核心推导从头到尾默写一遍卡住的地方再回去看笔记直到能独立推出来为止。这个过程很痛苦但效果非常好。另一个体会是代码实现和数学推导要结合起来。光推公式不写代码理解停留在纸面上光写代码不推公式遇到问题不知道怎么调。我的做法是每学完一个算法先用NumPy手写一遍不调库把公式里的每一步都对应到代码上。比如逻辑回归的梯度更新代码里就是theta - alpha * X.T (h - y) / m这一行代码对应的是梯度公式 ( \nabla_\theta J(\theta) \frac{1}{m} X^T (h - y) )。写完之后再和sklearn的结果对比如果差距很大说明推导或代码有问题回去排查。还有一点是关于学习节奏的。CS229的内容密度很大一讲的内容可能相当于其他课程两三讲。我一开始想一周看两讲结果发现根本消化不了。后来调整为每周一讲周末集中时间推导和写代码平时晚上看视频和讲义。这样四个月下来虽然慢但每一讲都学得很扎实。我觉得学这门课慢就是快贪多嚼不烂。最后分享一个整理笔记的小技巧我用Markdown写笔记公式用LaTeX代码用Python每讲一个文件。笔记里除了课程内容还加了自己的理解、踩过的坑、和实际项目的联系。这样复习的时候不光是看课程内容还能看到自己当时的思考过程。现在回头看这些笔记已经不只是CS229的笔记了更像是我机器学习学习路上的一个完整记录。