对比
| 维度 | nn.Sequential | nn.ModuleList |
|---|---|---|
| 核心定位 | 带执行逻辑的“流水线”容器 | 仅存储层的“货架”容器 |
| forward 方法 | 自带,按顺序执行所有层(黑盒执行) | 无,需手动遍历/调用层(白盒控制) |
| 参数注册 | ✅ 自动注册内部层的参数 | ✅ 自动注册内部层的参数 |
| 灵活性 | 低:仅支持线性顺序执行,无法中断/分支 | 高:支持跳层、分支、条件执行、快捷连接 |
| 层操作 | ❌ 不支持动态 append/extend(需重新定义) | ✅ 支持列表所有操作(append/extend/索引/删除) |
| 层命名 | ✅ 可通过 OrderedDict 给层命名(如 nn.Sequential(OrderedDict([('conv1', nn.Conv2d(...))]))) |
❌ 无命名功能,仅能通过索引访问 |
| 适用场景 | 1. 简单线性网络(如 CNN 基础特征层)<br>2. 无分支/无快捷连接的子模块<br>3. 想简化代码的场景 | 1. 复杂网络(如残差块、多头注意力)<br>2. 动态创建层(如自定义层数的 MLP)<br>3. 需要快捷连接/分支逻辑的场景 |
| 代码简洁度 | 高(无需手动写 forward 遍历) | 稍低(需手动控制层的执行) |
关键共性
- 都能正确注册参数:这是两者最核心的共性,也是区别于普通 Python 列表的关键——存储在其中的层的
weight/bias会被 PyTorch 识别,支持model.parameters()、model.to(device)等操作; - 都继承自 nn.Module:都是合法的子模块,可作为网络的属性直接使用;
- 都支持迭代/索引:都能通过
for layer in container遍历,或container[idx]访问指定层; - 都支持嵌套:比如
nn.Sequential里可以放nn.ModuleList,nn.ModuleList里也可以放nn.Sequential(混合写法很常用)。
选择策略
如果只需要 按顺序执行所有层,无任何分支/跳层 → 用
nn.Sequential(代码更短);如果需要 自定义执行逻辑(快捷连接/分支/条件) 或动态创建层 → 用
nn.ModuleList(灵活性更高);实际开发中可混合使用:用
Sequential封装简单线性子模块,用ModuleList管理需要自定义逻辑的层。
例子
场景1:简单线性网络(优先用 Sequential)
# 用 Sequential 更简洁
class SimpleCNN(nn.Module):
def __init__(self):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 16, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(16, 32, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(2)
)
def forward(self, x):
return self.features(x) # 一行搞定,无需遍历
场景2:带快捷连接的残差块(必须用 ModuleList)
# 用 ModuleList 实现灵活的快捷连接
class ResBlock(nn.Module):
def __init__(self, in_ch, out_ch):
super().__init__()
self.layers = nn.ModuleList([
nn.Conv2d(in_ch, out_ch, 3, 1, 1),
nn.BatchNorm2d(out_ch),
nn.ReLU(),
nn.Conv2d(out_ch, out_ch, 3, 1, 1),
nn.BatchNorm2d(out_ch)
])
self.shortcut = nn.Sequential() # 混合使用更简洁
if in_ch != out_ch:
self.shortcut = nn.Sequential(nn.Conv2d(in_ch, out_ch, 1))
def forward(self, x):
residual = self.shortcut(x)
# 手动控制层的执行,实现快捷连接
out = self.layers[0](x)
out = self.layers[1](out)
out = self.layers[2](out)
out = self.layers[3](out)
out = self.layers[4](out)
out += residual # 快捷连接核心逻辑
return nn.ReLU()(out)
场景3:动态创建层(优先用 ModuleList)
# 根据参数动态生成多层感知机
class DynamicMLP(nn.Module):
def __init__(self, in_dim, out_dim, hidden_dim, num_layers):
super().__init__()
self.layers = nn.ModuleList()
self.layers.append(nn.Linear(in_dim, hidden_dim))
# 动态追加 num_layers-2 个中间层
for _ in range(num_layers - 2):
self.layers.append(nn.Linear(hidden_dim, hidden_dim))
self.layers.append(nn.ReLU())
self.layers.append(nn.Linear(hidden_dim, out_dim))
def forward(self, x):
for layer in self.layers:
x = layer(x)
return x
# 一键创建 3 层/5 层 MLP
mlp_3 = DynamicMLP(10, 2, 32, 3)
mlp_5 = DynamicMLP(10, 2, 32, 5)
总结
- 核心差异:
nn.Sequential是“傻瓜式”顺序执行容器,简洁但不灵活;nn.ModuleList是“手动控制”存储容器,灵活但需手动写执行逻辑; - 核心共性:两者都能正确注册层的参数,区别于普通 Python 列表;
- 选择原则:看是否需要自定义层的执行逻辑——不需要用
Sequential,需要用ModuleList。