ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

机器学习作业通关:监督学习、神经网络与无监督复习清单

机器学习作业通关:监督学习、神经网络与无监督复习清单

简介:这份资料包围绕吴恩达机器学习课程的课后编程作业整理,面向正在系统学习机器学习、需要动手复现算法并核对结果的读者。内容包含无答案与有答案两版作业,其中有答案部分又分为三版,另附可直接打印的作业题目合并版与课堂讲义,便于按课程进度练习、自测和对照复盘;个人中文笔记同样支持打印,适合课前预习与期末梳理。压缩包共1028个文件、约202.33MB,以623个m脚本、103个mat数据、61个ipynb笔记本为主,另有146个txt说明、44个pdf讲义与笔记、11个py脚本及少量图片和日志,覆盖线性回归、逻辑回归、PCA人脸数据、K-means图像压缩等练习场景。已有2538人学习下载。读者可据此搭建完整的课程练习目录,借助.ipynb与.m文件对照实现,利用答案版检查参数与输出,再结合中文笔记和讲义理解推导过程,形成从题目、代码到复盘的闭环。

1. 先看清这门课的坐标系:机器学习作业里到底在练什么

很多人拿到一份机器学习作业,第一反应是找答案——这恰好是最容易走偏的起点。这门经典入门课的知识版图其实很规整:一条监督学习主线(线性回归、逻辑回归、正则化),一层神经网络(前向、反向、调试),一组无监督与推荐系统(K-means、PCA、异常检测、协同过滤),外加贯穿始终的工程习惯(向量化、维度对齐、评测口径)。无答案版练的是“从公式推到能跑通的代码”,有答案版练的是“对拍与自检”,个人笔记则是把两次踩坑压缩成可检索的索引。这套材料适合已经看完视频、却卡在“看得懂写不出”的人,也适合考前想快速过一遍全链路的人。地图先记住一句话:监督学习管预测,神经网络管拟合复杂边界,无监督管结构发现,工程习惯决定你能不能按时交卷。

2. 监督学习主线:假设函数、代价函数与梯度下降的三段式推导

监督学习部分是整份机器学习作业里占比最重、也最容易被“背公式”糊弄过去的一块。它的统一套路只有三步:先写假设函数 h(x),再定义代价函数 J(θ),最后用梯度下降或解析解把 θ 求出来。绝大多数作业题的差别,只是这三步里换了函数形态——线性回归换的是 h 和 J,逻辑回归换的是输出层映射,正则化换的是 J 的尾部。把这条三段式吃透,后面神经网络的反向传播不过是把它在多层结构上重写一遍。

2.1 线性回归:梯度下降与正规方程是两条不同的路

线性回归的假设函数是 h(x)=θᵀx,代价函数取均方误差的一半,乘 1/2 是为了求导时把平方的 2 约掉,这个细节在作业里经常被问“为什么不是 1/m”。梯度下降的更新式看着简单,落到代码里有两个必须过的坎:一是特征缩放,房屋面积动辄上千、房间数只有个位数,不归一化的话代价函数等高线会被拉成细长椭圆,学习率稍大就震荡发散;二是向量化,用 for 循环逐样本累加在作业评测里通常过不了时间限制。

import numpy as np def compute_cost(X, y, theta): # X 形状 (m, n+1),第一列已补 1;theta 形状 (n+1, 1);y 形状 (m, 1) m = len(y) residual = X @ theta - y # 一次性算出所有样本的残差 return float(residual.T @ residual) / (2 * m) def gradient_descent(X, y, theta, alpha, iters): m = len(y) history = [] for _ in range(iters): residual = X @ theta - y grad = (X.T @ residual) / m # (n+1, m) @ (m, 1) -> (n+1, 1) theta = theta - alpha * grad history.append(compute_cost(X, y, theta)) return theta, history

这段代码的重点不在写法,而在三个可调项的含义:alpha决定每步走多远,iters决定走多少步,history决定你能不能看出走歪了。实际跑的时候,先打印history[0]和history[-1],如果代价不降反升,八成是 alpha 太大或忘了特征缩放;如果降得极慢,则是 alpha 太小或特征量纲差异过大。正规方程 θ=(XᵀX)⁻¹Xᵀy 是另一条路,一步出解、不用调学习率,但代价是 O(n³) 的求逆开销,而且当特征线性相关时 XᵀX 不可逆,作业里常拿“特征数远大于样本数”来考这个点,标准处理是加一点正则化或者删冗余特征。

2.2 逻辑回归:分类问题为什么要换掉平方损失

逻辑回归把线性输出塞进 sigmoid,得到 0 到 1 之间的概率,再用 0.5 作阈值切成两类。重点在于:它虽然叫“回归”,损失函数却不能沿用均方误差。把 sigmoid 套进平方损失后,代价函数对 θ 是非凸的,梯度下降会停在局部极小;换成对数似然(交叉熵)形式,代价函数重新变凸,梯度形式还和线性回归长得一模一样——(h(x)-y)x_j。这个“形式相同、含义不同”的巧合是作业和考试的高频考点,答的时候要能说清:线性回归里 h 是实数输出,逻辑回归里 h 是概率,梯度相同只是数学上的巧合。

决策边界是另一个易混点。边界画在 h(x)=0.5 的位置,对应 θᵀx=0,所以边界是线性的,但特征可以人工构造多项式项,把边界掰成圆形或不规则形状——作业里那道“用多项式特征拟合非线性可分数据”的题,考的就是这个:特征工程决定边界形状,正则化强度决定边界平滑程度。多分类则用一对多,为每个类别训一个二分类器,预测时取概率最大的那个,注意每个分类器的输出概率之间没有归一化约束,不要直接当概率分布用。

2.3 正则化与偏差方差:λ 到底在惩罚谁

正则化项加在代价函数尾部,λ 越大,参数被压得越接近零,模型越简单。作业里最容易出错的地方是下标:正则化求和从 j=1 开始,θ₀ 也就是偏置项不参与惩罚。原因不玄学——偏置只控制整体平移,不影响曲线形状,惩罚它只会让模型被迫去拟合数据均值之外的偏移,得不偿失。在梯度下降里体现为:θ₀ 的更新式和原来一样,θ₁ 到 θₙ 的更新式多减一项 (λ/m)θⱼ。

偏差方差诊断靠学习曲线:横轴是训练样本数,纵轴是误差,画训练误差和验证误差两条线。两条线都很高且贴得近,是欠拟合,加特征、加多项式项、减小 λ;训练误差低但验证误差高且中间有明显 gap,是过拟合,加样本、加正则、减特征。这套判断在机器学习作业里几乎是送分题,但真正做题时很多人会跳过画图直接调参,结果调了半天不知道往哪个方向调。学完这块再回头看书面的机器学习笔记,会发现自己之前抄的那些公式终于连成了一条线:λ 控制复杂度,复杂度控制方差,方差决定验证误差。

3. 神经网络与训练细节:前向传播、反向传播与调参顺序

神经网络这一章在作业里通常是分水岭——前面的题靠推导能过,这里的题靠维度对齐才能过。它的本质是把逻辑回归重复堆叠:每一层先做线性变换 z=Wa+b,再过激活函数 a=g(z),最后一层接分类或回归的损失。难点不在概念,在于参数矩阵的形状一旦对不上,报错信息往往指向一个和真实错误无关的位置,让人以为是数据问题。

3.1 网络结构与前向传播的矩阵维度

设输入层 n 个单元、隐藏层 h 个单元、输出层 k 个单元,那么第一层权重 W¹ 的形状是 (h, n),第二层 W² 是 (k, h)。作业里给的参数通常是打平成一维向量的,第一步就得按这个形状 reshape 回去,reshape 错了后面全错。前向传播逐层往下写,每层记得给 z 补一列偏置,或者在矩阵乘法前手动加一列 1,两种写法不能混用,混用就会出现“隐藏层偏置加了两遍”这种隐蔽 bug。

激活函数的选择也有讲究:隐藏层用 sigmoid 在浅网络里能跑,但层数一多梯度会饱和,输出层做多分类时用 softmax 把 k 个输出归一化成概率分布,损失用交叉熵。作业里若出现“输出全为同一个类别”,先查 softmax 是否按行归一化,再查标签的独热编码是否和输出维度对齐。

3.2 反向传播:链式法则落到矩阵上

反向传播的核心是误差项 δ 的递推:输出层 δ 等于预测减真实值(这个形式和逻辑回归梯度同源),往前的每一层 δ 等于后一层 δ 乘权重矩阵再乘激活函数导数。梯度则由 δ 与本层输入的转置相乘累加得到。整个过程可以用一个检查动作兜底:数值梯度检查。

def relative_error(nn_params, X, y, epsilon=1e-4): # 仅在调试时用:逐个扰动参数,比较数值梯度与解析梯度的相对误差 num = np.zeros_like(nn_params) for i in range(nn_params.size): p_plus, p_minus = nn_params.copy(), nn_params.copy() p_plus[i] += epsilon p_minus[i] -= epsilon num[i] = (cost(p_plus, X, y) - cost(p_minus, X, y)) / (2 * epsilon) ana = analytic_grad(nn_params, X, y) return np.linalg.norm(num - ana) / (np.linalg.norm(num) + np.linalg.norm(ana) + 1e-12)

相对误差在 1e-9 量级说明解析梯度写得对,1e-4 以上基本可以判定某层下标错位。这里有两个必须遵守的纪律:一是关掉正则化再检查,否则正则项梯度会污染数值梯度;二是不在训练循环里调用,O(参数个数) 次前向传播的开销会让训练慢到怀疑人生。作业里绝大多数“训练不收敛”的报错,用这一次检查就能定位到具体是哪一层。

3.3 训练不收敛时的排查顺序

排查要按成本从低到高排:先看代价曲线,如果一开始就 NaN,是学习率过大或出现了 log(0),给概率加个极小值截断;如果代价平稳但很高,是容量不足或正则过强;如果代价下降后反弹,是学习率没随训练衰减。接着查初始化,所有参数初始化为零会导致同层神经元完全对称、梯度永远相同,必须随机初始化并保证对称破缺。然后查数据,标签编码、特征缩放、训练集与验证集的划分顺序都会影响结果。最后才怀疑网络结构,加层、加单元属于高成本操作,放在最后做。这套顺序看着朴素,但在机器学习作业里能省下大量无效试错。

4. 无监督学习与推荐系统:聚类、降维、异常检测的分工

无监督部分没有标签,因此作业的评分方式也从“预测对不对”变成了“结构合不合理”。这一块最忌把几个算法混为一谈:K-means 干的是把样本分组,PCA 干的是把维度压缩,异常检测干的是给每个样本打一个异常分数,协同过滤干的是补全用户对物品的评分矩阵。四件事的目标函数完全不同,只是都长得像“找相似”。

4.1 K-means 与 PCA:目标函数与使用边界

K-means 的目标是最小化每个样本到所属簇中心的距离平方和。算法两步循环:分配样本到最近中心,再用簇内均值更新中心。两个重点:一是初始化敏感,随机选点可能收敛到很差的局部最优,作业里通常要求多次随机初始化取代价最小的那一次;二是 K 的选择没有解析答案,肘部法看代价随 K 的下降曲线拐点,或者按业务可解释性定。另一个常被忽略的细节是:K-means 对量纲敏感,特征不缩放的话,数值大的那一维会主导距离计算。

PCA 则是找一组正交方向,让数据投影后的方差最大。实现上先对特征去均值,再做协方差矩阵的特征值分解或直接对数据矩阵做奇异值分解,取前 k 个主成分投影。

def pca_project(X, k): # 必须先去均值:否则第一主成分会被整体偏移方向带偏 Xc = X - X.mean(axis=0) U, S, Vt = np.linalg.svd(Xc, full_matrices=False) Z = Xc @ Vt[:k].T # 投影到前 k 个主成分,形状 (m, k) kept = S[:k].sum() / S.sum() # 方差保留率,作业常要求 99% 以上 return Z, kept

kept这个值决定了 k 取多少,一般要求保留 99% 左右的方差。需要分清的是:PCA 不是特征选择,它产生的新特征是原特征的线性组合,原始含义会丢失;它也不能替代正则化,降维解决的是计算与可视化,过拟合还得靠 λ 或更多数据。K-means 与 PCA 常被串起来用——先降维再聚类能加速,但降维后的簇结构未必和原始空间一致,作业里如果要求解释簇的含义,最好在原始特征上再看一遍中心点。

4.2 异常检测与协同过滤:概率模型与矩阵分解

异常检测假设正常样本在每一维上服从高斯分布,把各维概率连乘得到联合概率,低于阈值就判为异常。重点在阈值怎么定:不能凭感觉设,要用带标签的验证集试不同阈值,用 F1 分数挑最优。因为异常样本极少,准确率这个指标在这里几乎没有意义,全判正常也能有 99% 的准确率。另一个分支是多元高斯,它捕捉特征之间的相关性,但要求协方差矩阵可逆,样本数必须大于特征数,作业里常拿这个条件设陷阱。

协同过滤走的是完全不同的路:从评分矩阵出发,学习用户特征向量和物品特征向量,用两者内积逼近已有评分,再拿内积去预测缺失项。两个重点:一是均值归一化,某个用户一次都没评过的时候,他的特征向量学不出来,预测会全为零,减去用户均值后至少能回退到均值水平;二是正则化不能省,评分矩阵极其稀疏,不加正则几乎必然过拟合到已观测的那几个点。

4.3 作业里这几类练习的评测口径

无监督作业的评分脚本通常不比对你的中间变量,而是比对最终输出的形状、数值容差和随机种子。K-means 的中心点顺序不唯一,所以评测往往按代价比较而非逐元素相等;PCA 的投影方向符号也可能相反,正负号不影响方差,评测会取绝对值或比较重构误差。理解这一点很关键:本地跑出来的结果和答案版数值差一个负号,不一定是错,先看评测逻辑再改代码。有答案版的最大价值就在这里——它能告诉你“差异在容差内”和“真的写错了”之间的界线在哪。

5. 易错点:这五处最容易翻车的地方

这一章是整份笔记里回看频率最高的部分。机器学习作业的错法有很强的规律性,绝大多数不是不会,而是漏了某个约定俗成的细节。下面五条按出现频率排,每条都按“错在哪、为什么会漏、怎么补”的顺序写,建议对着自己的作业本逐条打勾。

5.1 只对答案不看评测:向量化与形状的隐性错误

易漏点:代码跑通、结果肉眼看着和答案差不多,就认为过了。为什么漏:答案版给的是数值,不给中间过程的检查点,而形状错误经常被广播机制悄悄吃掉——本该是 (m,1) 的列向量被当成 (m,) 参与运算,结果对了但过程全错,换一份数据立刻崩。怎么补:在每个函数入口打印关键变量的 shape,尤其是 X、y、theta 三个;对矩阵乘法前后做一次手算校验,确认 (m,n)@(n,1) 得到 (m,1);能不用广播就不用,显式 reshape 虽然啰嗦,但出错时能立刻定位。

5.2 跳过无答案版:看懂和写出来之间隔着一条河

易漏点:直接打开有答案版,顺着注释读一遍,觉得逻辑都懂,于是跳过无答案版。为什么漏:阅读产生的是“识别记忆”,写代码需要的是“提取记忆”,两者在脑内的强度差一个量级;看着答案时每一步都顺理成章,合上答案连代价函数的 1/(2m) 都会犹豫。怎么补:每道题先在无答案版上独立写,卡住超过二十分钟再看一眼答案的关键一行,然后立刻关掉重写;写完隔一天再默写一遍。个人笔记在这一步的用法是只记“我当时卡在哪一行”,不抄完整代码,抄了也不会再看。

5.3 正则化项漏掉偏置项与截距列

易漏点:正则化求和从 j=0 开始,或者在构造设计矩阵时忘了补第一列 1,导致维度对不上却强行运行。为什么漏:公式里 Σ 的下标写着 j=1 到 n,看的时候注意到了,写循环时从 0 开始是肌肉记忆;补 1 那一步在视频里一笔带过,笔记里常常没有。怎么补:写一个专门的工具函数负责补列,全篇统一调用;正则化梯度单独写一行,用切片theta[1:]显式排除 θ₀;交卷前用小规模数据手算一次带正则的代价,和代码输出对一下。

5.4 学习率与迭代次数绑死调参

易漏点:代价不降就加大迭代次数,或者反复试学习率,但两个参数始终一起动。为什么漏:两个参数都影响最终代价,直觉上一起调更容易“调出来”,实际上无法判断是谁起了作用,调参变成了碰运气。怎么补:固定迭代次数画代价曲线看趋势,先判断是发散、停滞还是收敛慢;发散降学习率,停滞查特征缩放和正则强度,收敛慢再单独加迭代次数。每次只动一个变量,把结果记在笔记里,这比事后回忆“我好像试过”靠谱得多。

5.5 笔记只记公式不记失败路径

易漏点:个人笔记里全是推导和最终代码,没有任何“这里我错过”的痕迹。为什么漏:整理笔记时人在顺境,潜意识里只保留正确路径,错误被自动过滤掉,而复习时真正需要被提醒的恰恰是那些坑。怎么补:每个模块后面加一行“踩坑记录”,写清报错信息关键词和根因,比如“维度不匹配→忘了 reshape 成 (n+1,1)”;复习时先读踩坑记录再读公式,顺序反过来效果差很多。这套做法不玄学,就是给自己留一份后悔药。

6. 复习清单:按四轮切片的自查表和查漏补缺顺序

复习这件事最怕从头再看一遍视频,时间不够且收益极低。比较有效的做法是按四轮切片,每轮只解决一类问题,用自查表控制进度。

轮次目标具体动作通过标准
第一轮恢复记忆只看个人笔记的公式区和踩坑记录能口述每章的三段式
第二轮补推导拿无答案版重写代价函数与梯度不看笔记写出向量化版本
第三轮对拍用有答案版跑同一份数据比对输出差异在容差内且能解释来源
第四轮模拟限时完成一套完整作业无报错、评测全过

查漏补缺的顺序也有讲究:先查维度,再查缩放,最后查超参。维度错会导致后面所有现象都失真,先排除它;特征没缩放会让学习率调参失去意义,第二步排除;前两项都干净了,再看学习率、迭代次数、正则强度才有意义。这个顺序是我踩过几次坑之后固定下来的——曾经花了一下午调学习率,最后发现是设计矩阵少补了一列 1。

具体到每天的执行,我的习惯是每完成一个模块就在笔记末尾追加三行:这一章的核心公式、我卡住的那个报错、下次重写时要注意的一个点。三行不多,但四周下来就是一份针对性极强的个人错题集。从那以后我每次开始新一轮复习,都强制先走一遍这四轮切片和那三条追加,不直接从第一页翻起,效率差别非常明显。希望帮到你。

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

返回列表