如何从PyTorch的ResNet模型中删除最后一个FC层?

Lee*_*ins 8 python resnet pytorch

我正在使用PyTorch的ResNet152模型。我想从模型中删除最后一个FC层。这是我的代码:

from torchvision import datasets, transforms, models
model = models.resnet152(pretrained=True)
print(model)
Run Code Online (Sandbox Code Playgroud)

当我打印模型时,最后几行如下所示:

    (2):  Bottleneck(
      (conv1):  Conv2d(2048,  512,  kernel_size=(1,  1),  stride=(1,  1),  bias=False)
      (bn1):  BatchNorm2d(512,  eps=1e-05,  momentum=0.1,  affine=True,  track_running_stats=True)
      (conv2):  Conv2d(512,  512,  kernel_size=(3,  3),  stride=(1,  1),  padding=(1,  1),  bias=False)
      (bn2):  BatchNorm2d(512,  eps=1e-05,  momentum=0.1,  affine=True,  track_running_stats=True)
      (conv3):  Conv2d(512,  2048,  kernel_size=(1,  1),  stride=(1,  1),  bias=False)
      (bn3):  BatchNorm2d(2048,  eps=1e-05,  momentum=0.1,  affine=True,  track_running_stats=True)
      (relu):  ReLU(inplace)
    )
  )
  (avgpool):  AvgPool2d(kernel_size=7,  stride=1,  padding=0)
  (fc):  Linear(in_features=2048,  out_features=1000,  bias=True)
)
Run Code Online (Sandbox Code Playgroud)

我想从模型中删除最后一个fc层。

我在SO上找到了一个答案(如何在Pytorch中将预训练的FC层转换为CONV层),其中mexmex似乎提供了我正在寻找的答案:

list(model.modules()) # to inspect the modules of your model
my_model = nn.Sequential(*list(model.modules())[:-1]) # strips off last linear layer
Run Code Online (Sandbox Code Playgroud)

所以我将这些行添加到我的代码中,如下所示:

model = models.resnet152(pretrained=True)
list(model.modules()) # to inspect the modules of your model
my_model = nn.Sequential(*list(model.modules())[:-1]) # strips off last linear layer
print(my_model)
Run Code Online (Sandbox Code Playgroud)

但是此代码无法像宣传的那样工作-至少对我而言不是如此。这篇文章的其余部分详细说明了为什么该答案无效,因此该问题不会重复出现。

首先,打印的模型比以前大了近5倍。我看到的模型与以前相同,但随后似乎是该模型的重复部分,但可能变得扁平了。

    (2): Bottleneck(
      (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace)
    )
  )
  (avgpool): AvgPool2d(kernel_size=7, stride=1, padding=0)
  (fc): Linear(in_features=2048, out_features=1000, bias=True)
)
(1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
(2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(3): ReLU(inplace)
(4): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
(5): Sequential(
  . . . this goes on for ~1600 more lines . . .
  (415): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (416): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  (417): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (418): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
  (419): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (420): ReLU(inplace)
  (421): AvgPool2d(kernel_size=7, stride=1, padding=0)
)
Run Code Online (Sandbox Code Playgroud)

其次,fc层仍然存在 -之后的Conv2D层看起来就像ResNet152的第一层。

第三,如果我尝试调用 my_model.forward(),pytorch会抱怨大小不匹配。预期大小为[1、3、224、224],但输入为[1,1000]。因此,看起来整个模型的副本(减去fc层)正附加到原始模型中。

总之,我在SO上找到的唯一答案实际上是行不通的。

小智 16

你可以简单地通过:

Model.fc = nn.Sequential()

Run Code Online (Sandbox Code Playgroud)

或者,您可以创建身份层:

class Identity(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, x):
        return x
Run Code Online (Sandbox Code Playgroud)

并用它替换 fc 层:

Model.fc = Identity()
Run Code Online (Sandbox Code Playgroud)

  • PyTorch 现在有一个内置的身份模块:https://pytorch.org/docs/stable/nn.html#identity (5认同)
  • 正确的调用方法是 torch.nn.Identity().. 节省打开正确调用方法链接的时间 (2认同)

unl*_*lut 10

对于ResNet模型,由于pytorch中的ResNet模型包含nn个模块,因此可以使用children属性访问层。(在pytorch 0.4.1上测试)

model = models.resnet152(pretrained=True)
newmodel = torch.nn.Sequential(*(list(model.children())[:-1]))
print(newmodel)
Run Code Online (Sandbox Code Playgroud)

更新:尽管没有一个通用的答案可以在所有pytorch模型上使用,但应该在所有结构良好的模型上都可以使用。您添加到模型中的现有图层(例如torch.nn.Lineartorch.nn.Conv2dtorch.nn.BatchNorm2d ...)全部基于torch.nn.Module类。而且,如果您实现自定义层并将其添加到网络,则应该从pytorch的torch.nn.Module类继承它。如文档中所述,children属性使您可以访问类/模型/网络的模块。

def children(self):
        r"""Returns an iterator over immediate children modules.  
Run Code Online (Sandbox Code Playgroud)

更新:必须注意,children()返回“立即”模块,这意味着如果网络的最后一个模块是顺序的,它将返回整个顺序。

  • 要保留图层名称,您可以执行“newmodel = torch.nn.Sequential(OrderedDict([*(list(model.named_children())[:-1])]))”。 (2认同)

Art*_*nov 10

如果您不仅要剥离最后一个 FC 层的模型,还要将其替换为您自己的模型,从而利用迁移学习技术,您可以这样做:

import torch.nn as nn
from collections import OrderedDict

n_inputs = model.fc.in_features

# add more layers as required
classifier = nn.Sequential(OrderedDict([
    ('fc1', nn.Linear(n_inputs, 512))
]))

model.fc = classifier
Run Code Online (Sandbox Code Playgroud)