ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CS229讲义作业与讲解:系统学习路径和避坑实践

CS229讲义作业与讲解:系统学习路径和避坑实践

简介:CS229全课程资料包系统整理了Andrew Ng教授斯坦福机器学习课程的讲义、作业与作业讲解,服务自学机器学习的在校学生、数据从业者与面试准备者,免去零散搜集、缺少配套练习的困扰。压缩包共47个文件、约9.22MB,以30个PDF讲义和官方解答为主体,另有7个ZIP数据包、6个M脚本和4个DAT数据文件,兼顾理论推导与实操复现。目前已有1320人学习/下载,是经典课程广受认可的一站式配套。讲义从线性回归、Logistic回归、广义线性模型到SVM、EM算法、PCA、ICA、强化学习,并配有线性代数、概率、凸优化、高斯过程等复习笔记,覆盖监督与无监督学习主干;作业部分包含编程任务和理论问题,配套解答与数据代码可直接对照练习。这套资料特别适合希望边学边练、通过作业讲解检验理解的学习者,能帮助其系统掌握模型选择、正则化、特征工程等关键能力,为后续研究或工作打下扎实基础。

1. CS229 的讲义、作业和作业讲解,为什么到现在还值得按顺序啃一遍

CS229 的讲义、作业和作业讲解,是很多算法工程师入行时都囤过的一套资料,但真正按顺序啃完的人不多。原因不是内容难,而是资料太散:讲义有多个年份的版本,作业要配 Octave 环境,讲解又散落在题解和笔记里。这篇内容把这三块拆开讲清楚——讲义怎么读、作业怎么做、讲解怎么用,以及这些年我见过最多的几个翻车现场。适合准备求职笔试、研究生入门、以及想系统补机器学习基础的开发者:课程不新,但里面的推导、作业和排错思路,到现在依然是面试里那些高频考点的源头。

2. 讲义怎么读:先分清版本和文件类型,再定阅读顺序

2.1 讲义文件分四类,先搞清楚你手里是哪一类

CS229 的讲义不是一本书,而是一堆 PDF 的集合。第一次打开课程主页的人,很容易被文件名绕晕。我一般会先把它们按用途分成四类,再决定哪些精读、哪些略读。

第一类是主课讲义。这类讲义按主题组织,覆盖监督学习、生成学习、SVM、学习理论、无监督学习和强化学习。每一份的篇幅不长,但推导密度很大,几乎每一页都在用矩阵和概率符号说话。第二类是 section notes,也就是讨论课讲义,常见内容包括矩阵求导、概率论复习、凸优化入门。这些材料是给“忘记数学细节”的人准备的,不是课程主线。

第三类是数学前置资料。课程早期会发一份概率论与统计复习、一份线性代数复习,里面把后文要用到的记号统一了,比如高斯分布、贝叶斯公式、矩阵迹的性质。我见过不少人跳过这部分直接读主讲义,结果在“对目标函数求导并令其为零”那里卡住,回来补数学反而花了更多时间。

第四类是深度学习补充讲义。这是课程后来迭代时加进来的内容,讲反向传播、卷积网络、循环网络的推导,风格比主讲义更偏现代框架。注意年份:早年的讲义里深度学习内容很少,甚至只有一节带过,后面几年的版本才把它扩成完整讲义。你下载的资料是哪一年归档的,决定了这份补充讲义有多厚。

提示:先看你手里这份资料的目录,标出年份,再决定阅读顺序。不同年份的讲义章节编号不一定对齐,网上文章提到的页码可能和你手里的完全对不上。

2.2 两周入门阅读计划:从数学复习到模型主线

讲义阅读顺序比读哪一份更重要。我自己的做法是给自己排两周计划,按“数学 → 监督学习 → 学习理论 → 无监督与扩展”推进,和作业的依赖关系完全一致。

阶段读什么为什么这么排
第 1 周概率论复习、线性代数复习、矩阵求导 section note后面所有推导都依赖这些符号和运算规则,先把记号统一
第 2 周前半线性回归、逻辑回归、SVM、生成学习对应作业 ex1/ex2 的模型基础,面试里问得最多的也是这一段
第 2 周后半学习理论:偏差方差、正则化、交叉验证对应 ex5,也决定你能不能答上“为什么正则化有用”
有余力无监督学习、深度学习补充讲义对应 ex3/ex4 的神经网络部分,以及简历上的项目扩展

这个顺序不是随便拍的。ex1 和 ex2 的作业代码里,大量使用了最小二乘、梯度下降和最大似然估计,这些恰好是主讲义开头的内容。ex5 的学习曲线和偏差方差分析,如果不是先读过学习理论那一章,画出来的图根本不知道怎么解释。读者如果只有一个月时间,我建议把第 1 周压缩到三天,重点只读矩阵求导那一份 section note。

2.3 哪些章节可以暂时跳过,哪些必须精读

讲义里不是每一章都值得花同样时间。我按“面试和作业里出现的频率”排序:线性回归、逻辑回归、SVM、学习理论这几章必须精读,里面每个公式都要能手推;生成学习里的朴素贝叶斯和高斯判别分析也要读,因为面试题里经常用它们来考“生成式 vs 判别式”的差异。

可以暂时跳过的部分,包括强化学习的几节和一部分无监督细节。强化学习在经典版本里只搭了个框架,后续的课程和教材比它讲得更清楚,入门阶段读它性价比不高。但这不意味着完全不看,至少要把术语过一遍,比如回报函数、策略、值函数,避免在讨论里完全听不懂。

另一个容易被忽略的点是讲义里的习题和章末注记。主讲义里有些“练习”不是作业,但常常是下一份讲义的前置引子,比如在线性回归后面让读者自己推导正则化后的解。把这些小练习当成“课间自测”去做,比憋到最后写作业时再返工要顺得多。我见过太多人把讲义当成小说一样读完,不落笔推导,最后写作业时连下标都搞混。

3. 作业怎么做:Octave 环境、代码骨架与判分机制,照着跑通第一份作业

3.1 环境选型:Octave、MATLAB 还是 Python

CS229 的作业历史上有两套环境。早年的作业默认用 Octave 或 MATLAB,代码文件以.m结尾,判分脚本也是围绕这套环境写的。后面有些年份的课程也开始提供 Python 版本的环境说明,但传统上“打开 ex1.m、跑通 submit”依然是最标准的路径。读者拿到的资料若是老版本,直接上 Python 迁移会踩不少数据格式的坑,反而得不偿失。

我的建议是:按你手里作业说明文件的默认环境来。如果作业压缩包里全是.m文件,就老老实实装 Octave;如果说明文档里明确给了 Python 的 requirements,再走 Python 路线。Octave 是开源实现,和 MATLAB 语法兼容度很高,绝大多数作业代码不用改就能跑。MATLAB 的优势是调试器和变量查看器更顺手,但需要正版授权,对学生和自学者不是必需品。

装 Octave 在 Linux 上也就是一条命令的事:

sudo apt install octave

Windows 用户去官网下载安装包即可。装完打开命令行,敲一个octave能进入交互环境,就算装成了。别急着再装一堆工具箱,CS229 作业用到的基本功能原生 Octave 就覆盖了。

注意:不要装最新版就以为万事大吉。作业里的 submit 脚本是多年前写的,个别新版本 Octave 改了函数行为会导致脚本报错。如果遇到不明错误,先换成课程说明里指定的大版本,再跑一遍。

3.2 第一份作业的最小跑通流程与核心代码

第一份作业通常是线性回归。打开压缩包,你会看到ex1.m、ex1data1.txt、ex1.pdf这类文件。正确的顺序是:先读ex1.pdf,再打开ex1.m看主流程,最后在标着 TODO 的位置填空。

ex1data1.txt是两列数据的文本文件,第一列是人口,第二列是房价。下面这段代码是作业里的核心步骤——数据读取、加截距项、梯度下降、画代价曲线:

% 读取训练数据并构造特征矩阵 data = load('ex1data1.txt'); X = data(:, 1); y = data(:, 2); m = length(y); % 在 X 前面加一列 1,对应线性回归的截距项 theta0 X = [ones(m, 1), X]; theta = zeros(2, 1); % 梯度下降超参数 alpha = 0.01; % 学习率,太大会发散,太小收敛过慢 iterations = 1500; % 迭代轮数 % 梯度下降主循环 for iter = 1:iterations h = X * theta; % 当前预测值,m x 1 向量 theta = theta - alpha / m * (X' * (h - y)); % 批量梯度下降更新 J(iter) = 1 / (2 * m) * sum((h - y) .^ 2); % 记录每轮代价 end % 画代价曲线,观察是否平滑下降 plot(1:iterations, J); xlabel('迭代次数'); ylabel('代价函数值');

这段代码的逻辑很直白:load读入文本文件,ones(m, 1)生成全 1 列向量来拼截距项,X' * (h - y)是一次向量化的梯度计算,等价于把所有样本的误差乘以特征再求和。代价函数J的定义用的是最小二乘的一半,除以m而不是2m,是因为求导后 2 会抵消。

参数上最容易出问题的两个地方:一是学习率alpha,0.01是作业给定的经验值,如果你自己调数据,遇到代价曲线震荡或 NaN,先把alpha缩小十倍;二是特征量纲差异大的时候要归一化,作业后面会给featureNormalize.m,它的作用就是把每列特征减去均值除以标准差,否则梯度下降会在某些维度上震荡很久。

3.3 判分机制与离线自检:别只盯着 submit 结果

作业提交脚本是submit系列函数,运行后它会打包你的答案,传回课程判分服务器打分。分数是按函数输出与标准答案的近似程度给的,允许很小的误差。但实际自学时,submit 结果不是唯一的检验手段,而且服务器多年后未必还开着,所以离线自检更重要。

我一般会在提交前做三件事:第一,看代价曲线是不是单调下降,如果中间有上翘,说明学习率太大或数据没归一化;第二,在终端打印当前theta,和讲义里的解析解公式对比,比如用pinv(X' * X) * X' * y算一个最优解,看差距是否在1e-3以内;第三,把预测值可视化,画在散点图上看看拟合线是否合理。

这三件事做完,即使 submit 不可用,你也能确定自己的实现是对的。反过来,有些人 submit 拿到 100 分但代码是抄的,换个数据集立刻就会露馅。作业的价值在于过程,不在于那个分数。

4. 作业讲解怎么用:对答案的四步法,把题解变成自己的思路

4.1 三类讲解材料,定位完全不同

“讲解”在这里不单指录制视频,而是所有能告诉你“这份作业为什么这么写”的材料。常见形态有三类,我建议按不同方式使用。

第一类是 solution 代码,即官方或流传下来的完整答案。它适合用来做最终对照,但绝对不适合一开始就打开。第二类是讨论课题解,对应课程每周的 section 板书,里面会把作业里涉及的数学推导重新推一遍,比如逻辑回归的似然函数求导、反向传播的链式法则展开。第三类是注释版笔记和讲解视频,通常是往届学生整理的,重点在“这里为什么要加正则项”这类文字解释。

这三类的信任度要分清:solution 代码只看结果,不看过程,它默认你知识都具备了;讨论课题解看推导过程,是补数学细节的地方;注释版笔记看思路,但可能有个人理解偏差,遇到和讲义不一致的地方,以讲义推导为准。我见过不少人拿注释版笔记里的结论直接去改代码,结果作业分数时好时坏,因为笔记省略了边界条件。

4.2 对答案四步法:先看结论、再看公式、最后比参数

对答案不是打开 solution 逐行比对,那样只会得到一份抄得整齐的代码,得不到思路。我习惯按四个步骤走,每一步都在解决一个具体问题。

第一步,先跑通自己的代码,记录卡点。如果你某一步完全不会写,把报错信息和卡住的位置截图留下来。第二步,打开 solution,先不看代码,只看它对应的输出结果,比如代价函数的最终数值、准确率是多少,拿自己的结果和它比。数字对得上,说明思路八成没问题;对不上,进入第三步。

第三步,回到讨论课题解或讲义里找对应公式。比如代价不一致,去看讲义里代价函数的定义是不是少了正则项;准确率偏低,去看特征预处理那一段。第四步,再回到自己的代码,只改出问题的那个函数,不要顺手把整个文件换成 answer。改完重跑一遍,确认数字和 solution 一致后,关掉 solution,隔一天后凭记忆重写一遍这个函数,能写出来才算真正吸收了。

4.3 一份作业的考点对照表与讲解重点

如果你手里是经典版本的作业,考点大致如下。后续年份可能把 SVM 单独拆出来做成 ex6,但主线不变。

作业核心考点对答案时盯什么
ex1线性回归、梯度下降、特征缩放代价曲线下降趋势;特征缩放前后收敛速度差异
ex2逻辑回归、正则化、决策边界决策边界画得是否合理;正则化系数对边界的影响
ex3多分类、one-vs-all、基础神经网络测试集准确率;哪些数字类最容易混淆
ex4正向传播、反向传播、梯度检查梯度检查的误差量级;向量化实现是否比循环快
ex5偏差方差、学习曲线、正则化学习曲线形状;高偏差与高方差的判断依据

对答案时盯“为什么”比盯“是什么”更有效。比如 ex4 的梯度检查,很多人只把它当作一道验证步骤跑完就结束,实际上它的原理是“用数值微分近似解析梯度,再对比两者差异”,这个思想在面试里可以直接转化为“如何验证自定义层的反向传播是正确的”。讲解视频讲到这一步时,我建议暂停下来自己推一遍链式法则,不要快进。

5. 避坑与排查:从讲义对不上到 submit 报错,五个常见翻车现场

5.1 讲义版本对不上,搜索答案搜不到

现象:想查“神经网络”章节,明明下载的资料里应该有一章,翻遍目录找不到,或者章节编号与网上文章里提到的完全对不上。

原因:CS229 讲义存在多个年份的版本,早期版本里深度学习只有一小节,后期版本才单列为完整讲义。网上大部分经验文章都基于某个特定年份,你手里的可能是另一个版本。

解决:先看你下载资料里的课程主页快照或 README,确认年份,再按那份资料自己的目录去学习。搜索答案时,优先搜公式名或作业文件名,比如“CS229 ex4 gradient checking”,而不是“CS229 neural networks 讲义第几章”。

5.2 装错 Octave 版本,submit 报函数未定义

现象:跑作业本身正常,一执行 submit 脚本就报“某个函数未定义”,或者脚本直接中断。

原因:判分脚本依赖多年前的 Octave API,新版本改动了一些内部函数的行为,不一定向后兼容。

解决:按作业说明里指定的大版本安装,不要贪新。装完后先跑一遍官方的示例函数验证环境,再碰作业代码。自测阶段如果 submit 一直报编译错误,可以直接放弃在线提交,用离线自检代替,不影响学习效果。

5.3 不看作业 PDF 直接填代码,函数签名对不上

现象:在.m文件里写完函数,运行主脚本时报“输出参数数量不正确”。

原因:代码骨架文件里只留了 TODO 注释和函数入口,具体要返回几个值、参数的顺序是什么,全部写在ex*.pdf里。跳读 PDF 等于漏掉一半条件。

解决:每个作业开始之前,先打开对应题目的 PDF,把所有出现“You should implement”“You need to return”的地方划出来,再动代码。填一个函数跑一次,不要一口气全部写完再调试。

5.4 用 Python 迁移作业,读入.mat后维度反向

现象:用scipy.io.loadmat读入数据后,打印y.shape得到的是(1, m)而不是(m, 1),广播运算时报维度不匹配。

原因:MATLAB 存储矩阵按列优先,且对于单列数据经常存成行向量;NumPy 的习惯是行优先,默认把每一行当样本。不加处理直接迁移,维度对不上是必然的。

解决:读入后立刻检查 shape,用np.reshape或np.ravel统一成自己习惯的维度。迁移作业时,不要照抄 MATLAB 的下标运算,先花十分钟把数据维度在纸上画清楚。

5.5 讲解看得多、自己写得少,关掉视频就卡壳

现象:看题解和讲解视频时觉得全都懂了,结果关上屏幕自己写,连theta的更新公式都要翻讲义。

原因:讲解是单向输入,写代码是主动输出。看一百遍推导不如自己推一遍,这是学习规律,不是能力问题。

解决:规定自己“先写后看”。每个作业给自己一小时独立实现时间,卡住了只看对应步骤的讲解片段,一次不超过十五分钟。我当时做 ex4 用了两个晚上,第一天连方法都写不全,第二天只看了五分钟题解就自己补完了,印象比看一整节视频深得多。

6. 把作业重写一遍才算学会:从 Octave 到 NumPy 的自测方法

学完一轮之后,验证自己是否真掌握了,我建议用“换个框架重写”的方法。具体做法是:挑一份最小作业,比如 ex1,用 NumPy 重写一遍线性回归,保持数据不变、超参数不变,最后和 Octave 版本的结果对比。

import numpy as np # 读取同样的数据,注意 CSV 分隔符 data = np.loadtxt('ex1data1.txt', delimiter=',') X, y = data[:, 0], data[:, 1] # 原始特征和标签 m = len(y) # 构造设计矩阵,加截距列 X = np.c_[np.ones(m), X] theta = np.zeros(2) alpha = 0.01 iterations = 1500 # 批量梯度下降 for i in range(iterations): grad = X.T @ (X @ theta - y) / m theta -= alpha * grad # 用解析解做基准验证 theta_best = np.linalg.inv(X.T @ X) @ X.T @ y print('梯度下降 theta:', theta) print('解析解 theta:', theta_best)

这段代码的核心是验证X @ theta和X.T @ (X @ theta - y)这两个运算是否和 MATLAB 版本一致。@是 NumPy 的矩阵乘法,X.T是转置,grad的维度由矩阵乘法自动保证。输出两个theta后,如果数值差异在1e-3以内,说明你的迁移实现没有逻辑错误。

我自己的自测清单是三条:数值对得上,代价曲线形态一致,换一份数据集仍然能收敛。除了解析解,也可以把每次迭代的代价打印出来,画一张和 Octave 版几乎重合的曲线,那比任何提交分数都有说服力。这个“重写”习惯帮我避了很多“一看就会、一写就废”的坑——面试现场写朴素贝叶斯、手推反向传播,靠的都是这类重复劳动。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进