Как обновить слои классификации без изменения весов для сверточных слоев

У меня есть CNN с многочисленными сверточными слоями. К каждому из этих сверточных слоев я прикрепил классификатор для проверки выходных данных промежуточных слоев. После того, как потери были произведены для каждого из этих классификаторов, я хочу обновить веса для классификатора, не касаясь весов для сверточных слоев. Этот код:

for i in range(len(loss_per_layer)):
    loss_per_layer[i].backward(retain_graph=True)
    self.classifiers[i].weight.data -= self.learning_rate * self.alpha[i] * self.classifiers[i].weight.grad.data
    self.classifiers[i].bias.data -= self.learning_rate * self.alpha[i] * self.classifiers[i].bias.grad.data

позволяет мне сделать это, если классификатор состоит из единственного nn.Linear слоя. Однако мои классификаторы имеют форму:

self.classifiers.append(nn.Sequential(
    nn.Linear(int(feature_map * input_shape[1] * input_shape[2]), 100),
    nn.ReLU(True),
    nn.Dropout(),
    nn.Linear(100, self.num_classes),
    nn.Sigmoid(),
    ))

Как я могу обновить веса последовательного блока, не касаясь остальной сети? Я недавно перешел с keras на pytorch и поэтому не уверен, как именно использовать функцию optimizer.step () в этой ситуации, но у меня есть подозрение, что это можно сделать с ее помощью.

Обратите внимание, мне нужно общее решение для последовательного блока любой формы, так как оно изменится в будущих итерациях модели.

Любая помощь горячо приветствуется.

См. также:  SwiftUI - Как остановить ScrollView, получающий касания за другим представлением
Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 2
  1. Kroshtan

    Вы можете реализовать свою модель, как показано ниже:

    class Model(nn.Module):
           def __init__(self, conv_layers, classifier):
               super().__init__()
               self.conv_layers = conv_layers
               self.classifier = classifier
    
           def forward(self,x):
               x = self.conv_layers(x)
               return self.classifier(x)
    

    При объявлении оптимизатора передавайте только те параметры, которые вы хотите обновить.

           model = Model(conv_layers, classifier)            
           optimizer = torch.optim.Adam(model.classifier.parameters(), lr=lr)
      
    

    Теперь, когда ты будешь делать

           loss.backward()
           optimizer.step()
           model.zero_grad()
    

    будут обновлены только параметры классификатора.

    РЕДАКТИРОВАТЬ: после комментария OP я добавляю ниже для более общих вариантов использования.

    Более общий сценарий

       class Model(nn.Module):
           def __init__(self, modules):
               super().__init__()
               # supposing you have multiple modules declared like below. 
               # You can also keep them as an array or dict too. 
               # For this see nn.ModuleList or nn.ModuleDict in pytorch 
               self.module0 = modules[0]
               self.module1 = modules[1]
               #..... and so on
    
           def forward(self,x):
               # implement forward 
    
       # model and optimizer declarations
       model = Model(modules)  
       # assuming we want to update module0 and module1          
       optimizer = torch.optim.Adam([
           model.module0.parameters(), 
           model.module1.parameters()
       ], lr=lr)
       # you can also provide different learning rate for different modules. 
       # See [documentation][https://pytorch.org/docs/stable/optim.html]
       
       # when training  
       loss.backward()
       optimizer.step()
       model.zero_grad()
       # use model.zero_grad to remove gradient computed for all the modules.
       # optimizer.zero_grad only removes gradient for parameters that were passed to it. 
    

    Спасибо. Будет ли это работать, если: у меня два сверточных слоя, я присоединяю второй сверточный слой к первому. Затем я прикрепляю классификатор к первому сверточному слою, а классификатор — ко второму сверточному слою. Затем я хочу обновить каждый классификатор с учетом градиента потерь, исходящих от этого классификатора. Думаю, тогда мне нужно сделать отдельный оптимизатор для каждого классификатора, верно? person Kroshtan; 26.01.2021

    Я понимаю, что вы имеете в виду, я обновляю ответ, чтобы отразить более общий сценарий. Надеюсь, это поможет. person Kroshtan; 26.01.2021

    Спасибо, похоже, алгоритмически это помогает. Однако теперь я получаю сообщение об ошибке: одна из переменных, необходимых для вычисления градиента, была изменена операцией на месте. Мне кажется, что я установил inplace = False везде, где это возможно. Может ли это иметь какое-то отношение к обновлению нескольких выходных слоев? person Kroshtan; 28.01.2021

    @Kroshtan Это не должно быть из-за этого … скорее всего. Я вижу, где это могло бы создать проблему, если бы вы могли поделиться кодом? person Kroshtan; 28.01.2021

  2. Kroshtan

    Если вы используете встроенный — или настраиваемый — _1 _, то вам не нужно выполнять обновление параметров вручную. Вы можете просто заморозить слои, которые не хотите обновлять (отключив их флаг requires_grad). Вызов .backward() по поводу вашей потери, тогда optimizer.step() только обновит классификатор.

    В зависимости от архитектуры вашей torch.nn.Module модели вы можете сделать что-то вроде этого:

    for param in model.feature_extractor.parameters():
        param.requires_grad = False
    

    Где model.feature_extractor будет заголовком вашей модели, содержащей сверточные слои (т.е. экстрактор признаков). Таким образом можно зацикливаться на любом модуле, и .parameters() будет перебирать все параметры дочерних параметров этого модуля для деактивации requires_grad.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: