深度学习笔记 02:MLP、前向传播与反向传播

这一篇整理多层感知机(MLP),以及神经网络到底如何通过前向传播和反向传播完成训练。

Forward pass 负责算预测和 loss,backpropagation 负责把 loss 对每个参数的梯度算出来,optimizer 再根据梯度更新参数。

这条链路是 deep learning 的训练核心。

神经元(neuron)在做什么

一个最简单的 neuron 可以写成:

\[ z = w^\top x + b \]

\[ h = \sigma(z) \]

其中,\(x\) 是输入,\(w\) 是权重,\(b\) 是 bias,\(\sigma\) 是 activation function。

如果没有 activation,这就是线性模型的一部分;加上 activation 后,它可以引入非线性。

一个具体例子:

1
2
3
x = [study_hours, sleep_hours]
w = [0.8, 0.3]
b = -2

这个 neuron 可能在粗略判断“考试通过倾向”。学习时间和睡眠时间越高,\(z\) 越大,经过 sigmoid 后输出越接近 1。

层(layer)就是一组神经元

把多个 neuron 放在一起,就是一层。

线性层通常写成:

\[ z = Wx + b \]

\[ h = \sigma(z) \]

这里 \(W\) 是矩阵,\(b\) 是向量。每一行权重可以看成一个 neuron。

如果输入是 batch,写法会变成:

\[ H = \sigma(XW^\top + b) \]

多层感知机(MLP)

MLP 由多个 fully connected layer 组成。

一个两层 MLP 可以写成:

\[ h = \sigma(W_1x + b_1) \]

\[ \hat{y} = W_2h + b_2 \]

其中,\(h\) 是 hidden representation。

分类任务里,最后通常接 softmax:

\[ p(y=k|x) = \frac{e^{z_k}}{\sum_j e^{z_j}} \]

回归任务里,最后可以直接输出连续值。

MLP 的缺点是没有针对图像、序列、文本结构做特殊假设。它很通用,但也很“笨”:如果输入是图片,MLP 不知道相邻像素之间有局部关系;如果输入是序列,MLP 不知道 token 顺序有意义。

前向传播(forward pass)

Forward pass 就是从输入一路算到输出和 loss。

以二分类 MLP 为例:

1
2
3
4
5
6
x
-> linear layer
-> ReLU
-> linear layer
-> sigmoid
-> binary cross entropy loss

写成公式:

\[ h = \text{ReLU}(W_1x+b_1) \]

\[ \hat{y} = \sigma(W_2h+b_2) \]

\[ L = -[y\log(\hat{y}) + (1-y)\log(1-\hat{y})] \]

Forward pass 只解决一个问题:当前参数下,模型预测得怎么样。

但训练还需要知道:如果 loss 大,参数应该怎么改。

反向传播(backpropagation)

Backpropagation 的核心是 chain rule。

如果:

\[ L = L(\hat{y}), \quad \hat{y}=f(h), \quad h=g(\theta) \]

那么:

\[ \frac{\partial L}{\partial \theta} = \frac{\partial L}{\partial \hat{y}} \frac{\partial \hat{y}}{\partial h} \frac{\partial h}{\partial \theta} \]

这就是反向传播的直觉:loss 对前面参数的影响,要沿着计算路径一层层传回去。本质上就是高效地在计算图上应用链式法则。

一个计算图例子

\[ z = wx + b \]

\[ \hat{y} = z \]

\[ L = (\hat{y} - y)^2 \]

现在要求 \(L\)\(w\) 的梯度。

根据 chain rule:

\[ \frac{\partial L}{\partial w} = \frac{\partial L}{\partial \hat{y}} \frac{\partial \hat{y}}{\partial z} \frac{\partial z}{\partial w} \]

分别计算:

\[ \frac{\partial L}{\partial \hat{y}} = 2(\hat{y}-y) \]

\[ \frac{\partial \hat{y}}{\partial z} = 1 \]

\[ \frac{\partial z}{\partial w} = x \]

所以:

\[ \frac{\partial L}{\partial w} = 2(\hat{y}-y)x \]

如果预测太大,\(\hat{y}-y\) 为正,梯度方向会让 \(w\) 下降;如果预测太小,梯度方向会让 \(w\) 上升。这个例子很简单,但多层网络也是同一个逻辑,只是计算图更大。

PyTorch 里的训练大概是:

1
2
3
4
5
optimizer.zero_grad()
pred = model(x)
loss = criterion(pred, y)
loss.backward()
optimizer.step()

这里:

  • pred = model(x) 是 forward pass
  • loss.backward() 是 backpropagation
  • optimizer.step() 是参数更新
  • zero_grad() 会清零梯度,防止上一轮的参数参与新的更新

激活函数(activation function)

Activation function 提供非线性。

常见 activation:

函数 形式 特点
Sigmoid \(\frac{1}{1+e^{-x}}\) 输出在 0 到 1,容易梯度饱和
Tanh \(\tanh(x)\) 输出在 -1 到 1,也可能饱和
ReLU \(\max(0,x)\) 简单高效,深度网络常用
GELU 平滑版本 Transformer 里常见

Sigmoid 和 tanh 在输入绝对值很大时,曲线会变平,梯度接近 0。深层网络里这会导致前面层学得很慢。

ReLU 的优点是正半轴梯度稳定,计算简单。但 ReLU 也可能出现 dead neuron:如果某个 neuron 长期输出 0,它对应参数可能很难更新。

参考资料