简介:这份资料包面向希望系统学习机器学习的学习者与转行者,完整收录了吴恩达教授CS229课程的核心内容,涵盖讲义、作业与配套讲解,帮助读者在缺乏教师指导的情况下也能扎实掌握监督学习、无监督学习及强化学习等核心框架。资源共47个文件,以30份PDF讲义与复习笔记为主体,辅以7个zip作业数据包、6个m脚本和4个dat数据文件,压缩包约9.22MB,便于按主题检索与本地练习。讲义从线性回归、逻辑回归延伸至支持向量机、EM算法、因子分析、独立成分分析与强化学习,并配有线性代数、概率、凸优化、高斯过程、隐马尔可夫模型等复习笔记;作业包含编程任务与理论问题,配套解答则提供难点剖析与实现技巧。目前已有1322人学习下载,适合作为课程同步练习或面试前查漏补缺的参考材料。
1. 从一份课程资料包说起:CS229 讲义、作业与讲解到底该怎么用
很多人第一次接触机器学习,是从一份流传很广的课程资料包开始的——里面塞满了讲义 PDF、作业代码骨架、数据文件,还有配套的作业讲解。标题里说的「CS229 所有讲义+作业+作业讲解」,指的就是这样一套完整的课程材料集合。它解决的核心问题很直接:你想系统学一遍机器学习,但不想只停留在看视频、抄公式,而是想真正把每个算法从推导到代码实现跑通一遍。适合谁?适合已经会 Python、懂一点线代和概率、但面对「梯度下降」「正则化」「核方法」这些词还觉得虚的从业者或学生。这套材料的价值不在于「看完」,而在于「做完」——作业里的每一行代码,都是把讲义里的数学符号翻译成可运行逻辑的过程。我见过太多人把讲义当小说翻,翻完就忘;真正拉开差距的,是那些把作业一道一道啃下来、再对着讲解复盘的人。
2. 讲义、作业、讲解三件套的定位与配合方式
2.1 讲义负责建立数学直觉,不是用来「读完」的
讲义的核心作用是给你一套自洽的符号体系和推导链条。以监督学习那部分为例,从最小二乘到逻辑回归,再到广义线性模型,讲义是一步步把「为什么用这个损失函数」讲清楚的。我的习惯是:第一遍只扫结构,知道这一章在解决什么问题;第二遍对着公式手推一遍,尤其是矩阵求导和概率假设那几页;第三遍再回头看,把每个符号和作业里的变量对应上。不要指望一遍读懂,讲义是工具书,不是小说。
2.2 作业是把数学翻译成代码的唯一路径
作业通常以代码骨架加 TODO 的形式给出,你需要补全核心函数。这一步最忌讳直接找答案抄。正确做法是:先看函数签名和注释,想清楚输入输出维度,再对照讲义里的公式,一行行写。比如实现逻辑回归的损失函数,你要先确认theta是列向量还是行向量,X的每一行是一个样本还是每一列是一个样本。这些维度问题,讲义里往往一笔带过,但代码里错一个转置就全盘皆输。
2.3 作业讲解用来校准思路,不是用来对答案
讲解的价值在于展示「标准解法是怎么想的」。我一般会在自己写完并跑通之后,再去看讲解,重点对比三件事:向量化写法是否更简洁、数值稳定性处理是否到位、边界条件是否考虑周全。如果先看讲解再写作业,你会失去自己踩坑的机会,而踩坑恰恰是记忆最深刻的部分。
3. 用 Python 复现作业核心模块的最小闭环
3.1 环境准备与数据加载
这套材料原版多用 MATLAB/Octave,但现在用 Python 复现更实际。我一般用numpy做矩阵运算,matplotlib画图,scipy做优化。先建一个干净的环境,避免版本冲突。
# 创建独立环境,避免污染全局包 python -m venv cs229_env source cs229_env/bin/activate # Windows 用 cs229_env\Scripts\activate pip install numpy matplotlib scipy数据文件通常是.txt或.csv,用np.loadtxt或pd.read_csv读入。注意检查分隔符和是否有表头,很多新手在这里翻车,读进来全是 NaN。
import numpy as np # 假设数据文件是空格分隔,无表头 data = np.loadtxt('ex1data1.txt', delimiter=',') X = data[:, 0] # 第一列是特征 y = data[:, 1] # 第二列是标签 m = len(y) # 样本数 X = np.c_[np.ones(m), X] # 加一列全1,对应截距项这里np.c_是按列拼接,把全 1 列加在最前面,这样theta[0]就是截距。维度上X是(m, n+1),y是(m,),后面矩阵运算才不会出错。
3.2 向量化实现梯度下降
作业里最核心的循环就是梯度下降。用 for 循环写能跑但慢,向量化写法才是标准答案。
def compute_cost(X, y, theta): """计算线性回归的均方误差代价""" m = len(y) predictions = X.dot(theta) # (m,) errors = predictions - y # (m,) cost = (1 / (2 * m)) * errors.dot(errors) # 标量 return cost def gradient_descent(X, y, theta, alpha, iterations): """批量梯度下降,返回最优 theta 和代价历史""" m = len(y) cost_history = np.zeros(iterations) for i in range(iterations): predictions = X.dot(theta) errors = predictions - y # 梯度:X^T * errors / m gradient = (1 / m) * X.T.dot(errors) theta = theta - alpha * gradient cost_history[i] = compute_cost(X, y, theta) return theta, cost_historyalpha是学习率,一般从 0.01 试起,太大震荡不收敛,太小收敛慢。iterations看代价曲线是否走平,通常 1500 到 4000 次足够。X.T.dot(errors)这一步把每个样本的误差按特征加权求和,得到每个参数的梯度方向,这就是向量化的精髓——一次矩阵乘法替代了内层循环。
3.3 用作业讲解校准数值稳定性
自己写完跑通后,再看讲解里怎么处理数值问题。比如逻辑回归的损失函数,直接写log(sigmoid(z))在z很负时会溢出,标准做法是加一个极小值或者用np.logaddexp。
def sigmoid(z): """数值稳定的 sigmoid 实现""" # 对正负分别处理,避免 exp 溢出 return np.where(z >= 0, 1 / (1 + np.exp(-z)), np.exp(z) / (1 + np.exp(z))) def compute_cost_logistic(X, y, theta): m = len(y) z = X.dot(theta) h = sigmoid(z) # 加 1e-15 防止 log(0) cost = (-1 / m) * (y.dot(np.log(h + 1e-15)) + (1 - y).dot(np.log(1 - h + 1e-15))) return cost这个np.where的写法就是讲解里常强调的「分段稳定」,新手直接写1/(1+np.exp(-z))在z=-800时会得到inf或警告。参数上1e-15是经验值,够小又不至于影响精度。
4. 避坑与排查:作业跑不通时先看这五条
4.1 代价函数不下降反而增大
现象:梯度下降迭代后代价越来越大,甚至变成 NaN。原因通常是学习率alpha太大,或者忘了除以样本数m。解决:先把alpha降到 0.001 试,确认梯度公式里有没有1/m。另外检查theta初始值是不是全零,如果数据没归一化,特征尺度差异大会导致梯度爆炸。
4.2 矩阵维度不匹配报错
现象:ValueError: shapes not aligned。原因多半是X和theta的维度没对齐,或者y是行向量而X.dot(theta)是列向量。解决:在关键步骤后打印X.shape、theta.shape、y.shape,确保X.dot(theta)的结果和y形状一致。我一般会在函数开头加一行assert X.shape[0] == y.shape[0]。
4.3 正则化项加错位置
现象:加了正则化后代价比不加还大,或者梯度里正则项符号反了。原因:正则化只对参数theta[1:]生效,不该惩罚截距项theta[0];另外梯度里的正则项是(lambda/m)*theta,不是lambda*theta。解决:把theta[0]单独拿出来,正则项只作用于其余参数。
4.4 数据归一化后忘记同步处理预测数据
现象:训练时归一化了,预测新样本时直接代入原始值,结果完全不对。原因:归一化的均值和标准差只用了训练集,预测时必须用同一组参数。解决:把训练集的mu和sigma存下来,预测时用(x - mu) / sigma转换。
4.5 作业讲解里的写法跑不通
现象:照着讲解敲的代码报错。原因:讲解可能基于旧版本库,或者省略了辅助函数。解决:先确认numpy版本,检查是否漏了import,再看讲解里的变量名是否和你的骨架一致。不要怀疑讲解本身,先怀疑自己有没有抄漏。
5. 从跑通到吃透:用交叉验证和可视化验证你的实现
最后一章说一个我反复用的技巧:每写完一个模块,不要只看代价曲线下降就完事,用交叉验证和可视化把「黑匣子」打开。具体做法是,把数据集按 7:3 切成训练集和验证集,在训练集上跑梯度下降,每迭代 100 次就在验证集上算一次代价,画两条曲线。如果训练代价下降但验证代价上升,说明过拟合了,这时候就该调正则化参数lambda。
def train_with_validation(X, y, alpha, iterations, lambda_reg): """带验证集的训练,返回训练和验证代价历史""" m = len(y) split = int(0.7 * m) X_train, y_train = X[:split], y[:split] X_val, y_val = X[split:], y[split:] theta = np.zeros(X.shape[1]) train_hist, val_hist = [], [] for i in range(iterations): # 训练集梯度(含正则化) pred = X_train.dot(theta) err = pred - y_train grad = (1 / split) * X_train.T.dot(err) grad[1:] += (lambda_reg / split) * theta[1:] # 不惩罚截距 theta -= alpha * grad if i % 100 == 0: train_hist.append(compute_cost(X_train, y_train, theta)) val_hist.append(compute_cost(X_val, y_val, theta)) return theta, train_hist, val_histlambda_reg从 0 开始试,逐步加到 0.1、1、10,观察验证代价最低点。这个参数没有固定答案,取决于数据量和特征数。我一般会画一张lambda对验证代价的曲线,选拐点附近的值。
另一个技巧是可视化决策边界。对于二维特征,把theta代入theta[0] + theta[1]*x1 + theta[2]*x2 = 0,解出x2关于x1的直线,画在散点图上。如果直线明显偏离两类点的分界,说明要么迭代不够,要么特征没选对。这一步能帮你直观判断模型是不是真的学到了东西,而不是只看一个代价数字。
最后说个血泪经验:这套材料里的作业,每一道都值得你关掉讲解、自己从头写一遍。我当初图快,对着讲解抄了一遍逻辑回归,结果过了一个月再让我手写向量化梯度,完全写不出来。后来逼自己把每道题重做三遍,第一遍看讲义写,第二遍关讲义写,第三遍给别人讲一遍,才算真正吃透。别怕慢,怕的是你以为自己会了。希望帮到你。
本文还有配套的精品资源,点击获取