CS231n(肆)——神经网络与反向传播
$\large\star$ 回顾:梯度下降与优化器
上一讲的核心:通过梯度下降优化损失函数,找到最优参数 $W$。
数值梯度 与 解析梯度
| 数值梯度 | 解析梯度 |
|---|---|
| 慢,近似,容易写 | 快,精确,容易出错 |
实践中:推导解析梯度,用数值梯度检查实现是否正确。
SGD (随机梯度下降)
全量梯度计算在 $N$ 很大时代价太高,改用 mini-batch(32/64/128/256 个样本)近似:
1 | while True: |
学习率调度
几种常见策略:
- Step: 在固定轮数处将 LR 乘以 0.1(如 ResNet 在第 30、60、90 epoch)
- Cosine: $\alpha_t = \frac{1}{2}\alpha_0(1 + \cos(\frac{t\pi}{T}))$
- Linear: 线性衰减
- Inverse sqrt: 反平方根衰减
$\large\star$ 一、神经网络
1.1 从线性分类器到神经网络
线性分类器(之前):
2层神经网络(现在):
- $W_1$:第一层权重(学习中间表示)
- $\max(0, \cdot)$:非线性激活函数
- $W_2$:第二层权重(输出分类分数)
- 实践中每层还会加上可学习的偏置项
1.2 为什么需要非线性?
线性变换的复合仍然是线性的 -> 如果没有非线性,多层网络等价于单层线性分类器
非线性激活函数可以将数据变换到另一个空间,使得原本线性不可分的数据变得可分——类似于特征变换。
核心:非线性激活函数打破了”线性复合仍是线性”的束缚,让网络拥有真正的表达能力。
1.3 常用激活函数
| 函数 | 形式 | 说明 |
|---|---|---|
| Sigmoid | $\sigma(x) = \frac{1}{1+e^{-x}}$ | 早期常用,有梯度饱和问题 |
| tanh | $\tanh(x)$ | 零中心,仍有饱和问题 |
| ReLU | $\max(0, x)$ | 计算简单,收敛快,大多数问题的默认选择 |
| Leaky ReLU | $\max(0.1x, x)$ | 缓解ReLU的”死亡神经元”问题 |
| ELU | $x$ if $x>0$ else $\alpha(e^x-1)$ | 更平滑的负半轴 |
| Maxout | $\max(w_1^Tx+b_1, w_2^Tx+b_2)$ | 更灵活但参数翻倍 |
默认选择:ReLU — 简单高效,适用于大多数场景。
1.4 网络架构与命名
- “2层神经网络” = “1个隐藏层的神经网络”(输入→隐藏→输出)
- “3层神经网络” = “2个隐藏层的神经网络”
- 这种结构也称作全连接网络(Fully-Connected Network)或多层感知机(MLP)
1.5 前向计算示例
对于一个 $3072 \to 100 \to 10$ 的2层网络:
1 | # 前向传播 |
线性分类器直接学10个模板(每个类别一个);2层神经网络学100个模板,并在类别间共享这些模板——表达能力更强。
1.6 训练神经网络的完整流程(~20行代码)
- 定义网络:确定层数、每层大小
- 前向传播:逐层计算输出
- 计算解析梯度:通过反向传播
- 梯度下降更新参数
1 | # 训练2层神经网络 |
1.7 网络容量 vs 正则化
- 更多神经元 = 更大容量(能拟合更复杂函数)
- 不要用小网络来充当正则化! 应使用足够大的网络 + 强正则化(如 L2、Dropout)
- 演示工具:ConvNetJS、TensorFlow Playground
1.8 与生物神经元的类比(谨慎!)
| 生物神经元 | 人工神经网络 |
|---|---|
| 树突(dendrite)接收信号 | 加权输入 |
| 细胞体(cell body)整合信号 | 加权和 + 激活函数 |
| 轴突(axon)传出信号 | 输出 |
| 复杂的非线性和连接模式 | 简化为规则的多层结构 |
⚠️ 谨慎使用类比:生物神经元远比人工神经元复杂——树突能进行复杂非线性计算,突触是复杂的非线性动力系统而非单一权重。不过有趣的是,随机连接的网络也能工作(Xie et al., 2019 “Randomly Wired Neural Networks”)。
$\large\star$ 二、反向传播
2.1 问题:如何计算梯度?
对于一个2层网络:
- 手推 $\frac{\partial L}{\partial W_1}, \frac{\partial L}{\partial W_2}$?—— 极其繁琐、换损失函数需重推、对复杂模型不可行
- 解决方案:计算图 + 反向传播
2.2 核心思想:计算图 + 链式法则
将任意复杂函数表示为计算图(computational graph),然后用链式法则沿图反向传播梯度。
反向传播 = 链式法则的递归应用:
即:
2.3 标量反向传播:一个简单例子
设 $f(x, y, z) = (x + y)z$,给定 $x = -2, y = 5, z = -4$:
前向传播:1
2x=-2 + y=5 → q=3
q=3 × z=-4 → f=-12
反向传播(从输出往输入方向计算梯度):
- $\frac{\partial f}{\partial f} = 1$ (基准情况)
- $\frac{\partial f}{\partial z} = q = 3$
- $\frac{\partial f}{\partial q} = z = -4$
- $\frac{\partial f}{\partial x} = \frac{\partial f}{\partial q} \cdot \frac{\partial q}{\partial x} = -4 \cdot 1 = -4$
- $\frac{\partial f}{\partial y} = \frac{\partial f}{\partial q} \cdot \frac{\partial q}{\partial y} = -4 \cdot 1 = -4$
2.4 梯度流的模式(Patterns in Gradient Flow)
| 门(Gate) | 前向 | 反向梯度行为 |
|---|---|---|
| Add 门 | $x + y$ | 梯度分发器:上游梯度原样传递给两个输入分支 |
| Mul 门 | $x \times y$ | 交换乘法器:上游梯度×另一个输入。$x$ 的梯度 = 上游梯度 $\times y$ |
| Copy 门 | 分叉到多路 | 梯度累加器:多条路径传来的梯度求和 |
| Max 门 | $\max(x, y)$ | 梯度路由器:上游梯度只传给较大的输入,较小者得 0 |
💡 直觉记忆:
- Add:梯度均分,来多少传多少
- Mul:梯度放大/缩小取决于另一个输入的值(类似”开关”)
- Max:梯度的”独木桥”——只通过最大值那条路
2.5 Sigmoid 作为复合门的简写
Sigmoid 可以分解为多个基本门的组合,但其局部梯度可以一步完成:
技巧:计算图的表示不唯一——选择一个局部梯度容易表达的!
2.6 模块化实现:forward / backward API
现代深度学习框架都遵循这种设计模式:
1 | class MultiplyGate: |
每个节点实现:
- forward():计算结果并缓存反向所需中间值
- backward():应用链式法则计算损失对输入的梯度
2.7 扩展到向量/张量
关键点:损失 $L$ 始终是标量!梯度变量始终与原始变量形状相同。
标量→标量:$(x) \to (y)$,导数 $\frac{dy}{dx}$
向量→标量:$(x_1,…,x_D) \to (y)$,梯度 $\nabla_x y = [\frac{\partial y}{\partial x_1}, …, \frac{\partial y}{\partial x_D}]$
向量→向量:$(x1,…,x_D) \to (y_1,…,y_M)$,雅可比矩阵 $J{M \times D}$
反向传播中:
示例:ReLU 的向量反向传播
1 | # 前向 |
⚠️ 关键:雅可比矩阵非常稀疏——永远不要显式构造雅可比,而是隐式地进行矩阵-向量乘法!
2.8 矩阵级别的反向传播
对于矩阵乘法 $Y = XW$($X: N\times D$, $W: D\times M$, $Y: N\times M$):
🧠 记忆技巧:这两个公式是唯一能让形状匹配的写法!不需要记公式,只需确保矩阵维度对齐。
以 $N=64, D=M=4096$ 为例,雅可比将占据 ~256 GB 内存——必须隐式处理!
$\large\star$ 三、总结
| 概念 | 核心要点 |
|---|---|
| 全连接神经网络 | 线性函数 + 非线性激活的堆叠,表达能力远超线性分类器 |
| 激活函数 | 打破线性复合;ReLU 是默认首选 |
| 反向传播 | 沿计算图递归应用链式法则,计算所有输入的梯度 |
| 实现模式 | 图结构 + 节点 forward()/backward() API |
| 向量/矩阵扩展 | 梯度形状始终等于原变量形状;不要显式构造雅可比 |
下一讲:卷积神经网络(Convolutional Neural Networks)!
