You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

由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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 08:35:05