简介:这份资源面向正在学习或复习吴恩达机器学习课程的学习者,尤其适合需要对照编程作业动手实践、又希望有中文笔记辅助理解的同学。包内包含课后编程作业的无答案版与有答案版(有答案版又细分三版),并附作业题目合并版PDF可直接打印;同时收录课堂讲义PDF与中文个人笔记,覆盖线性回归、逻辑回归、K-means图像压缩、人脸数据PCA等经典实验场景,便于按章节对照练习与复盘。资源共1028个文件,以m脚本、txt说明、mat数据、ipynb笔记本和pdf文档为主,另有少量py脚本与图片,压缩包约202.33MB,目录按作业与讲义模块组织,检索方便。目前已有2537人学习下载,适合希望系统刷题、核对答案思路并借助中文笔记查漏补缺的初中级学习者。
1. 从一份“双版本”作业包说起:为什么它值得你花一个周末
很多人学机器学习卡在同一个地方:视频看完了,公式也抄了,真让你从零写一个逻辑回归的代价函数和梯度下降,手还是抖的。这份资源就是冲着这个痛点来的——它把某门经典机器学习课程的编程作业整理成了两套:一套是留白的无答案版,一套是填好的有答案版,外加一份个人笔记。无答案版适合你关掉参考、自己硬啃;有答案版在你卡死两小时后用来对照,看自己错在维度、错在向量化、还是错在正则项系数。笔记则把每节的核心推导和踩坑点串了一遍。适合谁?适合已经能跑 Python、想系统补齐监督学习与无监督学习基本功的从业者,也适合带新人做训练营的工程师拿它当练习骨架。它不教你调包,它逼你把梯度推一遍。
2. 拆开压缩包:两版作业与笔记怎么配合用
2.1 目录结构与文件职责
拿到压缩包先别急着解压到桌面,建议单独建一个工作目录,把两版分开存放,避免自己改着改着把答案版覆盖成半成品。常见的目录组织是这样:
ml-homework/ ├── blank/ # 无答案版,练习用 │ ├── ex1_linear/ │ ├── ex2_logistic/ │ ├── ex3_multiclass/ │ └── ... ├── solved/ # 有答案版,对照用 │ └── ... └── notes/ # 个人笔记 ├── 01_linear_regression.md └── ...无答案版里通常保留了函数签名、数据加载脚本和测试入口,只把核心计算部分挖空,常见形式是TODO注释或直接pass。有答案版则是完整实现。笔记一般是 Markdown 或 PDF,按周次或按算法分节。这里的关键不是文件多整齐,而是你要建立一条使用顺序:先读笔记对应章节,再在 blank 里写,写完跑测试,跑不过再去 solved 里定位差异。反过来先看答案,练习价值基本归零,这是血泪经验。
2.2 环境准备与依赖版本
这类作业包年代跨度可能不小,直接pip install最新版 numpy 有时会碰到np.float被移除、矩阵乘法运算符行为变化之类的问题。稳妥做法是建虚拟环境并锁一个偏保守的版本组合:
python -m venv mlhw source mlhw/bin/activate # Windows 用 mlhw\Scripts\activate pip install "numpy<2.0" scipy matplotlib pandas jupyter逻辑说明:numpy<2.0是为了避开 2.x 里一批旧别名被彻底删除导致的AttributeError;scipy主要给优化器用(很多作业的fmincg或minimize依赖它);matplotlib用于作业里的可视化检查,画不出图往往说明你的预测函数输出形状不对。参数上,Python 建议 3.8~3.11,太老的 3.6 装不上新版 scipy,太新的 3.13 又可能和旧版 numpy 轮子不匹配。装完先跑一个最小自检:
import numpy as np print(np.__version__) a = np.array([[1, 2], [3, 4]]) print(a @ a) # 确认矩阵乘法正常如果这里就报错,别往下走,先把环境修好。很多“作业跑不通”的求助,最后查出来是环境里混了两个 numpy。
2.3 无答案版的使用节奏
无答案版的价值在于强制你面对空白。建议按“读笔记 → 手推公式 → 写向量化实现 → 跑测试 → 对答案”五步走,每步之间不要跳。以线性回归的代价函数为例,blank 里通常长这样:
def compute_cost(X, y, theta): # TODO: 返回标量代价 J pass你写完自己的版本后,先别急着看 solved,用一个小规模手算例子验证:取X=[[1,1],[1,2]]、y=[1,2]、theta=[0,0],手算 J 应该是多少,再和函数输出比。这个习惯能挡掉一大半“公式对但实现错”的情况。测试脚本一般会打印期望值和你的值,差在小数点后很多位通常只是浮点误差,差在量级上就是真错了。
2.4 有答案版的正确打开方式
有答案版不是拿来抄的,是拿来“对差异”的。推荐做法是:你的实现跑测试失败后,不要整段替换,而是逐行对比关键几行——代价函数看有没有漏掉1/(2m),梯度看有没有转置错,正则化看theta[0]有没有被误加惩罚。对照时把 solved 里的实现复制到一个临时文件,用diff或并排看,只改你怀疑的那一行,改完立刻重跑测试。这样你记住的是“我错在哪个思维盲区”,而不是“答案长什么样”。熟手可以进一步把 solved 当回归测试基准:自己重写一版,和 solved 的输出做数值比对,误差控制在1e-8以内算通过。
3. 核心作业的复现路径:从代价函数到向量化梯度
3.1 线性回归:代价函数与梯度下降的数值校验
线性回归是整包的起点,也是后面所有算法的地基。无答案版一般要求你实现compute_cost和gradient_descent。向量化写法如下:
import numpy as np def compute_cost(X, y, theta): m = len(y) residual = X @ theta - y # 形状 (m,) J = (residual @ residual) / (2 * m) return J def gradient_descent(X, y, theta, alpha, num_iters): m = len(y) J_history = np.zeros(num_iters) for i in range(num_iters): residual = X @ theta - y theta = theta - (alpha / m) * (X.T @ residual) J_history[i] = compute_cost(X, y, theta) return theta, J_history逻辑说明:residual @ residual等价于残差平方和,比np.sum(residual**2)少一次临时数组分配;梯度那行X.T @ residual是(n+1, m) @ (m,),得到(n+1,),正好和theta同形,不需要额外 reshape。参数上,alpha取 0.01 起步,num_iters先给 400,然后看J_history是否单调下降——如果第 5 次迭代就变成nan或突然变大,八成是学习率太大或忘了特征归一化。特征归一化这一步在作业里通常单独一个函数,别跳过,否则梯度下降收敛极慢,你会误以为自己公式写错了。
数值校验是这一节的精髓:写完梯度后,用中心差分近似验证解析梯度。
def numerical_gradient(X, y, theta, eps=1e-4): grad = np.zeros_like(theta) for i in range(len(theta)): t_plus, t_minus = theta.copy(), theta.copy() t_plus[i] += eps t_minus[i] -= eps grad[i] = (compute_cost(X, y, t_plus) - compute_cost(X, y, t_minus)) / (2 * eps) return grad把解析梯度和数值梯度相减,相对误差小于1e-6才算过。这个检查在逻辑回归、神经网络反向传播里同样适用,是排查“梯度写错但损失还在降”这类玄学问题的后悔药。
3.2 逻辑回归:正则化项与 sigmoid 的数值稳定
逻辑回归的坑集中在两处:正则化到底要不要惩罚偏置项,以及 sigmoid 在极端输入下溢出。代价函数常见实现:
def sigmoid(z): return 1 / (1 + np.exp(-z)) def cost_function_reg(X, y, theta, lam): m = len(y) h = sigmoid(X @ theta) eps = 1e-12 data_cost = -(y @ np.log(h + eps) + (1 - y) @ np.log(1 - h + eps)) / m reg = (lam / (2 * m)) * (theta[1:] @ theta[1:]) return data_cost + reg逻辑说明:eps是为了防止log(0)产生-inf,虽然理论上 h 不会精确等于 0 或 1,但浮点下极端参数会让它贴边;正则项用theta[1:]跳过偏置,这是作业里反复强调的点,漏掉会让决策边界偏移。参数lam是正则化强度,作业里常给 1 或 0.1,调大能压过拟合,但太大会让所有参数趋近 0、模型欠拟合。梯度实现里同样只对theta[1:]加(lam/m)*theta[1:]。
sigmoid 的数值稳定版可以写成:
def sigmoid_stable(z): out = np.empty_like(z, dtype=float) pos = z >= 0 out[pos] = 1 / (1 + np.exp(-z[pos])) neg = ~pos out[neg] = np.exp(z[neg]) / (1 + np.exp(z[neg])) return out这样在 z 很大或很小时都不会溢出。作业数据规模小,普通版通常够用,但如果你拿它跑自己的数据,这个版本能省掉一次深夜排查。
3.3 多分类与神经网络:前向传播的维度对齐
多分类用 one-vs-all 逻辑回归,核心是把标签转成 one-hot,然后对每个类别训练一个分类器。维度对齐是这里最大的翻车点:X是(m, n),Theta是(num_labels, n+1),预测时X @ Theta.T得到(m, num_labels),取argmax才是类别。神经网络作业的前向传播同理,每一层的a形状要和下一层Theta的列数对上,错一位就是广播错误或者结果全错但不报错。建议每写一层就打印一次形状:
def forward_propagate(X, Theta1, Theta2): a1 = np.hstack([np.ones((X.shape[0], 1)), X]) z2 = a1 @ Theta1.T a2 = sigmoid(z2) a2 = np.hstack([np.ones((a2.shape[0], 1)), a2]) z3 = a2 @ Theta2.T a3 = sigmoid(z3) return a1, z2, a2, z3, a3逻辑说明:hstack补偏置列是作业里的固定套路,注意补的是列不是行;Theta1.T是因为作业里参数矩阵按“输出×输入”存。写完先拿作业给的小规模权重做一次前向,和预期输出比对,再上完整数据。反向传播的梯度校验同样用 3.1 的中心差分,把Theta1、Theta2展平成一维再逐元素扰动,这一步能抓出 90% 的维度错位。
4. 避坑与排查:这份作业包最容易翻车的五个地方
4.1 现象:测试脚本报ValueError: shapes not aligned
原因:矩阵乘法两侧维度不匹配,最常见是theta用了行向量而X是(m, n+1),或者补偏置时补错了轴。解决:在报错行前打印X.shape、theta.shape,确认X @ theta的内维一致;补偏置统一用np.hstack([np.ones((m,1)), X]),不要用np.c_[...]混着来。
4.2 现象:代价函数输出nan
原因:log(0)或学习率过大导致参数爆炸,也可能是特征没归一化、数值量级差太多。解决:给log加eps;把alpha降到 0.001 重跑;检查归一化是否在训练集上算的均值和标准差、并应用到验证集,而不是各算各的。
4.3 现象:梯度校验通过,但训练不收敛
原因:校验用的是小规模随机点,掩盖了正则化项或偏置项处理错误;也可能是迭代次数不够。解决:把num_iters加到 2000 看损失曲线是否还在降;单独检查theta[0]的梯度是否被正则项污染;用J_history画图,单调不降就是学习率或符号问题。
4.4 现象:有答案版能跑,自己改完就错
原因:对照时整段替换,把 solved 里依赖的辅助函数或数据预处理一起覆盖了。解决:只替换你怀疑的那一个函数体,保留文件其余部分;改完用git diff或备份对比,确认改动范围;跑测试前先确认数据加载函数没被动过。
4.5 现象:笔记里的公式和代码对不上
原因:笔记是个人整理,符号约定可能和作业代码不一致,比如X是否已含偏置列、y是列向量还是行向量。解决:以作业测试脚本的输入形状为准,把笔记里的公式按代码的符号重写一遍贴在函数上方;不确定时用 3.1 的手算小例子反推约定,别硬套。
5. 把作业包变成自己的训练集:进阶用法与验证习惯
这份资源真正的价值不在“做完”,而在“改着做”。等你把基础作业跑通一遍,建议做三件事把它变成自己的东西。第一,把无答案版的测试脚本抽出来,改成参数化测试:同一份实现,换不同alpha、lam、num_iters跑一组,记录损失曲线和最终精度,做成一张小表。这样你能直观看到正则化强度对训练/验证误差的影响,而不是背“lam 太大会欠拟合”。第二,拿有答案版的实现当基准,自己重写一版向量化更彻底的代码,比如把梯度下降里的循环去掉、用scipy.optimize.minimize替代手写循环,然后比对两者在相同初始点下的最终参数,误差在1e-6内算等价。第三,把笔记里的推导补成“公式—代码—数值校验”三件套,每个算法留一个最小可运行例子,以后带新人直接甩这个例子,比讲半小时理论管用。
验证习惯上,我一般会强制走一遍“三查”:查形状(每层输入输出打印一次)、查梯度(中心差分相对误差小于1e-6)、查损失(单调下降,不降就停)。这三查在神经网络作业里尤其救命,因为反向传播写错时损失往往还在缓慢下降,不查梯度根本发现不了。还有一个容易忽略的点:作业里的数据划分是固定的,你自己改数据时记得固定随机种子,否则两次运行结果不可比,排查时会怀疑人生。
最后说句实在的,这类作业包网上版本很多,质量参差,这份“无答案+有答案+笔记”的组合胜在结构完整、能自己对自己。用的时候别贪快,一个算法一个算法地啃,卡住了先看笔记的推导,再对答案的差异,最后回到数值校验。从那以后我每次带人做机器学习练习,都强制先跑一遍梯度校验再谈调参,省下来的排查时间远比写校验代码多。希望帮到你。
本文还有配套的精品资源,点击获取