简介:这份李宏毅机器学习全套资料面向希望系统入门并深入掌握机器学习的学习者,无论是零基础初学者还是已有一定基础、想补齐理论短板并强化动手能力的同学,都能从中找到对应内容。资源包共234个文件,涵盖77个png图表、49个py脚本、25个csv数据集、25个pdf讲义、19个md笔记以及pptx课件、ipynb实验文件等,压缩包约70.21MB,结构清晰,便于按课程进度逐章检索学习。资料围绕监督学习、无监督学习、强化学习等核心主题展开,配有教学演示文稿、作业解答与课程讲义,帮助读者理解概率论、支持向量机、神经网络与深度学习等关键算法,并通过数据预处理、特征选择、模型训练与误差分析等练习巩固所学。目前已有2492人学习下载,适合作为长期参考的机器学习学习资料。
1. 从一份被转存了无数次的全套资料说起:机器学习入门到底该先啃什么
如果你在搜索框里敲过“机器学习入门资料”,大概率见过这个名字。它被存进无数个网盘、塞进各种学习群、在论坛里反复出现,但真正从头到尾跑通的人并不多。我见过太多人把资料存下来,翻了两页公式就卡住,然后换下一份,循环往复。问题不在资料本身,而在于没人告诉你这份资料该怎么用、先看哪块、哪些章节可以跳过、代码怎么跑起来。
这份全套资料的核心价值,是把机器学习从“调包”拉回到“手推+手写”的层面。它覆盖回归、分类、神经网络、CNN、RNN、Transformer、自注意力、生成模型等主线内容,配套作业和代码示例。适合两类人:一是刚学完Python基础、想系统入门但被各种速成教程带偏的新手;二是已经会用sklearn调参、但说不清梯度下降为什么这样更新、反向传播到底在算什么的老手。如果你只想快速调个模型交差,这份资料可能偏重;但如果你想真正理解模型内部在发生什么,它值得你花时间。
2. 资料结构拆解:从回归到自注意力,哪些章节必须手推
2.1 主线内容的分层逻辑
这份资料的组织方式不是按“算法字典”排列,而是按学习路径递进。前半段是基础:回归、梯度下降、分类、逻辑回归、损失函数。中段进入神经网络:反向传播、优化技巧、CNN、RNN。后段是近年重点:Transformer、自注意力、生成模型。每一块都配有对应的作业和代码框架。
我一般建议按这个顺序走,但不要平均用力。回归和梯度下降部分必须手推一遍,因为后面所有模型的训练过程都是这套逻辑的变体。CNN和RNN可以快速过,重点理解卷积核在提取什么、循环结构为什么能处理序列。Transformer和自注意力是当前面试和实际项目的高频区,需要反复看、配合代码调试。
常见做法是:第一遍只看视频和讲义,不碰作业;第二遍回头手推公式,同时跑代码;第三遍挑一个作业从头实现,不看参考答案。三遍下来,基本能建立起“看到模型知道它在干什么”的直觉。
2.2 作业与代码框架的使用方式
资料里的作业通常给出数据加载、模型定义、训练循环的骨架,留出关键函数让你补全。这种设计比直接给完整代码更有效,但前提是你得先理解骨架在做什么。
以线性回归作业为例,骨架一般长这样:
# 线性回归骨架示例(根据资料常见结构整理) import numpy as np # 数据加载部分通常已给出 # 需要补全的是模型前向计算和梯度更新 def forward(x, w, b): # 补全:计算预测值 return x * w + b def loss_fn(y_pred, y_true): # 补全:均方误差 return np.mean((y_pred - y_true) ** 2) def gradient(x, y, w, b): # 补全:计算w和b的梯度 y_pred = forward(x, w, b) dw = 2 * np.mean((y_pred - y) * x) db = 2 * np.mean(y_pred - y) return dw, db # 训练循环 for epoch in range(1000): y_pred = forward(x, w, b) dw, db = gradient(x, y, w, b) w -= lr * dw b -= lr * db这段代码的关键不在语法,而在理解每一步的数学含义。forward对应假设函数,loss_fn对应目标函数,gradient对应链式法则求导。参数lr是学习率,设太大会震荡,设太小收敛慢。我一般先用0.01试,观察loss曲线,如果下降太慢就调到0.1,如果震荡就降到0.001。
作业里的CNN和RNN部分会复杂一些,但逻辑一样:先搞清楚输入输出形状,再补全前向传播,最后检查梯度是否正确。如果训练loss不降,先检查数据有没有归一化、标签有没有对齐、学习率是不是太大。
提示:作业代码不要直接复制网上的答案。自己写一遍,哪怕报错,调试过程比看答案有价值得多。
3. 环境配置与代码跑通:从零搭一个能复现的本地环境
3.1 依赖安装与版本选择
这份资料的代码示例主要基于Python和NumPy,部分作业涉及PyTorch。我一般用conda建一个独立环境,避免和系统Python冲突。
# 创建虚拟环境 conda create -n ml-study python=3.9 conda activate ml-study # 安装基础依赖 pip install numpy matplotlib jupyter # 安装PyTorch(根据显卡情况选择CPU或GPU版本) # CPU版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 如果有NVIDIA显卡,去PyTorch官网查对应CUDA版本的安装命令版本选择上,Python 3.8到3.10都比较稳,3.11以上有些旧版库可能不兼容。PyTorch选1.12以上都行,重点是和CUDA版本匹配。如果只是跑作业,CPU版本足够,训练数据量不大。
常见翻车点是pip源太慢导致安装中断。我一般会临时换国内镜像源,但注意不要用来源不明的第三方源。安装完成后,跑一个简单测试确认环境正常:
import torch import numpy as np print(torch.__version__) print(np.__version__) # 如果能正常打印版本号,说明基础环境没问题3.2 数据加载与预处理中的参数设置
资料里的作业数据一般以CSV或NumPy数组形式给出。加载时要注意几个参数:编码格式、分隔符、是否有表头。我遇到过用pd.read_csv默认参数读出来乱码的情况,后来发现是文件编码是GBK而不是UTF-8。
import pandas as pd # 常见做法是显式指定编码和分隔符 df = pd.read_csv('data.csv', encoding='utf-8', sep=',') # 检查数据形状和缺失值 print(df.shape) print(df.isnull().sum()) # 特征归一化(线性回归和神经网络都建议做) from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(df.drop('target', axis=1)) y = df['target'].values归一化这一步很多人会跳过,结果训练时loss爆炸。StandardScaler把均值变0、方差变1,对梯度下降的收敛速度影响很大。如果数据里有类别特征,还需要做独热编码。这些预处理步骤在资料里不一定每处都强调,但实际跑代码时不做就会出问题。
注意:归一化参数(均值和方差)只能从训练集计算,然后应用到验证集和测试集。如果全量数据一起归一化,会造成信息泄露。
4. 避坑与排查:那些年跑作业时踩过的坑
4.1 梯度消失与梯度爆炸
现象:训练深层网络时,loss一开始下降,几轮后突然变成NaN,或者loss一直不降。
原因:梯度消失通常是因为激活函数选错(比如用sigmoid导致深层梯度趋近0),梯度爆炸则多是学习率太大或权重初始化不当。
解决:把sigmoid换成ReLU,学习率从0.001开始试,权重用Xavier或He初始化。如果已经出现NaN,先检查输入数据有没有异常值,再逐步调小学习率。
4.2 过拟合与欠拟合的判断
现象:训练集loss很低,验证集loss很高,或者两者都高。
原因:过拟合是模型太复杂或数据太少,欠拟合是模型太简单或训练不够。
解决:过拟合加Dropout、L2正则、早停;欠拟合加层数、加神经元、延长训练。我一般先看训练loss能不能降到接近0,如果能但验证loss不降,就是过拟合;如果训练loss都降不下去,先检查模型结构和学习率。
4.3 数据形状不匹配
现象:报错RuntimeError: mat1 and mat2 shapes cannot be multiplied或类似维度错误。
原因:全连接层的输入维度没对上,或者卷积层输出尺寸算错。
解决:在forward里打印每一层的输出形状,逐层核对。CNN里常用公式是(W - K + 2P) / S + 1,其中W是输入尺寸,K是卷积核大小,P是padding,S是步长。算一遍再写代码,比报错后瞎改快得多。
4.4 随机种子未固定导致结果不可复现
现象:同样的代码跑两次,结果不一样。
原因:NumPy和PyTorch的随机初始化没有固定种子。
解决:在代码开头加:
import numpy as np import torch import random seed = 42 np.random.seed(seed) torch.manual_seed(seed) random.seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)固定种子后,每次运行结果一致,方便调试和对比。但注意,固定种子不代表模型效果一定好,只是让实验可复现。
4.5 学习率设置不当
现象:loss震荡不收敛,或者下降极慢。
原因:学习率太大导致跳过最优解,太小导致训练太慢。
解决:先用0.01试,观察loss曲线。如果震荡,除以10;如果下降太慢,乘以10。更系统的做法是用学习率预热或余弦退火,但作业阶段手动调就够了。
5. 从手推公式到项目实战:把自注意力机制拆开看
5.1 自注意力的计算流程与参数含义
自注意力是Transformer的核心,也是这份资料后段的重点。它的计算流程可以拆成四步:输入映射、计算注意力分数、softmax归一化、加权求和。
import torch import torch.nn.functional as F def self_attention(x, W_q, W_k, W_v): # x: (batch, seq_len, d_model) Q = x @ W_q # 查询 K = x @ W_k # 键 V = x @ W_v # 值 # 计算注意力分数 d_k = Q.size(-1) scores = Q @ K.transpose(-2, -1) / (d_k ** 0.5) # softmax归一化 attn_weights = F.softmax(scores, dim=-1) # 加权求和 output = attn_weights @ V return output, attn_weightsW_q、W_k、W_v是可学习参数矩阵,把输入映射到查询、键、值空间。除以d_k ** 0.5是为了防止点积结果太大导致softmax梯度消失。attn_weights表示每个位置对其他位置的关注程度,可视化出来能看出模型在关注什么。
我一般会打印attn_weights的形状和部分数值,确认softmax后每行和为1。如果和不为1,检查dim参数是不是设错了。
5.2 多头注意力的实现与调试
多头注意力是把自注意力并行做多次,每次用不同的映射矩阵,最后拼接。这样做能让模型同时关注不同位置的信息。
class MultiHeadAttention(torch.nn.Module): def __init__(self, d_model, num_heads): super().__init__() self.num_heads = num_heads self.d_model = d_model self.d_k = d_model // num_heads self.W_q = torch.nn.Linear(d_model, d_model) self.W_k = torch.nn.Linear(d_model, d_model) self.W_v = torch.nn.Linear(d_model, d_model) self.W_o = torch.nn.Linear(d_model, d_model) def forward(self, x): batch, seq_len, _ = x.shape # 映射并分头 Q = self.W_q(x).view(batch, seq_len, self.num_heads, self.d_k).transpose(1, 2) K = self.W_k(x).view(batch, seq_len, self.num_heads, self.d_k).transpose(1, 2) V = self.W_v(x).view(batch, seq_len, self.num_heads, self.d_k).transpose(1, 2) # 计算注意力 scores = Q @ K.transpose(-2, -1) / (self.d_k ** 0.5) attn = F.softmax(scores, dim=-1) out = attn @ V # 拼接多头 out = out.transpose(1, 2).contiguous().view(batch, seq_len, self.d_model) return self.W_o(out)调试时重点检查view和transpose后的形状。d_model必须能被num_heads整除,否则d_k不是整数会报错。如果训练时loss不降,先确认W_o有没有加,很多人忘了最后一步线性映射。
5.3 位置编码的作用与实现
自注意力本身不包含位置信息,所以需要额外加位置编码。资料里一般用正弦余弦编码:
def positional_encoding(seq_len, d_model): pe = torch.zeros(seq_len, d_model) position = torch.arange(0, seq_len).unsqueeze(1).float() div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(np.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) return pe偶数维度用sin,奇数维度用cos。这样每个位置都有唯一的编码,且相对位置关系可以通过线性变换表示。如果去掉位置编码,模型对序列顺序就不敏感了,效果会明显下降。
5.4 验证模型是否真正学到东西
跑完训练后,不要只看loss数字。我一般做三件事:一是可视化注意力权重,看模型有没有关注到合理的区域;二是用测试集评估,确认没有过拟合;三是手动构造几个输入,看输出是否符合直觉。
比如训练一个文本分类模型后,输入一个明显正面的句子和一个明显负面的句子,看预测概率是否合理。如果两个句子预测结果一样,说明模型没学到东西,需要检查数据标签、损失函数、学习率。
提示:注意力可视化可以用matplotlib画热力图,横轴是查询位置,纵轴是键位置,颜色深浅表示权重大小。
从那以后我每次跑完一个新模型,都强制走一遍“看注意力+测边界样本”的流程,比只看准确率靠谱得多。希望帮到你。
本文还有配套的精品资源,点击获取