小结
- 前向传播在神经网络定义的计算图中按顺序计算和存储中间变量,它的顺序是从输入层到输出层。
- 反向传播按相反的顺序(从输出层到输入层)计算和存储神经网络的中间变量和参数的梯度。
- 在训练深度学习模型时,前向传播和反向传播是相互依赖的。
- 训练比预测需要更多的内存。
练习
-
向本节中描述的模型的隐藏层添加偏置项(不需要在正则化项中包含偏置项)。
- 画出相应的计算图。
- 推导正向和反向传播方程。
正向传播方程及各变量维度
-
输入数据 X \mathbf{X} X:
- 维度: n × m n \times m n×m
-
隐藏层线性变换:
Z = X W + b \mathbf{Z} = \mathbf{X} \mathbf{W} + \mathbf{b} Z=XW+b- W \mathbf{W} W: 权重矩阵, 维度 m × h m \times h m×h
- b \mathbf{b} b: 偏置向量, 维度 1 × h 1 \times h 1×h
- Z \mathbf{Z} Z: 隐藏层输入, 维度 n × h n \times h n×h
-
隐藏层激活:
A = σ ( Z ) \mathbf{A} = \sigma(\mathbf{Z}) A=σ(Z)- A \mathbf{A} A: 激活值, 维度 n × h n \times h n×h
-
输出层线性变换:
Y = A V + c \mathbf{Y} = \mathbf{A} \mathbf{V} + \mathbf{c} Y=AV+c- V \mathbf{V} V: 权重矩阵, 维度 h × o h \times o h×o
- c \mathbf{c} c: 偏置向量, 维度 1 × o 1 \times o 1×o
- Y \mathbf{Y} Y: 输出, 维度 n × o n \times o n×o
反向传播方程及各变量维度
假设损失函数为 L ( Y , T ) L(\mathbf{Y}, \mathbf{T}) L(Y,T),其中 T \mathbf{T} T 是真实标签,维度为 n × o n \times o n×o。我们通过链式法则计算各参数的梯度。
-
输出层反向传播:
-
输出层损失梯度:
∂ L ∂ Y = ∂ L ∂ Y \frac{\partial L}{\partial \mathbf{Y}} = \frac{\partial L}{\partial \mathbf{Y}} ∂Y∂L=∂Y∂L -
维度: n × o n \times o n×o
-
输出层权重梯度:
∂ L ∂ V = A T ∂ L ∂ Y \frac{\partial L}{\partial \mathbf{V}} = \mathbf{A}^T \frac{\partial L}{\partial \mathbf{Y}} ∂V∂L=AT∂Y∂L -
∂ L ∂ V \frac{\partial L}{\partial \mathbf{V}} ∂V∂L: 维度 h × o h \times o h×o
-
A T \mathbf{A}^T AT: 维度 h × n h \times n h×n
-
∂ L ∂ Y \frac{\partial L}{\partial \mathbf{Y}} ∂Y∂L: 维度 n × o n \times o n×o
-
输出层偏置梯度:
∂ L ∂ c = ∑ i = 1 n ∂ L ∂ Y i \frac{\partial L}{\partial \mathbf{c}} = \sum_{i=1}^{n} \frac{\partial L}{\partial \mathbf{Y}_i} ∂c∂L=i=1∑n∂Yi∂L -
∂ L ∂ c \frac{\partial L}{\partial \mathbf{c}} ∂c∂L: 维度 1 × o 1 \times o 1×o
-
激活值梯度:
∂ L ∂ A = ∂ L ∂ Y V T \frac{\partial L}{\partial \mathbf{A}} = \frac{\partial L}{\partial \mathbf{Y}} \mathbf{V}^T ∂A∂L=∂Y∂LVT -
∂ L ∂ A \frac{\partial L}{\partial \mathbf{A}} ∂A∂L: 维度 n × h n \times h n×h
-
∂ L ∂ Y \frac{\partial L}{\partial \mathbf{Y}} ∂Y∂L: 维度 n × o n \times o n×o
-
V T \mathbf{V}^T VT: 维度 o × h o \times h o×h
-
-
隐藏层反向传播:
-
激活函数梯度:
∂ L ∂ Z = ∂ L ∂ A ⊙ σ ′ ( Z ) \frac{\partial L}{\partial \mathbf{Z}} = \frac{\partial L}{\partial \mathbf{A}} \odot \sigma'(\mathbf{Z}) ∂Z∂L=∂A∂L⊙σ′(Z) -
∂ L ∂ Z \frac{\partial L}{\partial \mathbf{Z}} ∂Z∂L: 维度 n × h n \times h n×h
-
∂ L ∂ A \frac{\partial L}{\partial \mathbf{A}} ∂A∂L: 维度 n × h n \times h n×h
-
σ ′ ( Z ) \sigma'(\mathbf{Z}) σ′(Z): 维度 n × h n \times h n×h
-
隐藏层权重梯度:
∂ L ∂ W = X T ∂ L ∂ Z \frac{\partial L}{\partial \mathbf{W}} = \mathbf{X}^T \frac{\partial L}{\partial \mathbf{Z}} ∂W∂L=XT∂Z∂L -
∂ L ∂ W \frac{\partial L}{\partial \mathbf{W}} ∂W∂L: 维度 m × h m \times h m×h
-
X T \mathbf{X}^T XT: 维度 m × n m \times n m×n
-
∂ L ∂ Z \frac{\partial L}{\partial \mathbf{Z}} ∂Z∂L: 维度 n × h n \times h n×h
-
隐藏层偏置梯度:
∂ L ∂ b = ∑ i = 1 n ∂ L ∂ Z i \frac{\partial L}{\partial \mathbf{b}} = \sum_{i=1}^{n} \frac{\partial L}{\partial \mathbf{Z}_i} ∂b∂L=i=1∑n∂Zi∂L -
∂ L ∂ b \frac{\partial L}{\partial \mathbf{b}} ∂b∂L: 维度 1 × h 1 \times h 1×h
-
-
假设想计算二阶导数。计算图发生了什么?预计计算需要多长时间?
计算过程
正向传播:计算损失 𝐿(𝑥)反向传播:计算一阶导数 ∇𝐿(𝑥)
二阶反向传播:对每个一阶导数分量再计算一次反向传播,得到 Hessian 矩阵 𝐻𝐿(𝑥)
应该是o(n方)的复杂度,时间大大增加 -
假设计算图对当前拥有的GPU来说太大了。
- 请试着把它划分到多个GPU上。
- 与小批量训练相比,有哪些优点和缺点?
划分模型层:
将模型的不同层或部分分配到不同的GPU上。例如,如果有两个GPU,可以将前半部分的层放在GPU 0上,后半部分的层放在GPU 1上。
import torch
import torch.nn as nn
class SplitNN(nn.Module):
def __init__(self):
super(SplitNN, self).__init__()
self.fc1 = nn.Linear(1000, 500).cuda(0) # First part on GPU 0
self.fc2 = nn.Linear(500, 10).cuda(1) # Second part on GPU 1
def forward(self, x):
x = x.cuda(0)
x = torch.relu(self.fc1(x))
x = x.cuda(1)
x = self.fc2(x)
return x
model = SplitNN()
划分数据和计算:
在前向传播和反向传播过程中,数据需要在不同GPU之间传递。可以利用PyTorch的torch.cuda模块来实现。
input_data = torch.randn(32, 1000).cuda(0) # Input data on GPU 0
output = model(input_data)
loss = torch.nn.functional.mse_loss(output, torch.randn(32, 10).cuda(1)) # Loss computed on GPU 1
loss.backward()
同步计算:
确保各GPU之间的计算同步,可以利用torch.cuda.synchronize()来实现。
python
torch.cuda.synchronize()
3.2 优点和缺点
优点
更大模型的训练:
模型并行可以使得训练更大规模的模型成为可能,因为单个GPU可能无法容纳整个模型的计算图和参数。
充分利用GPU资源:
在拥有多个GPU的环境下,可以充分利用所有GPU的计算能力,从而提高计算效率。
更高效的内存使用:
通过将模型划分到多个GPU上,可以更加高效地利用每个GPU的内存,避免单个GPU因内存不足而导致的计算瓶颈。
缺点
通信开销:
在多个GPU之间传递数据会产生通信开销,特别是在前向传播和反向传播过程中频繁的数据传递,这会影响整体的计算效率。
实现复杂性:
需要对模型进行拆分和调整,这增加了实现和维护的复杂性。需要特别注意不同GPU之间的数据同步和传递。
负载不均衡:
如果模型的不同部分计算量差异较大,可能会导致GPU负载不均衡,从而无法充分利用所有GPU的计算能力。
与小批量训练比较
小批量训练的优点
减少内存占用:
小批量训练可以减少每次训练过程中所需的内存,从而能够在单个GPU上训练更大的模型。
更稳定的梯度估计:
小批量训练通过累积多个样本的梯度,可以得到更稳定和准确的梯度估计,从而有助于训练过程的收敛。
实现简单:
实现和维护小批量训练相对简单,不需要在多个GPU之间进行复杂的数据传递和同步。
小批量训练的缺点
训练速度较慢:
与模型并行相比,小批量训练在单个GPU上的训练速度可能较慢,特别是在数据量较大时。
无法训练超大模型:
对于超大规模的模型,即使使用小批量训练也可能无法在单个GPU上进行训练。