第 7 章 · 神经网络与反向传播

线性模型的假设空间是一条直线(平面),太弱。神经网络把它堆成了千层:线性变换 + 非线性激活的反复叠加,理论上能逼近任意函数(万能逼近定理)。支撑训练的核心算法反向传播,本质是链式法则(高数)的系统性应用。本章从单个神经元讲到多层网络,把「深度」的深度讲出来。

7.1 神经元:线性 + 激活

一个人工神经元 = 加权求和 + 非线性:

h=σ(w1x1+w2x2+⋯+wnxn+b)h = \sigma(w_1 x_1 + w_2 x_2 + \cdots + w_n x_n + b)

σ(激活函数)必须有非线性——没有它,多层网络塌缩成单层(线性套线性还是线性,第 6 章白堆)。常用激活:

  • ReLU:max(0, x)——现代默认,算得快、梯度不消失(正区恒 1);
  • sigmoid/tanh:老派,输出 (0,1)/(−1,1),深了梯度消失(导数最大才 0.25/1,连乘数十层趋零);
  • 激活的进化史就是「怎么让深层梯度活下来」的攻关史。

7.2 多层网络:深度从哪来

把神经元排成层,层层相接:

输入层(784 个像素)→ 隐藏层(128 神经元)→ 隐藏层(64)→ 输出层(10 类, softmax)

前向传播:数据从左到右流过,每层 = 矩阵乘法 + 激活:

h‘(1)‘=σ(W‘(1)‘x+b‘(1)‘),h‘(2)‘=σ(W‘(2)‘h‘(1)‘+b‘(2)‘), …h^`{(1)}` = \sigma(W^`{(1)}` x + b^`{(1)}`), \quad h^`{(2)}` = \sigma(W^`{(2)}` h^`{(1)}` + b^`{(2)}`), \ \ldots

计组课第 4 章的矩阵乘、数据结构课第 1 章的复杂度在此变现:一层 784×128 的矩阵乘 = 10 万次乘加,现代 GPU(几千个核心并行)就是为这种「巨大但规则」的运算造的(深度学习革命的硬件推手,第 1 章)。

万能逼近定理:单隐藏层足够宽就能逼近任意连续函数。那为什么还要深?效率:深网络用「层次复用」表达同样函数参数少得多(识别「猫脸」= 组合「边缘 → 纹理 → 部件 → 脸」,每层复用下层的成果,第 8 章的卷积把这个层次显式化)。「深度」就是「多层组合的表示复用」。

7.3 反向传播:链式法则的集体执行

训练 = 调全部参数让损失最小(第 6 章),问题是参数动辄百万,每个参数的梯度怎么算?反向传播(backpropagation):从损失出发,把梯度从输出层往输入层倒着传,一路用链式法则分解:

‘∂L‘‘∂w(1)‘=‘∂L‘‘∂h(2)‘⋅‘∂h(2)‘‘∂h(1)‘⋅‘∂h(1)‘‘∂w(1)‘\frac`{\partial L}``{\partial w^{(1)}}` = \frac`{\partial L}``{\partial h^{(2)}}` \cdot \frac`{\partial h^{(2)}}``{\partial h^{(1)}}` \cdot \frac`{\partial h^{(1)}}``{\partial w^{(1)}}`

工程上的完整循环(第 6 章的 SGD 循环放大版):

① 前向:输入流到输出,记下每层的中间值
② 算损失:预测 vs 标签
③ 反向:损失梯度倒传,得到每个参数的梯度
④ 更新:w ← w − η·梯度(SGD 一小批一小批来)

实现上的革命是自动微分(autograd):你只写前向计算,框架(PyTorch/TensorFlow)自动建计算图、自动反传。手推两层小网络的 backprop(考试题型)与「框架一行 loss.backward()」是同一件事的两个抽象层级,手推一遍(练习 2)才知道框架替你干了什么。

反向传播还有一个动人的对应:它和「学习」的神经科学假说(误差信号回传)同构——虽然真实大脑是否用 backprop 仍是未解之谜(AI 用的是数学版,不是仿生版,破除「AI 在模拟大脑」的流行误解)。

7.4 训练实战:手写数字识别

经典入门项目 MNIST(6 万张手写数字,28×28 像素),两层网络:

import torch, torch.nn as nn

model = nn.Sequential(
    nn.Flatten(),               # 28×28 → 784
    nn.Linear(784, 128), nn.ReLU(),
    nn.Linear(128, 64),  nn.ReLU(),
    nn.Linear(64, 10)           # 10 类的得分
)
opt = torch.optim.SGD(model.parameters(), lr=0.1)
loss_fn = nn.CrossEntropyLoss()

for epoch in range(10):
    for x, y in train_loader:              # 每批 64 张
        pred = model(x)                    # ① 前向
        loss = loss_fn(pred, y)            # ② 损失
        opt.zero_grad(); loss.backward()   # ③ 反向
        opt.step()                         # ④ 更新

十几个 epoch 后测试准确率 ~97%。回头看:这段代码 = 第 6 章(SGD + 交叉熵)+ 本章(多层 + 反向传播)的全部;深度学习的门槛不在代码量,在概念密度。

7.5 训练的常见病症与药方

深度学习训练是「看病开方」的过程,常见病对照表:

病症症状药方
loss 不降学习率不对 / 数据没洗从 1e-3 起扫;检查输入归一化(像素除 255!)
loss = nan学习率太大爆炸减 lr;查除零
训练好测试差过拟合(第 5 章)正则化全家桶:weight decay、dropout、数据增强
训练测试都差欠拟合加深加宽、训久点、换更强的结构

调参的第一课:每次只动一个旋钮,记录对比(软件工程课的实验纪律)。深度学习工程的 80% 是「可控的实验管理」,20% 才是灵感。

常见认知误区

  • 「神经网络模拟大脑」。只是灵感来源;真实的反向传播在生物上不成立,神经元也远比加权和复杂。把它当数学工具,别当仿生学。
  • 「层数越多越强」。深而无激活 = 单层;深而无规范(第 9 章的归一化技术)梯度消失/爆炸照样瘫痪。「能训得动的深度」才有意义。
  • 「参数多 = 记忆多 = 会过拟合」一定发生。大模型常「过参数化却泛化好」(双下降现象),经典统计直觉在高维区失效;泛化的理论仍是未解之谜,别拿 5.3 的教科书结论套死所有场景。
  • 「看懂了就能训好」。训练的隐性知识(数据清洗、超参直觉、debug 直觉)只能从练习 1 的跑模型里长出来,纸上谈兵是最大误区。

练习

  1. 动手:跑通 7.4 的 MNIST 代码(没有 GPU 用 Colab),把隐藏层砍到 16 个神经元再看准确率,体会「容量」;再加一层看变化。
  2. 手算:两层网络(各 1 神经元,sigmoid 激活)对输入 x = 1 的前向传播:w¹ = 0.5, b¹ = 0;w² = 2, b² = 0。算 h¹、输出,再手推 loss = (输出 − 1)² 对 w² 的梯度(链式法则两步)。
  3. 动手:激活函数实验:把 7.4 的 ReLU 全换成 sigmoid,对比收敛速度(同学习率),亲历「梯度消失」。
  4. 思考:反向传播需要「全量中间值存着等回传」,这是训练比推理耗内存的原因(大模型微调贵)。查一下「梯度检查点(gradient checkpointing)」的思路(用时间换内存),结合计组课第 7 章(存储层次)讨论这个权衡。