Как удалить последний слой FC из модели ResNet в PyTorch?
Я использую модель ResNet152 от PyTorch. Я хотел бы снять последний слой 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)
)
Я хочу удалить этот последний слой с моделью.
Я нашел ответ здесь на SO ( Как преобразовать предварительно обученные слои FC в слои CONV в Pytorch), где 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)
Но этот код не работает как рекламируется - как минимум, не для меня. Остальная часть этого поста является подробным объяснением того, почему этот ответ не работает, поэтому этот вопрос не будет закрыт как дубликат.
Во-первых, напечатанная модель почти в 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()
Пыторч жалуется на несоответствие размеров. Ожидается размер [1, 3, 224, 224], но вход был [1, 1000]. Таким образом, похоже, что копия всей модели (за исключением слоя fc) добавляется к исходной модели.
Итог, единственный ответ, который я нашел на SO, на самом деле не работает.
4 ответа
Для модели ResNet вы можете использовать дочерний атрибут для доступа к слоям, поскольку модель ResNet в pytorch состоит из 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...), все основаны на классе torch.nn.Module. А если вы реализуете собственный слой и добавляете его в свою сеть, вы должны наследовать его от класса torch.nn.Module в pytorch. Как написано в документации, атрибут children позволяет получить доступ к модулям вашего класса / модели / сети.
def children(self):
r"""Returns an iterator over immediate children modules.
Обновление: важно отметить, что children() возвращает "немедленные" модули, что означает, что если последний модуль вашей сети является последовательным, он вернет весь последовательный.
Вы можете сделать это просто:
Model.fc = nn.Sequential()
или же вы можете создать слой Identity:
class Identity(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
return x
и замените слой fc следующим:
Model.fc = Identity()
Если вы ищете не просто раздеть модель последнего слоя 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
Из учебника PyTorch «Тонкая настройка моделей TorchVision» :
Здесь мы используем Resnet18, так как наш набор данных небольшой и состоит всего из двух классов. Когда мы печатаем модель, мы видим, что последний слой является полностью связанным слоем, как показано ниже:
(fc): Linear(in_features=512, out_features=1000, bias=True)
Таким образом, мы должны повторно инициализировать
model.fc
быть линейным слоем с 512 входными функциями и 2 выходными функциями с:model.fc = nn.Linear(512, num_classes)