由getattr引发的CNN模型权重同步异常问题排查
问题:多CNN模型特征提取层权重同步的矛盾错误分析
我同时训练8个结构完全相同的CNN模型,每完成一个批次训练后,需要将其中一个模型的特征提取层权重同步给另外7个。但在实现同步逻辑时遇到了矛盾的错误,具体情况如下:
模型定义
import torch import torch.nn as nn from torchvision import models class LogTanh(nn.Module): def forward(self, x): return torch.log(torch.tanh(x) + 1) # 补充LogTanh的简单实现,原代码未给出 class GNet(nn.Module): def __init__(self, dim_output, dropout=0.5): super(GNet, self).__init__() self.out_dim = dim_output # Load the pretrained AlexNet model alexnet = models.alexnet(pretrained=True) self.pre_filtering = nn.Sequential( alexnet.features[:4] ) # Set requires_grad to False for all parameters in the pre_filtering network for param in self.pre_filtering.parameters(): param.requires_grad = False # construct the feature extractor # every intermediate feature will be fed to the feature extractor # res: 25 x 25 self.feat_ex1 = nn.Conv2d(192, 128, kernel_size=3, stride=1) # res: 25 x 25 self.feat_ex2 = nn.Sequential( nn.BatchNorm2d(128), nn.Dropout(p=dropout), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1) ) # res: 25 x 25 self.feat_ex3 = nn.Sequential( nn.BatchNorm2d(128), nn.Dropout(p=dropout), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1) ) # res: 13 x 13 self.feat_ex4 = nn.Sequential( nn.MaxPool2d(kernel_size=3, stride=2, padding=1), nn.BatchNorm2d(128), nn.Dropout(p=dropout), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1) ) # res: 13 x 13 self.feat_ex5 = nn.Sequential( nn.BatchNorm2d(128), nn.Dropout(p=dropout), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1) ) # res: 13 x 13 self.feat_ex6 = nn.Sequential( nn.BatchNorm2d(128), nn.Dropout(p=dropout), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1) ) # res: 13 x 13 self.feat_ex7 = nn.Sequential( nn.BatchNorm2d(128), nn.Dropout(p=dropout), nn.Conv2d(128, 64, kernel_size=3, stride=1, padding=1) ) # define the flexible pooling field of each layer # use a full convolution layer here to perform flexible pooling self.fpf13 = nn.Conv2d(in_channels=448, out_channels=448, kernel_size=13, groups=448) self.fpf25 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=25, groups=384) self.linears = nn.ModuleDict() # 原代码用普通字典,改为ModuleDict让PyTorch识别参数 for i in range(self.out_dim): self.linears[f'linear_{i+1}'] = nn.Linear(832, 1) self.LogTanh = LogTanh() self.flatten = nn.Flatten()
初始同步函数与错误
初始实现的权重同步函数:
def sync_weights(models, current_sub, sync_seqs): for sub in range(1, 9): if sub != current_sub: # Synchronize the specified layers with torch.no_grad(): for seq_name in sync_seqs: reference_layer = getattr(models[current_sub], seq_name)[2] layer = getattr(models[sub], seq_name)[2] layer.weight.data = reference_layer.weight.data.clone() if layer.bias is not None: layer.bias.data = reference_layer.bias.data.clone()
执行时触发错误:
'Conv2d' object is not iterable
修改后的同步函数与错误
移除[2]索引后的同步函数:
def sync_weights(models, current_sub, sync_seqs): for sub in range(1, 9): if sub != current_sub: # Synchronize the specified layers with torch.no_grad(): for seq_name in sync_seqs: reference_layer = getattr(models[current_sub], seq_name) layer = getattr(models[sub], seq_name) layer.weight.data = reference_layer.weight.data.clone() if layer.bias is not None: layer.bias.data = reference_layer.bias.data.clone()
触发错误:
'Sequential' object has no attribute 'weight'
传入的sync_seqs参数
sync_seqs = [ 'feat_ex1', 'feat_ex2', 'feat_ex3', 'feat_ex4', 'feat_ex5', 'feat_ex6', 'feat_ex7' ]
错误原因分析
两个错误并不矛盾,核心问题是sync_seqs中的成员类型不一致:
feat_ex1是直接定义的Conv2d对象,并非Sequential容器,所以用[2]索引时会触发“不可迭代”错误;feat_ex2到feat_ex7都是Sequential容器,容器本身没有weight属性,只有内部的子层(比如最后一层的Conv2d)才有,直接访问容器的weight会报错。
解决方法
针对不同类型的层做区分处理,同步时先判断当前模块的类型:
def sync_weights(models, current_sub, sync_seqs): for sub in range(1, 9): if sub != current_sub: with torch.no_grad(): for seq_name in sync_seqs: ref_module = getattr(models[current_sub], seq_name) target_module = getattr(models[sub], seq_name) # 处理直接的Conv2d层 if isinstance(ref_module, nn.Conv2d): target_module.weight.data.copy_(ref_module.weight.data) if target_module.bias is not None: target_module.bias.data.copy_(ref_module.bias.data) # 处理Sequential容器,同步内部的Conv2d层(根据定义取最后一层) elif isinstance(ref_module, nn.Sequential): ref_conv = ref_module[-1] target_conv = target_module[-1] target_conv.weight.data.copy_(ref_conv.weight.data) if target_conv.bias is not None: target_conv.bias.data.copy_(ref_conv.bias.data)
内容的提问来源于stack exchange,提问作者JohnnyWang97
相关产品推荐
相关产品推荐

