nn.Sequential vs nn.ModuleList
为了帮你快速理解,我将它们的核心区别总结在下表中:
| 特性 | nn.Sequential |
nn.ModuleList |
|---|---|---|
| 核心定位 | 一个可执行的、有序的容器,本身是一个"模型"。 | 一个存储子模块的Python列表,是"模型部件的集合"。 |
| 可调用性 | 可直接调用,输入会按顺序通过所有子模块。 | 不可直接调用,只是一个列表,需要手动遍历或索引访问。 |
| 前向传播 | 自动定义。只需调用容器本身,内部顺序即前向逻辑。 | 手动定义 。你需在forward中明确写出如何使用每个模块。 |
| 主要用途 | 构建简单、线性的模型流水线(如 CNN 的 conv->relu->pool 堆叠)。 | 组织和管理一组模块 ,以实现灵活、非顺序的连接(如循环、分支、共享层)。 |
| 顺序重要性 | 至关重要,顺序决定了数据流向。 | 不重要 ,存储顺序不影响模型逻辑,取决于你在forward中如何使用它们。 |
错误示例
from torch import nn
import os
class ASimpleNet(nn.Module):
def __init__(self, layers=3):
super(ASimpleNet, self).__init__()
# 使用 Sequential 替代 ModuleList
self.linears = nn.Sequential(
*[nn.Linear(3, 3, bias=False) for _ in range(layers)]
)
def forward(self, x):
print("forward batchsize is: {}".format(x.size()[0]))
x = self.linears(x) # 正确调用
x = torch.relu(x)
return x
[nn.Linear(3, 3, bias=False) for i in range(3)] 中,定义三个完全相同(输入输出均为3维)的线性层,通常有以下几种设计意图:
-
构建更深的网络(最常见意图):
-
想法:通过增加网络深度(层数)来增强模型的表达能力,学习更复杂的特征变换。这是深度学习的基础理念。
-
你代码中的问题 :纯线性层堆叠(
Linear -> Linear -> Linear)没有中间非线性激活函数 。根据线性代数,多个线性变换的复合等价于一个线性变换 (即W3(W2(W1*x))等价于(W3*W2*W1)*x),因此深度没有带来任何好处。 -
正确做法 :应在每层线性层后加入非线性激活(如ReLU),形成
Linear -> ReLU -> Linear -> ReLU -> Linear的结构,这样每层才能学习不同的非线性映射。
-
-
实现并行或分支结构:
-
想法:让同一输入同时被多个不同的线性层(可视为不同的"专家"或"视角")处理,然后将结果融合(相加、拼接等)。
-
这需要
nn.ModuleList:因为你需要分别调用这三个层,然后手动组合它们的输出。
-
-
作为模块复用的示例:
- 想法 :教学演示如何用
nn.ModuleList或nn.Sequential来管理多个相同类型的层。
- 想法 :教学演示如何用
💡 如何正确使用它们?------ 实例对比
假设我们想构建一个网络,它先将输入投影到三个不同的3维子空间,然后将结果相加 。这必须 使用 nn.ModuleList:
class ParallelNet(nn.Module):
def __init__(self):
super().__init__()
# 用 ModuleList 存储三个独立的层
self.branches = nn.ModuleList([nn.Linear(3, 3) for _ in range(3)])
def forward(self, x):
branch_outputs = [branch(x) for branch in self.branches] # 分别计算
x = torch.stack(branch_outputs).sum(dim=0) # 堆叠后求和
return x
如果只是想构建一个简单的深度网络,则使用 nn.Sequential 更清晰:
class DeepNet(nn.Module):
def __init__(self):
super().__init__()
# 用 Sequential 定义顺序执行的层
self.net = nn.Sequential(
nn.Linear(3, 3),
nn.ReLU(), # 关键!加入非线性
nn.Linear(3, 3),
nn.ReLU(), # 关键!加入非线性
nn.Linear(3, 3),
)
def forward(self, x):
return self.net(x) # 调用一次即可
总结与选择建议
-
关于"三个层" :在层间没有非线性激活 的情况下,堆叠多个
Linear层是无效的。你需要明确目标:是构建深度网络 (加非线性,用Sequential),还是并行分支 (用ModuleList手动融合)。 -
关于容器选择:
-
当你的模型是
层A -> 层B -> 层C这种简单、严格的顺序 时,毫不犹豫地选择nn.Sequential。 -
当你的模型需要循环 (如RNN)、条件判断 、分支处理 或自定义连接方式 时,选择
nn.ModuleList来存储模块,然后在forward中实现你的逻辑。
-
简单记:Sequential 用于"自动流水线",ModuleList 用于"手动装配线"。