CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
机器学习入门/课时/深度学习基础
RawBlame
课时目录12 节

机器学习入门

  1. 01机器学习是什么
  2. 02数学基础
  3. 03Python 与数据栈
  4. 04数据预处理
  5. 05监督学习
  6. 06无监督学习
  7. 07模型评估
  8. 08Scikit-learn 工作流
  9. 09深度学习基础
  10. 10NLP 与 Transformer
  11. 11TensorFlow 与 PyTorch
  12. 12综合练习

深度学习基础

MLP、CNN、RNN、反向传播。

40 分钟

深度学习基础

这一课你将能做到

  1. 用 PyTorch 的 autograd 理解「自动求导」——反向传播不再是一个黑名词
  2. 用 TensorDataset + DataLoader 把数据切成批次,喂进神经网络
  3. 用 nn.Sequential 搭一个三层 MLP,跑通「zero_grad → 前向 → 反向 → 更新」训练四步曲
  4. 用 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 数组直接喂 modeldtype/设备报错先转 torch.tensor(float32)
学习率设成 1loss 爆炸变 NaN从 1e-3 起步
训练完直接在训练集上庆祝只会背答案切独立测试集评估

卡点急救

  1. 报错 expected scalar type Float but found Double → 构造张量时加 dtype=torch.float32。
  2. loss = nan → 学习率太大或特征没标准化,先除量纲、降 lr。
  3. 提示 CUDA 不可用 → 本课 500 条数据 CPU 几秒跑完,不需要 GPU。

面试常问

Q1:反向传播是什么? 从损失出发,按链式法则沿计算图反向逐层算出每个参数的梯度;框架的 autograd 把这个过程自动化了,你只需 loss.backward()。

Q2:为什么 BCEWithLogitsLoss 不用先做 Sigmoid? 它内部融合了 Sigmoid 和交叉熵,用对数恒等式做了数值稳定处理,重复加 Sigmoid 反而会导致梯度消失、训练失败。

Q3:项目里为什么选/不选深度学习? 数据形态决定:表格小数据用 GBDT,非结构化或需要表示学习的用神经网络;再权衡训练成本、可解释性和部署难度。

自测三问

  1. nn.Linear(3, 16) 有多少个参数?(3×16 个权重 + 16 个偏置 = 64)
  2. 忘记 zero_grad 会发生什么?(梯度累加,参数更新方向错乱)
  3. 推理时为什么要 torch.no_grad()?(不建计算图,省内存、加速)

小结一句话

深度学习 = 自动求导 + 梯度下降的规模化,框架把微积分压缩成四行训练循环,剩下的功课是数据。

延伸

  • 官方文档:PyTorch 官方教程
  • 下一课预告:神经网络最成功的主场之一是语言——从词向量到 attention,看懂 Transformer 如何读题干。
加载本节练习…
← 上一课Scikit-learn 工作流下一课 →NLP 与 Transformer