web-dev-qa-db-fra.com

Comment supprimer la dernière couche FC d'un modèle ResNet dans PyTorch?

J'utilise un modèle ResNet152 de PyTorch. J'aimerais retirer la dernière couche FC du modèle. Voici mon code:

from torchvision import datasets, transforms, models
model = models.resnet152(pretrained=True)
print(model)

Lorsque j'imprime le modèle, les dernières lignes ressemblent à ceci:

    (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)
)

Je souhaite supprimer cette dernière couche fc du modèle.

J'ai trouvé une réponse ici sur SO ( Comment convertir des couches FC pré-entraînées en couches CONV dans Pytorch ), où mexmex semble fournir la réponse que je recherche:

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

J'ai donc ajouté ces lignes à mon code comme ceci:

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)

Mais ce code ne fonctionne pas comme annoncé - du moins pas pour moi. Le reste de cet article explique en détail pourquoi cette réponse ne fonctionne pas, de sorte que cette question ne soit pas fermée en double.

Premièrement, le modèle imprimé est presque 5 fois plus grand qu'avant. Je vois le même modèle que précédemment, suivi de ce qui semble être une répétition du modèle, mais peut-être aplati.

    (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)
)

Deuxièmement, la couche fc est still là - et la couche Conv2D après elle ressemble à la première couche de ResNet152. 

Troisièmement, si j'essaie d'invoquer my_model.forward(), pytorch se plaint d'une incompatibilité de taille. Il attend la taille [1, 3, 224, 224], mais l’entrée était [1, 1000]. Il semble donc qu'une copie de l'ensemble du modèle (moins la couche fc) soit ajoutée au modèle d'origine.

En bout de ligne, la seule réponse que j'ai trouvée sur SO ne fonctionne pas réellement.

3
Lee Jenkins

Pour le modèle ResNet, vous pouvez utiliser l'attribut children pour accéder aux couches car le modèle ResNet dans pytorch est constitué de nn modules. (Testé sur pytorch 0.4.1)

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

Mise à jour: Bien qu'il n'y ait pas de réponse universelle à la question qui puisse fonctionner sur tous les modèles de pytorch, elle devrait fonctionner sur tous les modèles bien structurés. Les couches existantes que vous ajoutez à votre modèle (telles que torch.nn.Linear , torch.nn.Conv2d , torch.nn.BatchNorm2d ...), toutes basées sur torch. nn.Module classe . Et si vous implémentez une couche personnalisée et l'ajoutez à votre réseau, vous devez l'hériter de la classe torch.nn.Module de pytorch. Comme indiqué dans documentation , l'attribut children vous permet d'accéder aux modules de votre classe/modèle/réseau.

def children(self):
        r"""Returns an iterator over immediate children modules.  

Mise à jour: il est important de noter que children () renvoie les modules "immédiats", ce qui signifie que si le dernier module de votre réseau est séquentiel, il retournera tout le séquentiel.

3
unlut

Si vous souhaitez non seulement supprimer le modèle de la dernière couche FC, mais le remplacer par le vôtre, tirant ainsi parti de la technique d'apprentissage par transfert, procédez comme suit:

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
0
Artem Trunov