直接用普通 list 存子模块会报错,因 PyTorch 不自动注册其参数和 forward 方法;须改用 nn.ModuleList 或 nn.ModuleDict 才能被 parameters()、to(device) 等识别并参与训练。

为什么直接用 list 存子模块会报错 AttributeError: 'list' object has no attribute 'forward'
PyTorch 的 nn.Module 不会自动递归注册普通 Python list 或 dict 里的子模块。你往 self.layers = [nn.Linear(10, 5), nn.ReLU()] 里塞东西,模型参数不会被 model.parameters() 捕获,to(device) 也不会把它们搬到 GPU,训练时自然找不到 forward —— 因为 PyTorch 根本没把它们当“模块”看。
解决办法不是手动遍历注册,而是改用 PyTorch 提供的容器类:
-
nn.ModuleList替代普通list:支持索引、切片、append、ehttps://www.php.cn/link/bc72fcdec4667b67109692744061556ftend,且自动注册 -
nn.ModuleDict替代普通dict:键必须是字符串,值必须是nn.Module,同样自动注册 - 二者都继承自
nn.Module,所以能嵌套、能参与train()/eval()切换
ModuleList 的正确初始化时机和常见误用
必须在 __init__ 中完成初始化并赋值给实例属性(如 self.blocks),不能在 forward 里动态创建或修改——那只是临时对象,不进参数图。
典型错误写法:
立即学习“Python免费学习笔记(深入)”;
def forward(self, https://www.php.cn/link/bc72fcdec4667b67109692744061556f):
layers = nn.ModuleList([nn.Linear(10, 5), nn.ReLU()]) # ❌ 错!每次 forward 都新建,不注册、无参数
return layers[1](layers[0](https://www.php.cn/link/bc72fcdec4667b67109692744061556f))
正确写法:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
class MyNet(nn.Module):
def __init__(self):
super().__init__()
self.blocks = nn.ModuleList([
nn.Linear(10, 20),
nn.ReLU(),
nn.Linear(20, 1)
])
<pre class="brush:php;toolbar:false;">def forward(self, https://www.php.cn/link/bc72fcdec4667b67109692744061556f):
for layer in self.blocks:
https://www.php.cn/link/bc72fcdec4667b67109692744061556f = layer(https://www.php.cn/link/bc72fcdec4667b67109692744061556f)
return https://www.php.cn/link/bc72fcdec4667b67109692744061556f
- 不能用
+=或+拼接两个ModuleList(会返回普通list);要用.ehttps://www.php.cn/link/bc72fcdec4667b67109692744061556ftend() - 支持
for i, m in enumerate(module_list):,但不支持字典式遍历(如.items()) - 如果模块顺序不固定、需按名调用(比如多分支结构),优先考虑
ModuleDict
ModuleDict 在条件分支/多头结构中的实际用法
当你需要根据字符串 key 动态选择子模块(例如不同任务头、不同分辨率路径、不同 loss 分支),ModuleDict 比一堆 if/elif 更清晰也更易扩展。
示例:一个多任务分类器,支持 "age" 和 "gender" 两个 head:
self.heads = nn.ModuleDict({
"age": nn.Sequential(nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 1)),
"gender": nn.Sequential(nn.Linear(128, 32), nn.ReLU(), nn.Linear(32, 2))
})
<p>def forward(self, https://www.php.cn/link/bc72fcdec4667b67109692744061556f, task="age"):
return self.heads<a href="https://www.php.cn/link/bc72fcdec4667b67109692744061556f">task</a> # ✅ 自动找到对应模块并执行
- key 必须是合法标识符(不能含空格、破折号等),否则初始化时报
TypeError -
self.heads.keys()返回的是torch.nn.modules.container.ModuleDictKeys,不是普通list,要转成list(self.heads.keys())才能迭代 - 不能用
.update()增量添加(会静默失败);新增必须显式赋值,如self.heads["ethnicity"] = ...
混合使用 ModuleList 和 ModuleDict 时的参数同步陷阱
当网络中既有顺序链式结构(用 ModuleList),又有命名分支结构(用 ModuleDict),容易忽略一个细节:二者虽都注册了参数,但 state_dict() 的键名生成规则不同。
-
ModuleList中第i个模块的参数前缀是blocks.<i>.</i>(如blocks.0.weight) -
ModuleDict中模块参数前缀是heads.<key>.</key>(如heads.age.0.weight) - 如果你手动构造
state_dict加载权重,或做迁移学习时冻结部分分支,必须严格匹配这些路径格式,否则load_state_dict(..., strict=False)也可能漏掉某些层
调试建议:打印 dict(model.named_parameters()).keys(),确认所有期望的参数都在里面,且命名符合预期。尤其注意嵌套层级深时,ModuleList 内部的 Sequential 会进一步展开路径,别只查一级。

















