两个具有相同架构的模型,但一个有算术错误

青葱年少 pytorch 230

原文标题Two models with same architecture, but one has an arithmetic error

我正在尝试将权重从 NNCLR 的 Resnet18 主干加载到线性分类器上。问题是我得到一个矩阵乘法错误。在下面的代码中,model指的是整个 NNCLR 模型。其中大部分基于 Lightly 文档。

pt_backbone = model.backbone

state_dict = {'resnet18_parameters': pt_backbone.state_dict()}
torch.save(state_dict, 'test_nnclr.h5')

resnet18_new = torchvision.models.resnet18()

backbone_new = nn.Sequential(*list(resnet18_new.children())[:-1])

ckpt = torch.load('test_nnclr.h5')
backbone_new.load_state_dict(ckpt['resnet18_parameters'])
backbone_new.add_module('fc', nn.Linear(512, 2, device=cuda0))
backbone_new(torch.tensor(np.random.uniform(-10, 10, (8, 3, 128, 128)), device=cuda0).float())

我收到以下运行时错误:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-51-ac885f772507> in <module>()
      1 backbone_new.add_module('fc', nn.Linear(512, 2, device=cuda0))
----> 2 backbone_new(torch.tensor(np.random.uniform(-10, 10, (8, 3, 128, 128)), device=cuda0).float())

4 frames
/usr/local/lib/python3.7/dist-packages/torch/nn/functional.py in linear(input, weight, bias)
   1846     if has_torch_function_variadic(input, weight, bias):
   1847         return handle_torch_function(linear, (input, weight, bias), input, weight, bias=bias)
-> 1848     return torch._C._nn.linear(input, weight, bias)
   1849 
   1850 

RuntimeError: mat1 and mat2 shapes cannot be multiplied (4096x1 and 512x2)

我知道4096来自512 x 8,其中8是批量大小,512是线性层之前的最后一个维度输出。但我很困惑,因为我不知道如何在新的线性层中解释批量大小。由于以下结果,我特别困惑:

resnet18_new(torch.tensor(np.random.uniform(-10, 10, (8, 3, 128, 128)), device=cuda0).float()).shape

这似乎完美地工作,导致torch.Size([8, 2])。但是这两个模型具有相同的架构,所以我不明白一个有错误而另一个没有。两个模型之间的区别在于backbone_new(顺便说一句,它实际上不是主干)具有不同的权重。如何修复此错误?

原文链接:https://stackoverflow.com//questions/71889578/two-models-with-same-architecture-but-one-has-an-arithmetic-error

回复

我来回复
  • draw的头像
    draw 评论

    考虑方法forwardofclass ResNet的代码。它调用像self.reluorself.layer2这样的属性,但在完全连接之前它需要torch.flatten(x, 1)。要了解为什么它很重要,请查看为 forward ofresnet18_new 生成的代码:

    from torch.fx import symbolic_trace
    
    symbolic_traced = symbolic_trace(resnet18_test)
    
    print(symbolic_traced.code)
    

    stdout 中的最后几行类似于:

    avgpool = self.avgpool(layer4_1_relu_1);  layer4_1_relu_1 = None
    flatten = torch.flatten(avgpool, 1);  avgpool = None
    fc = self.fc(flatten);  flatten = None
    return fc
    

    但是对于backbone_new的类似过程返回不同的结果,之后:

    symbolic_traced_new = symbolic_trace(backbone_new)
    
    print(symbolic_traced_new.code)
    

    stdout 中的最后几行看起来像(记住它是生成的,所以它不漂亮也就不足为奇了):

    _8 = getattr(self, "8")(_7_1_relu_1);  _7_1_relu_1 = None
    fc = self.fc(_8);  _8 = None
    return fc
    

    其中getattr(self, "8")对应AdaptiveAvgPool2d(output_size=(1, 1))。当然会崩溃——flatten被扔掉了!而这一切都是因为用法torch.flatten原作ResNet._forward_impl。所以要修复,当添加一个新模块时,把nn.Flatten也放在那里(行为类似于torch.flatten(x, 1)):

    backbone_new = nn.Sequential(*list(resnet18_new.children())[:-1])
    ckpt = torch.load('test_nnclr.h5')
    backbone_new.load_state_dict(ckpt['resnet18_parameters'])
    backbone_new.add_module('fc', nn.Sequential(nn.Flatten(1), nn.Linear(512, 2, device='cuda:0')))
    
    2年前 0条评论