深度学习基础
这一课你将能做到
- 用 PyTorch 的 autograd 理解「自动求导」——反向传播不再是一个黑名词
- 用 TensorDataset + DataLoader 把数据切成批次,喂进神经网络
- 用 nn.Sequential 搭一个三层 MLP,跑通「zero_grad → 前向 → 反向 → 更新」训练四步曲
- 用 state_dict 保存和加载模型权重
为什么要学
前面的逻辑回归、决策树都属于「浅层模型」,特征要靠人设计。深度学习的核心优势是自己学特征:题目文本、用户行为序列这类复杂信号,人工提特征提不动,让神经网络逐层抽象。
PyTorch 是当前学术界和工业界的主流框架,API 像原生 Python 一样直白。本课不追求训练出 SOTA 模型,只求你亲手跑通一次完整的训练循环,理解每一步在做什么。
先建立图像
一个神经元: 输出 = ReLU(w₁x₁ + w₂x₂ + b)
三层 MLP(本课结构):
输入(3维) → Linear(3→16) → ReLU → Linear(16→8) → ReLU → Linear(8→1) → logits
训练循环四步曲(背下来):
opt.zero_grad() # 清空上一批的梯度
loss = 前向 # 算预测和损失
loss.backward() # 反向传播算所有参数的梯度
opt.step() # 按梯度更新参数
跟着做
步骤 1:autograd——自动求导 5 行看懂
import torch
x = torch.tensor(2.0, requires_grad=True)
y = 2 * x ** 2 # y = 2x²
y.backward() # 反向传播
print(y.item(), x.grad.item()) # 8.0 8.0 —— 导数 4x 在 x=2 处等于 8
backward() 沿计算图用链式法则自动算出梯度。训练百万参数的模型时,人手算不现实,这就是框架存在的意义。
步骤 2:构造做题数据并分批
import numpy as np
from torch.utils.data import TensorDataset, DataLoader
rng = np.random.default_rng(42)
n = 500
accuracy = rng.uniform(0.1, 0.95, n).round(3)
attempts = (rng.uniform(1, 9, n) + (0.6 - accuracy) * 5).round(1)
seconds = (260 - 180 * accuracy + rng.normal(0, 20, n)).round(0)
mastery = (accuracy > 0.5) & (attempts > 3)
X = torch.tensor(np.column_stack([accuracy, attempts, seconds]),
dtype=torch.float32)
y = torch.tensor(mastery.astype(np.float32)).unsqueeze(1)
loader = DataLoader(TensorDataset(X, y), batch_size=64, shuffle=True)
batch_size=64:每次用 64 条记录估一次梯度。小批量比全量更省内存,噪声还能帮模型跳出局部最优。
步骤 3:搭建 MLP 并训练
import torch.nn as nn
model = nn.Sequential(
nn.Linear(3, 16), nn.ReLU(),
nn.Linear(16, 8), nn.ReLU(),
nn.Linear(8, 1), # 输出 logits,不接 Sigmoid
)
loss_fn = nn.BCEWithLogitsLoss()
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(30):
total = 0.0
for xb, yb in loader:
opt.zero_grad() # ① 清梯度
loss = loss_fn(model(xb), yb) # ② 前向
loss.backward() # ③ 反向
opt.step() # ④ 更新
total += loss.item()
if (epoch + 1) % 10 == 0:
print(f"epoch {epoch+1:2d} 平均 loss={total/len(loader):.4f}")
BCEWithLogitsLoss = Sigmoid + 二分类交叉熵,数值更稳,所以输出层不要再接 Sigmoid。
步骤 4:评估
model.eval()
with torch.no_grad(): # 推理不需要计算图
proba = torch.sigmoid(model(X)).numpy().ravel()
print("训练集准确率:", ((proba > 0.5) == mastery).mean().round(3))
注意:这里只演示流程,正式评估必须切分独立测试集(上上课讲过的纪律同样适用)。
步骤 5:保存与加载
torch.save(model.state_dict(), "mlp_mastery.pt")
model2 = nn.Sequential(nn.Linear(3,16), nn.ReLU(), nn.Linear(16,8),
nn.ReLU(), nn.Linear(8,1)) # 同结构重建
model2.load_state_dict(torch.load("mlp_mastery.pt"))
state_dict 只存参数张量,比整个对象更稳、更省,是社区推荐做法。
讲清楚要点
- 神经网络 = 可微的函数复合:每层是矩阵乘法加非线性,反向传播只是链式法则的自动化。
- ReLU 的使命是打破线性:没有它,任意多层线性层叠起来仍等价于一层,模型永远学不了弯的边界。
- 四步曲顺序固定:
zero_grad → forward → backward → step。漏掉第一步,梯度会跨批次累加,训练莫名其妙震荡。 - 表格数据不一定需要深度学习:几百行的做题记录,XGBoost 往往又快又准;深度学习的优势在图像、文本、序列这类高维非结构化数据。
你可能会懵的地方
Q1:为什么必须激活函数? 两层线性层 W₂(W₁x) = (W₂W₁)x 仍是线性。ReLU 引入非线性后,网络才能拟合任意复杂的决策边界。
Q2:batch size 怎么选? 受显存和梯度稳定性双向约束,常见 32-256。小数据集 64 足够;batch 太大泛化可能变差,太小则梯度噪声大、训练慢。
Q3:loss 不降怎么排查? 按顺序查:学习率是否过大/过小 → 特征是否标准化 → 标签和数据是否对齐 → 模型容量是否够。90% 的问题出在前两步。
Q4:什么时候用深度学习,什么时候用 XGBoost? 中小规模表格数据(万行以内)首选 XGBoost/GBDT:无需调批、无需 GPU、自带处理缺失值。图像/文本/时序或特征需要自动学习时才上深度学习。
Q5:model.eval() 和 torch.no_grad() 各管什么?
eval 关闭 Dropout 和 BatchNorm 的训练态行为;no_grad 让框架不构建计算图,省内存提速。推理两个都要,缺一不可。
错法对照
| 错法 | 后果 | 正确做法 |
|---|---|---|
| 忘写 opt.zero_grad() | 梯度跨批累加,loss 震荡 | 每批开头清零 |
| 输出层接 Sigmoid 再用 BCEWithLogitsLoss | 双重 sigmoid,loss 降不动 | 直接输出 logits |
| numpy 数组直接喂 model | dtype/设备报错 | 先转 torch.tensor(float32) |
| 学习率设成 1 | loss 爆炸变 NaN | 从 1e-3 起步 |
| 训练完直接在训练集上庆祝 | 只会背答案 | 切独立测试集评估 |
卡点急救
- 报错
expected scalar type Float but found Double→ 构造张量时加dtype=torch.float32。 - loss = nan → 学习率太大或特征没标准化,先除量纲、降 lr。
- 提示 CUDA 不可用 → 本课 500 条数据 CPU 几秒跑完,不需要 GPU。
面试常问
Q1:反向传播是什么?
从损失出发,按链式法则沿计算图反向逐层算出每个参数的梯度;框架的 autograd 把这个过程自动化了,你只需 loss.backward()。
Q2:为什么 BCEWithLogitsLoss 不用先做 Sigmoid? 它内部融合了 Sigmoid 和交叉熵,用对数恒等式做了数值稳定处理,重复加 Sigmoid 反而会导致梯度消失、训练失败。
Q3:项目里为什么选/不选深度学习? 数据形态决定:表格小数据用 GBDT,非结构化或需要表示学习的用神经网络;再权衡训练成本、可解释性和部署难度。
自测三问
nn.Linear(3, 16)有多少个参数?(3×16 个权重 + 16 个偏置 = 64)- 忘记 zero_grad 会发生什么?(梯度累加,参数更新方向错乱)
- 推理时为什么要
torch.no_grad()?(不建计算图,省内存、加速)
小结一句话
深度学习 = 自动求导 + 梯度下降的规模化,框架把微积分压缩成四行训练循环,剩下的功课是数据。
延伸
- 官方文档:PyTorch 官方教程
- 下一课预告:神经网络最成功的主场之一是语言——从词向量到 attention,看懂 Transformer 如何读题干。