如何从 PyTorch 的 ResNet 模型中删除最后一个 FC 层?
How to remove the last FC layer from a ResNet model in PyTorch?
我正在使用 PyTorch 的 ResNet152 模型。我想从模型中剥离最后一个 FC 层。这是我的代码:
from torchvision import datasets, transforms, models
model = models.resnet152(pretrained=True)
print(model)
当我打印模型时,最后几行是这样的:
(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)
)
我想从模型中删除最后一个 fc 层。
我在 SO 上找到了答案(), where mexmex 似乎提供了我正在寻找的答案:
list(model.modules()) # to inspect the modules of your model
my_model = nn.Sequential(*list(model.modules())[:-1]) # strips off last linear layer
所以我将这些行添加到我的代码中,如下所示:
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)
但是这段代码并不像宣传的那样有效——至少对我来说是这样。 post 的其余部分详细解释了为什么该答案不起作用,因此该问题不会作为重复问题而关闭。
首先,打印出来的模型比以前大了将近 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)
)
其次,fc 层仍然存在 -- 之后的 Conv2D 层看起来就像 ResNet152 的第一层。
第三,如果我尝试调用 my_model.forward()
,pytorch 会抱怨大小不匹配。它期望大小 [1, 3, 224, 224],但输入是 [1, 1000]。所以看起来整个模型的副本(减去 fc 层)被附加到原始模型。
最重要的是,我在 SO 上找到的唯一答案实际上并没有用。
对于 ResNet 模型,您可以使用 children 属性访问层,因为 pytorch 中的 ResNet 模型由 nn 个模块组成。 (在 pytorch 0.4.1 上测试)
model = models.resnet152(pretrained=True)
newmodel = torch.nn.Sequential(*(list(model.children())[:-1]))
print(newmodel)
更新:虽然没有一个适用于所有 pytorch 模型的问题的通用答案,但它应该适用于所有结构良好的模型。您添加到模型的现有层(例如 torch.nn.Linear, torch.nn.Conv2d, torch.nn.BatchNorm2d...) all based on torch.nn.Module class. And if you implement a custom layer and add that to your network you should inherit it from pytorch's torch.nn.Module class. As written in documentation,children 属性允许您访问 class/model/network.
的模块
def children(self):
r"""Returns an iterator over immediate children modules.
更新:重要的是要注意 children() returns "immediate" 模块,这意味着如果网络的最后一个模块是顺序的,它将 return 整个顺序.
如果您不仅要剥离最后一个 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
你可以简单地通过:
Model.fc = nn.Sequential()
或者您可以创建身份层:
class Identity(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
return x
并用它替换 fc 层:
Model.fc = Identity()
来自 PyTorch 教程 "Finetuning TorchVision Models":
Here we use Resnet18, as our dataset is small and only has two classes. When we print the model, we see that the last layer is a fully connected layer as shown below:
(fc): Linear(in_features=512, out_features=1000, bias=True)
Thus, we must reinitialize model.fc
to be a Linear layer with 512 input features and 2 output features with:
model.fc = nn.Linear(512, num_classes)
我正在使用 PyTorch 的 ResNet152 模型。我想从模型中剥离最后一个 FC 层。这是我的代码:
from torchvision import datasets, transforms, models
model = models.resnet152(pretrained=True)
print(model)
当我打印模型时,最后几行是这样的:
(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)
)
我想从模型中删除最后一个 fc 层。
我在 SO 上找到了答案(
list(model.modules()) # to inspect the modules of your model
my_model = nn.Sequential(*list(model.modules())[:-1]) # strips off last linear layer
所以我将这些行添加到我的代码中,如下所示:
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)
但是这段代码并不像宣传的那样有效——至少对我来说是这样。 post 的其余部分详细解释了为什么该答案不起作用,因此该问题不会作为重复问题而关闭。
首先,打印出来的模型比以前大了将近 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)
)
其次,fc 层仍然存在 -- 之后的 Conv2D 层看起来就像 ResNet152 的第一层。
第三,如果我尝试调用 my_model.forward()
,pytorch 会抱怨大小不匹配。它期望大小 [1, 3, 224, 224],但输入是 [1, 1000]。所以看起来整个模型的副本(减去 fc 层)被附加到原始模型。
最重要的是,我在 SO 上找到的唯一答案实际上并没有用。
对于 ResNet 模型,您可以使用 children 属性访问层,因为 pytorch 中的 ResNet 模型由 nn 个模块组成。 (在 pytorch 0.4.1 上测试)
model = models.resnet152(pretrained=True)
newmodel = torch.nn.Sequential(*(list(model.children())[:-1]))
print(newmodel)
更新:虽然没有一个适用于所有 pytorch 模型的问题的通用答案,但它应该适用于所有结构良好的模型。您添加到模型的现有层(例如 torch.nn.Linear, torch.nn.Conv2d, torch.nn.BatchNorm2d...) all based on torch.nn.Module class. And if you implement a custom layer and add that to your network you should inherit it from pytorch's torch.nn.Module class. As written in documentation,children 属性允许您访问 class/model/network.
的模块def children(self):
r"""Returns an iterator over immediate children modules.
更新:重要的是要注意 children() returns "immediate" 模块,这意味着如果网络的最后一个模块是顺序的,它将 return 整个顺序.
如果您不仅要剥离最后一个 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
你可以简单地通过:
Model.fc = nn.Sequential()
或者您可以创建身份层:
class Identity(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
return x
并用它替换 fc 层:
Model.fc = Identity()
来自 PyTorch 教程 "Finetuning TorchVision Models":
Here we use Resnet18, as our dataset is small and only has two classes. When we print the model, we see that the last layer is a fully connected layer as shown below:
(fc): Linear(in_features=512, out_features=1000, bias=True)
Thus, we must reinitialize
model.fc
to be a Linear layer with 512 input features and 2 output features with:model.fc = nn.Linear(512, num_classes)