文章目录
一、前言
这篇博文旨在基于pytorch深度学习框,讲解模型定义以及推理数据流向,围绕下面这个问题:
用过pytorch的朋友肯定知道,在模型所谓model
初始化之后,再去看这个变量的内容,可以看到每一层结构,那么可能会有一个疑问,我还没forward
(推理),怎么就知道我的数据流向呢?这个模型定义的顺序和初始化的顺序有没有关系呢?
二、模型初始化
在 PyTorch 的 nn.Module 中,参数层(如 Conv2d、Linear 等)确实有前后顺序,这与它们在模型中定义的顺序一致。这种顺序在以下几个方面体现:
2.1 定义顺序
在定义模型的过程中,层的顺序是重要的,因为它决定了数据在前向传播(forward pass)中的流动顺序。例如:
import torch.nn as nn
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc1 = nn.Linear(10, 20) # 第一层
self.relu = nn.ReLU() # 第二层
self.fc2 = nn.Linear(20, 5) # 第三层
def forward(self, x):
x = self.fc1(x) # 数据先经过 fc1
x = self.relu(x) # 然后经过 ReLU 激活
x = self.fc2(x) # 最后经过 fc2
return x
在这个例子中:
- self.fc1 是第一个线性层。
- self.relu 是中间的激活层。
- self.fc2 是最后一个线性层。
数据流动顺序决定了各层的作用顺序。
2.2 参数顺序
- 在调用 self.parameters() 或 self.named_parameters() 时,返回的参数的顺序与层定义的顺序一致:
model = SimpleModel() for name, param in model.named_parameters(): print(name)
- 输出顺序:
fc1.weight fc1.bias fc2.weight fc2.bias
- 这里的顺序反映了层的定义顺序(fc1 在 fc2 之前)。
2.3 在优化器中的顺序
当你将模型的参数传递给优化器时,它们会保留这个顺序:
import torch.optim as optim
optimizer = optim.SGD(model.parameters(), lr=0.01)
优化器会按照 self.parameters() 返回的顺序更新参数。但这并不影响梯度更新的正确性,因为每个参数的梯度是独立计算和更新的。
2.4 在计算图中的顺序
前后顺序在计算图中也很重要,尤其是在前向传播和反向传播过程中。数据按层的顺序流动,梯度也按相反的顺序传播(从最后一层向前传递)。
2.5 总结
- 定义顺序决定了前向传播顺序。
- self.parameters() 返回的参数顺序与定义顺序一致。
- 优化器会按顺序更新参数,但不会影响训练的正确性。
这种顺序有助于确保模型前向和反向传播流程的逻辑性和可读性。
三、推理数据流
层的顺序记录:定义 vs. Forward 过程
这里有个核心问题是:PyTorch 中参数顺序是基于 init 定义,还是 forward 方法中的执行顺序?
3.1 init 定义顺序
-
在 nn.Module 的 init 方法中,你定义了模型的各个层(如 nn.Conv2d、nn.Linear 等)。
-
PyTorch 会在初始化时注册这些层,并按照它们在 init 中定义的顺序存储。这个顺序决定了调用 self.parameters() 或 self.named_parameters() 时参数的顺序。
-
示例:
class SimpleModel(nn.Module): def __init__(self): super(SimpleModel, self).__init__() self.fc1 = nn.Linear(10, 20) # 第一层 self.fc2 = nn.Linear(20, 5) # 第二层
- 在这里,self.fc1 会先被注册,self.fc2 后注册。
- 使用 model.parameters() 会按 fc1 → fc2 的顺序返回。
3.2 forward 中的执行顺序
- forward 决定数据的流动顺序。
你可以在 forward 中改变数据流经各层的顺序,但这不会影响参数的存储顺序。 - 示例:
def forward(self, x):
x = self.fc2(x) # 先经过 fc2
x = self.fc1(x) # 再经过 fc1
return x
- 在这个例子中:
- 数据流顺序是 fc2 → fc1。
- 参数顺序依然是 fc1 → fc2,因为它们是在 init 中按这个顺序定义的。
- 注意:
这种灵活性允许你在 forward 中实现复杂的前向传播逻辑,比如跳跃连接、并行处理等。
3.3 训练与优化器中的顺序
- 优化器更新顺序:
优化器更新参数的顺序与 init 定义的顺序一致,而不是 forward 中的顺序。
原因: 参数和梯度是分层存储和更新的,优化器不会关心数据流顺序。
3.4 总结:
- 参数的顺序是在 init 中定义时决定的。
- 数据的流动顺序是在 forward 方法中决定的。
- 训练过程中,参数更新顺序遵循 init 的定义,而非 forward 的执行顺序。
这种设计允许你在 forward 中灵活构建前向传播逻辑,而 init 确保所有层的参数能被正确注册和管理。