跳转至

第二章 深度学习基础速览

导读

这一章不是要教你成为深度学习专家——而是给你**工程师视角的最小知识量**:足以让你在后续章节遇到"梯度""激活函数""反向传播"时不会卡住。如果你已经熟悉 PyTorch 与神经网络基本概念,可以快速浏览本章并跳到第三章。

2.1 张量:深度学习的"数据类型"

深度学习计算的基本数据结构是**张量(Tensor)**——多维数组。

名字 维度 例子
标量 scalar 0 5
向量 vector 1 [1.0, 2.0, 3.0]
矩阵 matrix 2 [[1, 2], [3, 4]]
张量 tensor 3+ 图像 [3, 224, 224]、batch 文本 [32, 512, 768]

对工程师来说,张量就是"高维 numpy 数组 + GPU 加速 + 自动求导"。

import torch
x = torch.randn(32, 512, 768, device="cuda")   # batch=32, seq=512, hidden=768
x.requires_grad_(True)                          # 标记需要梯度

张量在深度学习中几乎是一切数据的载体:输入是张量、权重是张量、梯度是张量、loss 是张量。

2.2 线性变换与感知机

神经网络最基本的运算单元是**线性变换 + 非线性激活**:

def neuron(x, w, b):
    z = (x * w).sum() + b           # 加权求和
    return relu(z)                  # 非线性激活

这个最简结构叫**感知机(Perceptron)**,1958 年由 Rosenblatt 提出。

感知机与多层感知机

单层感知机能学线性分类(如 AND、OR),但**学不会 XOR**——这是 1969 年 Minsky 的著名批评,让神经网络研究陷入寒冬近 20 年。

解法是把多层堆起来:多层感知机(Multi-Layer Perceptron, MLP)。理论已证明:只要有足够宽的隐藏层,两层 MLP 可以逼近任意连续函数(万能逼近定理)。这就是"深度学习"的雏形——"深度"只是说层数多。

2.3 激活函数

如果没有非线性激活,多层线性变换等价于单层。所以激活函数是"让网络变深才有意义"的关键。

常见激活函数:

函数 公式 用途
Sigmoid 1/(1+e^-x) 早期,输出 0-1 概率
Tanh (e^x-e^-x)/(e^x+e^-x) 输出 -1 到 1
ReLU max(0, x) CNN、早期 Transformer,简单高效
GELU x·Φ(x) BERT、GPT-2 标准
SwiGLU Swish(xW1) * (xW2) Llama、现代 LLM 标配

现代 LLM 几乎都用 SwiGLU——它是 GLU(Gated Linear Unit)的变体,引入"门控"机制,比 ReLU 表现更好但参数量稍多。

2.4 反向传播:自动求导的链式法则

神经网络的训练核心是**反向传播(Backpropagation)**——通过链式法则从输出端反向计算每个参数的梯度。

前向反向传播计算图

工程师视角的关键点:

  1. 前向必须保存中间激活——反向求导时要用到。这就是为什么训练显存 ≈ batch × 层数 × hidden × dtype 大致成立。
  2. 反向遍历两次计算图——所以训练算力 ≈ 2-3 × 推理算力(前向 + 反向 + 优化器更新)。
  3. 更新规则W ← W - η · ∂L/∂W,η 是学习率(learning rate, lr)。
  4. 现代框架自动完成——PyTorch 的 loss.backward() + optimizer.step() 自动完成所有事。
logits = model(input)              # 前向
loss = cross_entropy(logits, target)
loss.backward()                    # 反向:自动填充所有 .grad
optimizer.step()                   # 更新参数
optimizer.zero_grad()              # 清理梯度,准备下一步

2.5 优化器:从 SGD 到 AdamW

最朴素的更新方式是 SGD(Stochastic Gradient Descent)

W = W - lr * W.grad

问题:梯度有噪声、方向不平稳、不同参数收敛速度不一致。改进:

  • Momentum:保留历史梯度的滑动平均,像有惯性一样冲过鞍点;
  • RMSProp:对每个参数自适应学习率(按梯度平方滑动归一化);
  • Adam = Momentum + RMSProp,主流选择;
  • AdamW = Adam + 权重衰减解耦,当前 LLM 训练事实标准

AdamW 公式(伪代码):

m = β1 * m + (1 - β1) * g              # 一阶矩
v = β2 * v + (1 - β2) * g * g          # 二阶矩
m_hat = m / (1 - β1^t)                 # bias 修正
v_hat = v / (1 - β2^t)
W = W - lr * m_hat / (sqrt(v_hat) + eps) - lr * weight_decay * W

典型超参:lr=3e-4(小模型)/ 2e-4(大模型)、β1=0.9β2=0.95(LLM 不同于 Adam 默认 0.999)、weight_decay=0.1

2.6 损失函数

损失函数衡量"模型输出与目标的差距"。训练就是最小化它。

  • 回归:MSE(均方误差)Σ(ŷ - y)²
  • 分类:Cross-Entropy -Σ y·log(ŷ)
  • LLM 预训练:也是 Cross-Entropy,但目标不是 one-hot 类别,而是"下一个 token 的真实 id"。
# LLM 预训练 loss 伪代码
logits = model(input_ids)             # [B, L, V]
target = input_ids[:, 1:]             # 错位一位,预测下一个
logits = logits[:, :-1, :]
loss = F.cross_entropy(logits.reshape(-1, V), target.reshape(-1))

这个 loss 看起来平平无奇,但它"训练"出了今天所有的 LLM——下一个 token 预测足够难,使得模型为了少错,必须学会语法、知识、推理。

2.7 训练循环骨架

for epoch in range(num_epochs):
    for batch in dataloader:
        input_ids = batch.input_ids.to("cuda")
        labels    = batch.labels.to("cuda")

        logits = model(input_ids)
        loss = F.cross_entropy(logits.view(-1, V), labels.view(-1))

        loss.backward()
        optimizer.step()
        scheduler.step()                  # 学习率调度
        optimizer.zero_grad()

        if step % 100 == 0:
            log({"loss": loss.item(), "lr": scheduler.get_last_lr()[0]})

工程师读这段代码时关注的是:

  1. 数据移动:每步要把 batch 从 CPU 拷到 GPU,是 I/O 瓶颈之一;
  2. 前向显存:保存中间激活用于反向;
  3. 通信:分布式训练时,反向完成后要做 all-reduce 同步梯度;
  4. 学习率调度:warmup + cosine 是 LLM 标准。

2.8 评估指标

训练 loss 在降,不代表模型在变好——可能过拟合。常用指标:

  • 训练 loss / 验证 loss:差值大 = 过拟合;
  • Accuracy:分类任务;
  • Perplexity(PPL):语言模型常用,exp(loss),越低越好;
  • 下游 benchmark:MMLU、HumanEval 等(第 14 章详述)。

工程实战要点

  • 学习率是最重要的超参:宁可花时间调 lr 也不要乱调别的;
  • AdamW + cosine + warmup 是 90% LLM 训练的默认配方;
  • 训练显存 ≈ 模型权重 + 激活 + 优化器状态,三者比例随 batch/seq 变化;
  • 梯度爆炸/消失:watch grad_norm,正常范围 0.1-10,过大用 grad clip;
  • 混合精度(bf16):现代 GPU 都支持,2× 速度、显存减半,几乎是默认。

小结

  • 张量是深度学习的基本数据结构,本质是 GPU 加速的高维数组;
  • 神经元 = 线性变换 + 非线性激活;多层堆叠 = MLP;
  • 反向传播通过链式法则自动求导,PyTorch 已自动化;
  • 优化器从 SGD 演进到 AdamW,是当前 LLM 训练标准;
  • LLM 预训练 loss 就是 next-token 的 cross-entropy;
  • 训练循环 = forward + backward + step,配合 lr schedule 与混合精度。

练习题

  1. 为什么"没有非线性激活的多层线性网络等价于单层"?
  2. 训练算力大约是推理算力的几倍?为什么?
  3. AdamW 相对于 Adam 的关键改进是什么?为什么对 LLM 重要?
  4. 写出 LLM 预训练的 loss 表达式,并解释"为什么 next-token 预测能学到知识"。
  5. bf16 与 fp16 的区别?为什么现代 LLM 训练更倾向 bf16?
  6. 解释 loss.backward() 在 PyTorch 中具体做了什么。
  7. 训练时如果发现 grad_norm 突然飙到 1000+,可能是哪些原因?怎么处理?
  8. 学习率 warmup 是怎么工作的?为什么 LLM 训练几乎一定要 warmup?
  9. 一个 7B 模型用 AdamW、batch=128、seq=4096 训练,估算大约需要多少显存。

下一章:第三章 Transformer架构