https://discuss.pytorch.org/t/how-to-modify-a-pretrained-model/60509
https://discuss.pytorch.org/t/how-to-modify-the-final-fc-layer-based-on-the-torch-model/766/7
https://greeksharifa.github.io/pytorch/2018/11/10/pytorch-usage-03-How-to-Use-PyTorch/
https://pytorch.org/docs/stable/generated/torch.nn.Module.html
엄청나게 다양한 방법이 존재한다.
이제 바꿔보자.
test_model의 마지막을 바꿔보자.
for name, module in test_model.named_modules(): if name == 'fc.3': module == nn.Linear(in_features=100, out_features=555, bias=True)
이렇게 하면 될줄 알았는데 안된다.
그래서 찾아보다가 model을 보는 데 굳이 앞에서 처럼 for문을 사용할 필요가 없다는 것을 알았다.
다음을 보자.
list(vgg16.named_modules()) list(vgg16.named_children()) list(vgg16.modules()) list(vgg16.children()) list(vgg16.features) 이때 feature는 vgg16안에 있음 주의
즉, 그냥 list로 바로 반환 해준다.
이제 변경 해보자.
이때 크게 두가지의 방법이 있다. 첫번째는 직접 접근하여 특정 layer를 변경(추가,삭제)하는 것이고 두번째는 model을 원하는 만큼 복사하여 다시 새로운 model class로 만드는 것이다.
첫번째 방법
변경
vgg16 = torchvision.models.vgg16(pretrained = True) vgg16 ------------------------------------------------------------------------------ VGG( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): ReLU(inplace=True) (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): ReLU(inplace=True) (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (6): ReLU(inplace=True) (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (8): ReLU(inplace=True) (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (11): ReLU(inplace=True) (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (13): ReLU(inplace=True) (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (15): ReLU(inplace=True) (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (18): ReLU(inplace=True) (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (20): ReLU(inplace=True) (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (22): ReLU(inplace=True) (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (25): ReLU(inplace=True) (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (27): ReLU(inplace=True) (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (29): ReLU(inplace=True) (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=(7, 7)) (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) ) )
이게 진짜 쉽다. 찍어 보니 위와 같이 나왔고 여기서 바로 직접 접근 할 수 있다.
예를들어vgg16.classifier \ type = torch.nn.modules.container.Sequential ------------------------------------------------------------------------------ Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=1000, bias=True) )
이다. 여기서 마지막을 변경하고 싶으면
vgg16.classifier[6] ------------------------------------------------------------------------------ Linear(in_features=4096, out_features=1000, bias=True)
위와 같이 볼수 있으므로
vgg16.classifier[6] = nn.Linear(in_features=4096, out_features=100, bias=True) vgg16.classifier ------------------------------------------------------------------------------ Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (6): Linear(in_features=4096, out_features=100, bias=True) )
이렇게 하면 변경된다. 당연히
vgg16
을 찍어봐도 잘 변경 되었다는 것을 알 수 있다.
삭제
위의 변경된 vgg16을 그대로 사용한다.
마지막의Linear
를 삭제해 보자.del(vgg16.classifier[6])
이렇게 하면 삭제 된다.
추가
vgg16.classifier.add_module('test_add', nn.Linear(in_features=4096, out_features=555, bias=True)) vgg16.add_module('test_add', nn.Linear(in_features=4096, out_features=555, bias=True)) vgg16 ------------------------------------------------------------------------------ VGG( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): ReLU(inplace=True) (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): ReLU(inplace=True) (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (6): ReLU(inplace=True) (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (8): ReLU(inplace=True) (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (11): ReLU(inplace=True) (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (13): ReLU(inplace=True) (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (15): ReLU(inplace=True) (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (18): ReLU(inplace=True) (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (20): ReLU(inplace=True) (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (22): ReLU(inplace=True) (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (25): ReLU(inplace=True) (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (27): ReLU(inplace=True) (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (29): ReLU(inplace=True) (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=(7, 7)) (classifier): Sequential( (0): Linear(in_features=25088, out_features=4096, bias=True) (1): ReLU(inplace=True) (2): Dropout(p=0.5, inplace=False) (3): Linear(in_features=4096, out_features=4096, bias=True) (4): ReLU(inplace=True) (5): Dropout(p=0.5, inplace=False) (test_add): Linear(in_features=4096, out_features=555, bias=True) ) (test_add): Linear(in_features=4096, out_features=555, bias=True) )
model.add_module('name', layer)
하면 잘 되고 앞에model
말고 구성 요소를 추가하여 더 구체적인 위치에 추가 가능하다.
추가인데 이제 새로운 model class를 만든다.
단 이때도 pre-trianed parameter가 그대로 유지된다.
class vgg16__(nn.Module): def __init__(self, originalModel): super(vgg16__, self).__init__() self.original = nn.Sequential(*list(originalModel.features)) self.add = nn.Linear(1000, 8) def forward(self, x): x = self.original(x) x = self.add(x) return x
이렇게 하면 되는데 몇가지의 문제가 있다.
일단 feature말고 전체가 불가능 하다. 또한 module에 하나만 있는 경우도 불가능 하다....
추가 내용
def modify_resnet_for_fsl(model, fc_out_features=5): for name, module in model.named_modules(): if type(module) == torch.nn.BatchNorm2d: module.track_running_stats = False model.fc.out_features = fc_out_features
이렇게 해서 전체적으로 바꿀 수도 있다.