news 2026/10/31 12:51:57

机器学习编程作业双版本:无答案+有答案+笔记,从零手推梯度

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
机器学习编程作业双版本:无答案+有答案+笔记,从零手推梯度

简介:这份资源面向正在学习或复习吴恩达机器学习课程的学习者,尤其适合需要对照编程作业动手实践、又希望有中文笔记辅助理解的同学。包内包含课后编程作业的无答案版与有答案版(有答案版又细分三版),并附作业题目合并版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)、查损失(单调下降,不降就停)。这三查在神经网络作业里尤其救命,因为反向传播写错时损失往往还在缓慢下降,不查梯度根本发现不了。还有一个容易忽略的点:作业里的数据划分是固定的,你自己改数据时记得固定随机种子,否则两次运行结果不可比,排查时会怀疑人生。

最后说句实在的,这类作业包网上版本很多,质量参差,这份“无答案+有答案+笔记”的组合胜在结构完整、能自己对自己。用的时候别贪快,一个算法一个算法地啃,卡住了先看笔记的推导,再对答案的差异,最后回到数值校验。从那以后我每次带人做机器学习练习,都强制先跑一遍梯度校验再谈调参,省下来的排查时间远比写校验代码多。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 12:51:15

RAGFlow 四层存储架构深度解析:元数据、对象、检索、缓存协同机制

1. 项目概述&#xff1a;RAGFlow 四层存储不是“堆砌”&#xff0c;而是精密齿轮咬合 你打开 RAGFlow 的文档&#xff0c;看到“元数据”“对象”“检索”“缓存”四个词并列排开&#xff0c;第一反应可能是——这又是个术语堆砌的宣传话术&#xff1f;我试过直接照着文档跑 de…

作者头像 李华
网站建设 2026/10/31 12:51:14

深度学习在经典名著推荐系统中的应用与实践

1. 项目概述与背景这个基于深度学习的经典名著推荐系统&#xff0c;本质上是一个结合现代机器学习技术与传统文学研究的交叉领域应用。我在开发过程中发现&#xff0c;当前数字阅读平台虽然拥有海量书籍资源&#xff0c;但普遍存在推荐精准度不足的问题——要么过度依赖热门榜单…

作者头像 李华
网站建设 2026/10/31 12:50:45

Langfuse源码审计:AI可观测平台的工程可信度验证

1. 这不是“又一个监控工具”&#xff0c;而是AI工程化落地的源码级信任锚点Langfuse被越来越多团队用作LLM应用的可观测底座&#xff0c;但几乎没人公开讲过&#xff1a;它的代码里到底有没有隐藏的逻辑陷阱&#xff1f;有没有被忽略的权限边界&#xff1f;有没有未文档化的状…

作者头像 李华
网站建设 2026/10/31 12:49:54

8MB小模型叫板DeepSeek V4 Flash:量化压缩与专项推理的边界

Cactus Needle 3这段时间讨论热度确实不低&#xff0c;核心就一句话&#xff1a;8MB的模型&#xff0c;号称能跟DeepSeek V4 Flash扳手腕。乍一听有点像标题党&#xff0c;但顺着技术细节摸了一遍之后&#xff0c;我得说这事儿比表面上看起来更有意思。这不是又出了一个“小钢炮…

作者头像 李华
网站建设 2026/10/31 12:49:42

C++语言特性变更对防御性编程的影响与应对策略

1. 项目概述"C 语言特性的变更可能让你的防御成为马奇诺防线"这个标题揭示了C语言演进过程中一个常被忽视的安全隐患。就像二战时期法国人引以为傲的马奇诺防线最终被德军绕道突破一样&#xff0c;程序员们精心构建的防御性代码也可能因为语言标准的更新而变得脆弱不…

作者头像 李华
网站建设 2026/10/31 12:48:56

Blender中文手册入门:版本配置、术语打通与跟练排错

简介&#xff1a;面向零基础及初级3D爱好者的Blender中文入门手册&#xff0c;以单份PDF形式系统梳理软件框架逻辑、界面布局与核心功能操作&#xff0c;帮助缺乏系统资料的初学者建立从建模、材质贴图到动画制作与3D渲染的完整学习路径。压缩包内仅含1个PDF文件&#xff0c;体…

作者头像 李华