简介:这份资源是李宏毅机器学习课程的全套配套资料包,面向希望系统入门并深入掌握机器学习的大学生、研究生与自学者,覆盖从监督学习、无监督学习、半监督学习到强化学习,以及支持向量机、神经网络与深度学习等进阶主题,可用于课前预习、课后复盘与作业实践。压缩包共234个文件,约70.21MB,以77张png图示、49个py脚本、25个csv数据集、25份pdf讲义与19份md笔记为主,另含pptx课件、ipynb交互式练习及txt说明等,图文、代码与数据相互配合,便于按章节对照学习。已有2497人学习下载,说明其在机器学习入门群体中具有一定认可度。资料既包含课件讲解,也提供作业解答与实验数据,读者可沿着作业题目完成数据预处理、特征选择、模型训练与误差分析的完整流程,并借助代码与图示理解算法细节,适合作为长期查阅的学习底稿。
1. 先立骨架:机器学习这门课的四层地图与复习顺序
机器学习这四个字,初看像一个塞满名词的仓库:回归、分类、深度学习、自监督、强化学习、生成模型、模型压缩、可解释性……真要复习才发现,它们不是并列的章节,而是同一条主线上的不同补丁。这套全套资料给的主线其实很朴素:先把问题定义成「找一个函数」,再解决「怎么把这个函数找好」,最后解决「数据不够、算力不够、标签不够时怎么照样找」。抓住这条线,几十个小时的课才不会碎成一地名词。
复习顺序建议先倒着看一遍目录:模型决定假设空间有多大,损失函数决定你在优化什么,优化算法决定你走不走得到那儿,评估与正则化决定这东西能不能真的用。四层里任何一层空着,后面背下来的公式都会变成玄学。这套资料适合已经完整过了一遍课、想系统重建框架的人,也适合考前想快速定位薄弱模块的人——先知道自己缺哪一层,再去补,比从头再刷一遍效率高得多。
2. 监督学习主干:回归、分类与树模型的复习重点
监督学习是整门课的地基,也是复习时最容易被「我已经会了」这四个字骗过去的部分。真正拉开差距的不是会不会写model.fit(),而是能不能说清楚:这个模型的假设空间长什么样、损失函数为什么长这样、换一个损失会发生什么。下面三块是主干里的主干,复习时按「假设空间 → 损失 → 优化」三段式过一遍,比按 PPT 页码顺序翻要有效得多。
2.1 回归:平方损失不是随手选的
线性回归的假设空间是一族带权重的直线(高维就是超平面),这件事人人都知道,但复习时最该问的是:为什么偏偏用平方损失,而不是绝对值、四次方?标准答案是概率视角——假设目标值等于模型输出加上一个零均值的高斯噪声,那么最大化似然等价于最小化平方误差。这条推导值得亲手写一遍,因为它是后面所有「为什么这个损失合理」问题的母版:交叉熵来自伯努利/多项分布的最大似然,Huber 损失来自对重尾噪声的妥协,逻辑是同一套。
第二件容易忽略的事是数值行为。平方损失对离群点极其敏感,一个标注错误的样本能把整条线拽偏;而在特征量纲差异巨大时,梯度下降会呈明显的「之字形」,收敛慢到让人怀疑代码写错了。这也是为什么要做特征标准化,以及为什么学习率调参前必须先看特征尺度。
import numpy as np def linear_regression_closed_form(X, y): """闭式解:w = (X^T X)^{-1} X^T y X 形状 (n, d+1),第一列补 1 作为偏置项 """ A = X.T @ X b = X.T @ y # 用 solve 而不是 inv:少一次显式求逆,数值更稳 return np.linalg.solve(A, b) def ridge_closed_form(X, y, lam=1e-3): """岭回归:给对角线加 lam,等价于 L2 正则""" d = X.shape[1] A = X.T @ X + lam * np.eye(d) A[0, 0] -= lam # 通常不对偏置项施加惩罚 return np.linalg.solve(A, X.T @ y)这两段代码的重点不在「会写」,而在两个细节:一是特征高度共线时X^T X接近奇异,闭式解会炸,加lam本质上是把病态矩阵拉回可逆区间,这也是正则化最早被接受的解释;二是lam的量级和特征尺度强相关,做过标准化之后 1e-3 到 1e-1 是常见区间,没做标准化时同样的lam可能毫无作用。复习时能把这两句讲清楚,比背十遍公式有用。
2.2 分类:逻辑回归不是「回归加个阈值」
初学者最常见的误解是「回归输出连续值,四舍五入不就分类了」。这条路会翻车,而且翻得很有教育意义:假设用平方损失训练一个二分类器,某个样本已经离决策边界很远、被判定得非常正确(输出 10),平方损失仍然会惩罚它,要求它往 1 靠,于是模型被这些「过于正确」的样本牵着走,决策边界被拉歪。换成交叉熵,这类样本的梯度会趋近于零,模型不再浪费容量去纠正已经分对的点。这个对比是理解「损失函数决定模型行为」的最佳例子。
分类的标准三步曲是:函数集先用 sigmoid(多分类用 softmax)把输出压到概率单纯形上;损失用交叉熵,等价于对正确类别的负对数似然;优化仍然用梯度下降,而逻辑回归的梯度形式出奇地简洁——预测概率减真实标签,再乘特征。很多人复习到这里会松一口气:原来深度学习的训练循环,和这里没有本质区别,只是函数集从线性换成了多层复合。
还有一条常被略过的线索:生成模型与判别模型。用高斯分布分别建模每一类的条件概率,当两类共享同一个协方差矩阵时,后验概率化简后正好是 sigmoid 的形式——也就是说,生成式建模在特定假设下会退化成逻辑回归。这个结论在复习时价值很高,它把「生成」和「判别」两个看似对立的框架缝在了一起。
| 对比项 | 回归 | 分类 |
|---|---|---|
| 输出 | 实数 | 概率分布 |
| 常用损失 | 平方损失 / Huber | 交叉熵 |
| 输出层 | 恒等映射 | sigmoid / softmax |
| 典型误用 | 用平方损失做分类 | 在 softmax 之后再套一层 log |
2.3 树模型与集成:表格数据上的老牌强手
决策树的假设空间是一系列轴对齐的矩形划分,这一点决定了它的性格:对特征单调变换不敏感、不需要标准化、天然处理混合类型特征,但同时很难表达斜向的决策边界,且单棵树极易过拟合。复习时要抓住的其实是「怎么分裂」:信息增益基于熵的下降,基尼指数是熵的一阶近似,两者在实践中的差别远小于「树深和叶节点最小样本数」这两个超参数带来的差别。
集成部分要分清两条完全不同的思路。随机森林是并行地「平均」,靠对样本和特征各做一次随机采样来降低方差,每棵树都长得比较深,单棵偏差小、方差大。梯度提升是串行地「纠错」,每棵新树拟合的是前面模型没解释掉的残差(严格说是损失函数的负梯度),所以每棵树都很浅,靠数量堆出精度。这两条路线调参的方向是反的:随机森林怕树太深,梯度提升怕学习率太大或树太多。
在结构化数据上,树集成至今仍然是非常能打的基线。神经网络在图像、语音、文本上碾压,但面对几十个字段的表格,一个调好的梯度提升模型往往只需要几分钟就能到很强的水平,而且特征重要性可解释。复习时把「什么数据用什么模型」这条判断标准建立起来,比多背一个算法更值。
3. 训练的地基:损失函数、梯度下降与正则化的重点难点
这一章是整套资料里信息密度最高、也最容易被快进的部分。多数人卡在模型结构上,真正的坑却埋在训练过程里:损失选错、学习率设错、正则加错,这三件事造成的「模型不收敛」占了实践中的大多数。复习顺序建议反着来,先看训练过程是否健康,再看模型结构是否合适。
3.1 损失函数:先想清楚你在优化什么
损失函数的本质是「把业务目标翻译成可求导的数字」。分类用交叉熵,回归用平方损失或 Huber,这是常识;不常识的是它们各自隐含的假设。交叉熵假设标签是「唯一正确」的硬目标,如果标注本身有噪声、多个标注者意见不一,就该考虑标签平滑;平方损失假设噪声是高斯的、对称的,遇到长尾分布时会被极端值绑架,此时 Huber 或分位数损失更稳。
另一个高频误区是在多任务场景直接相加。不同任务的损失量级可能差几个数量级,直接相加等于让量级大的任务独占梯度。常见做法是给每个任务配一个可学习的不确定性权重,或者简单地对每个任务的损失做滑动平均归一化。复习时如果只记住「分类用交叉熵」,遇到多任务就会当场卡住。
3.2 梯度下降调参:顺序比数值重要
调参有一个几乎不会错的顺序:先确认数据管道正确,再定学习率,再定批大小,最后才动优化器和网络结构。顺序反了,你会用结构改动去掩盖一个其实是数据标签错位的 bug。学习率的判断标准也很具体:训练损失前几个 epoch 就变成 NaN 或剧烈震荡,说明太大;损失几乎不动,说明太小或者梯度被断掉了。
批大小影响的不只是显存占用。小批量的梯度噪声大,有正则化效果,但训练不稳;大批量梯度准,但泛化有时反而变差,需要配合学习率缩放。这个权衡在复习时值得单独记一笔,因为它是「为什么同样的模型别人训得比我好」的常见答案。
for epoch in range(num_epochs): model.train() # 打开 Dropout,BatchNorm 用当前批统计量 for xb, yb in train_loader: logits = model(xb) loss = criterion(logits, yb) # 分类直接用交叉熵,别在 softmax 后再取 log optimizer.zero_grad() # 漏掉这行 = 梯度累加,属于血泪经验级 bug loss.backward() # RNN / Transformer 必备:梯度裁剪放在 step 之前 torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() model.eval() # 关掉 Dropout,BatchNorm 切到滑动统计量 with torch.no_grad(): val_loss = evaluate(model, val_loader)这段循环里有三个「位置」值得记住:zero_grad()必须在backward()之前,否则梯度会跨批次累加;clip_grad_norm_必须在step()之前,否则裁剪对参数更新无效;model.eval()必须和train()成对出现,BatchNorm 在两种模式下的行为差异是「训练指标很好、推理一塌糊涂」的头号元凶。
3.3 过拟合与正则化:先分清是过拟合还是没训好
偏差方差分解提供了判断框架:训练损失高、验证损失也高,是欠拟合,该加容量或减正则;训练损失低、验证损失高,才是过拟合,该加数据或加正则。很多人一看到验证指标不涨就加 Dropout,结果把本来就没训好的模型压得更死。先看训练损失,这一步不能省。
正则化手段各有适用面:L2 让权重整体收缩,L1 造稀疏,Dropout 相当于对指数多个子网络做集成(推理时要按保留概率缩放,或者在训练时做反向缩放),早停是最便宜也最有效的一种,数据增强则是把先验知识直接写进训练集。值得注意的是,正则化强度不是越大越好,它和模型容量、数据量是一个三角平衡,单独调其中一个维度往往得不到想要的结果。
4. 表示学习进阶:从 CNN、RNN 到注意力机制的关键跳跃
前面三章解决的是「怎么把给定的特征拟合好」,这一章解决的是「特征本身能不能学出来」。这套资料把这条演进线讲得很连贯:全连接层不知道相邻像素的关系,于是有了卷积;卷积不知道时间顺序,于是有了循环网络;循环网络记不住长距离依赖,于是有了注意力;注意力需要大量标注,于是有了自监督预训练。每一步都是被上一步的缺陷逼出来的,复习时按「缺陷 → 补丁」的方式串一遍,比按模型名逐个记要牢。
4.1 CNN:归纳偏置换来的效率
卷积层做的两件事值得反复咀嚼:局部连接和权重共享。前者假设相邻位置的信息更相关,后者假设同一模式可以出现在图像任意位置。这两条假设把参数数量从「全连接的平方级」压到「卷积核大小级」,本质上是把先验知识写进了结构里。感受野是另一个必须掌握的概念:堆叠两层 3×3 卷积的感受野等于一层 5×5,但参数更少、非线性更多,这就是「小卷积核堆深」成为主流的算术理由。
池化的取舍也值得记一笔。最大池化提供了一定的位移不变性,代价是丢失精确位置信息;在分类任务里这是好事,在分割、检测、关键点定位里就可能是灾难,所以现代结构更倾向于用步长卷积代替池化,把「要不要丢位置」变成可学习的。
4.2 RNN 与 LSTM:序列建模的老办法卡在哪
循环网络的核心是隐状态在时间上递推,这让它能处理变长输入,也让它的梯度必须沿时间反向传播。问题恰恰出在这里:连乘的雅可比矩阵会让梯度指数级衰减或爆炸,于是模型只能记住最近几步。梯度裁剪解决爆炸,门控机制缓解衰减——LSTM 的输入门、遗忘门、输出门本质上是在学「什么时候该记住、什么时候该忘」。
复习时容易被跳过的一点是:LSTM 缓解了梯度消失,但没有根除长距离依赖问题。序列够长时,信息仍然要逐跳传递,路径长度是线性的。理解这个局限,才能明白后面的注意力机制为什么是范式变化而不是一次普通改进。
4.3 注意力机制:把路径长度从线性压到常数
自注意力做的事情很直白:每个位置都直接和所有位置算一次相关性,然后按相关性加权求和。任意两个位置之间的路径长度变成 1,这是它相对 RNN 的根本优势,也是它天然适合并行计算的原因。代价是复杂度变成序列长度的平方,长文本场景下的显存开销是实打实的工程问题。
import numpy as np def scaled_dot_product_attention(Q, K, V, mask=None): # Q: (T_q, d_k) K: (T_k, d_k) V: (T_k, d_v) d_k = Q.shape[-1] scores = Q @ K.T / np.sqrt(d_k) # 点积方差随 d_k 增长,不缩放会让 softmax 饱和 if mask is not None: scores = np.where(mask, scores, -1e9) # 因果掩码:每个位置只能看左边 # 减去行最大值再取指数,防止 exp 溢出,结果等价 w = np.exp(scores - scores.max(axis=-1, keepdims=True)) w = w / w.sum(axis=-1, keepdims=True) return w @ V, w三个参数细节决定了这段代码是否正确:除以sqrt(d_k)是因为随机初始化下点积的方差正比于维度,不缩放会让 softmax 输出接近 one-hot,梯度几乎为零;减行最大值是纯数值技巧,不改变数学结果;掩码用-1e9而不是-inf,是为了避免整行全被屏蔽时出现 NaN。多头注意力则是在这个基础上把 Q、K、V 投影到多组低维子空间,让不同的头关注不同的关系模式。
还有一点极易被忽略:自注意力本身是置换不变的,把输入顺序打乱,输出只是跟着打乱。位置信息必须靠位置编码显式注入,否则整个模型退化成一个复杂的词袋。这条性质在面试和考试里都是高频点。
4.4 自监督与预训练:标签不够时的两条路
标注数据永远是稀缺的,自监督的思路是「从数据本身造监督信号」。两条主流路线:一条是掩码重建,遮住一部分输入让模型预测,逼它理解上下文;另一条是对比学习,把同一个样本的不同视图拉近、把不同样本推远。前者的代表是掩码语言建模,后者在视觉表征学习里用得更多。
预训练加微调这个范式值得单独复习,因为它改变了整个领域的游戏规则:下游任务不再从随机初始化开始,而是从一个已经理解语言的起点开始。复习时要能说清楚三件事——预训练任务设计成什么样、微调时是冻结还是全量更新、以及为什么小样本下微调容易过拟合。
5. 易错点清单:复习机器学习时最容易翻车的五个地方
有些错误不属于「不会」,而属于「以为自己会」。它们在看课件时完全不会暴露,一上手就现原形,而且往往表现为「模型效果不好」这种模糊症状,让人往错误的方向调半天。下面五个是梳理下来出现频率最高的,每一个都按「易漏点 → 为什么漏 → 怎么补」写清楚。
5.1 评估与数据上的三个坑
易漏点一:拿测试集反复调参。为什么漏:因为在代码里划分数据集只是一行,跑起来毫无障碍,看着测试准确率一点点涨上去,心理上很难不继续调。怎么补:把测试集物理隔离,用一个脚本单独跑、只跑一次,中间所有模型选择都在验证集上做;如果数据量小,用交叉验证代替单次划分,把「选择」和「报告」两个动作彻底分开。
易漏点二:先归一化再划分数据集。为什么漏:这两行代码的书写顺序看起来无关紧要,语法上都对,跑起来也不报错。怎么补:所有依赖统计量的预处理——标准化、缺失值填充、目标编码——都必须先fit在训练集上,再transform到验证集和测试集。判断标准很简单:只要这个变换用到了「全体数据的均值或分布」,就是数据泄漏。泄漏带来的指标虚高是最难察觉的一种,因为整个流程跑得无比顺畅。
易漏点三:类别不平衡时只看准确率。为什么漏:准确率是默认指标,几乎所有的库都直接给你这个数。怎么补:正负样本 99:1 时,全预测为负就有 99% 的准确率,模型什么都没学到。换成看精确率、召回率、F1,或者更稳妥的 PR 曲线下面积。选哪个取决于业务代价:漏检代价高就看召回,误报代价高就看精确率,这个判断必须在建模之前定下来,而不是训练完再挑一个好看的指标。
5.2 模型与优化上的两个坑
易漏点四:训练损失降不下去,第一反应是加容量。为什么漏:直觉上「学不会」等于「模型不够强」,这个直觉在很多场景是错的。怎么补:按固定顺序排查——先拿一个极小的子集(几十个样本)看能不能过拟合到接近零损失,如果连这个都做不到,问题一定在数据管道、标签对齐或损失函数上,跟模型容量无关;再检查学习率是否过大导致震荡、梯度是否被detach或no_grad意外截断。这个「先过拟合小批量」的动作能在几分钟内定位绝大多数训练 bug。
易漏点五:正则化强度越大越安全。为什么漏:过拟合是教科书里的头号反派,于是所有对抗过拟合的手段都被默认为「多多益善」。怎么补:正则化是在用偏差换方差,加过头会直接掉进欠拟合。正确的做法是先建立一个无正则的基线,观察训练与验证曲线的间隙,再逐步加正则并观察间隙是否收窄、验证指标是否真的提升。BatchNorm 和 Dropout 的相互作用尤其要小心,两者叠在一起时行为会变得难以预测,这也是很多人复现论文失败的原因之一。
6. 复习清单:把整套资料压成一张可自查的表
到这一章,前面所有的模块应该能压成一张表。复习的价值不在于再看一遍视频,而在于能不能对着表格逐项自问自答,卡住的那一行就是接下来要补的地方。
| 模块 | 一句话自查 | 卡住时回看 |
|---|---|---|
| 回归 | 我能推导平方损失与高斯噪声的等价关系吗 | 2.1 |
| 分类 | 我能说清为什么平方损失不适合分类吗 | 2.2 |
| 树与集成 | 随机森林降方差、梯度提升降偏差,方向为什么相反 | 2.3 |
| 损失函数 | 多任务时损失量级不一致该怎么处理 | 3.1 |
| 训练调参 | 学习率、批大小、优化器的调整顺序是什么 | 3.2 |
| 正则化 | 如何用训练/验证曲线区分欠拟合与过拟合 | 3.3 |
| CNN | 两层 3×3 的感受野为什么等价于一层 5×5 | 4.1 |
| 序列模型 | LSTM 缓解了梯度消失,但没有解决什么 | 4.2 |
| 注意力 | 为什么要除以 sqrt(d_k),为什么需要位置编码 | 4.3 |
| 自监督 | 掩码重建与对比学习各自的监督信号从哪来 | 4.4 |
| 评估 | 数据泄漏有哪几种典型形态 | 5.1 |
自查的方法比表格本身更重要。我的习惯是合上资料,拿一张白纸,对每一行先写一句话结论,再写一句「所以呢」——比如「交叉熵来自最大似然」是结论,「所以标签有噪声时应该用标签平滑而不是硬目标」是推论。写不出推论,说明这一行只是记住了名词,没有真正接上使用的场景。第二遍再过表格时,只挑写不出推论的项,能把复习时间压缩到原来的三分之一。
还有一个具体的技巧:把每个模块都准备一个「最小可复现实验」。回归用闭式解和梯度下降互相对照,看两者结果是否一致;分类把平方损失和交叉熵各训一遍,亲眼看决策边界怎么变形;注意力把sqrt(d_k)去掉,观察 softmax 输出如何饱和。这类实验每个不超过三十行代码,但留下的印象远超看十页公式。
踩过的坑里,代价最大的一次是在一个课程项目上花了整整两天调网络结构,最后发现是划分数据集时把标准化写在了前面,验证指标一直是虚高的,越调越偏。从那以后我每次开新项目,都强制先走一遍「小批量过拟合 + 检查预处理顺序 + 确认测试集隔离」这三步,再谈模型。希望帮到你。
这套全套资料的完整版本已经整理归档,需要的可以按文末方式获取,对着上面这张表过一遍,缺口会很清楚地浮出来。
本文还有配套的精品资源,点击获取