【问题标题】:How to update classification layers without changing weights for convolutional layers如何在不改变卷积层权重的情况下更新分类层
【发布时间】:2021-04-26 23:04:39
【问题描述】:

我有一个包含许多卷积层的 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(),
    ))

如何在不触及网络其余部分的情况下更新 Sequential 块的权重?我最近从 keras 更改为 pytorch,因此不确定如何在这种情况下准确地使用 optimizer.step() 函数,但我怀疑可以使用它来完成。

请注意,我需要任何形状的序列块的通用解决方案,因为它会在模型​​的未来迭代中发生变化。

非常感谢任何帮助。

【问题讨论】:

    标签: python deep-learning pytorch


    【解决方案1】:

    您可以按如下方式实现您的模型:

    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. 
    

    【讨论】:

    • 谢谢。如果出现以下情况,这仍然有效:我有两个卷积层,我将第二个卷积层附加到第一个。然后我将分类器附加到第一个 conv 层,并将一个分类器附加到第二个 conv 层。然后我想用来自该分类器的损失梯度来更新每个分类器。我想我需要为每个分类器制作一个单独的优化器,对吧?
    • 我明白你的意思,我正在更新答案以反映更通用的情况。希望对您有所帮助。
    • 谢谢,这似乎在算法上起到了作用。但是,我现在收到一条错误消息:梯度计算所需的变量之一已被就地操作修改。我觉得我在任何可能的地方都设置了“inplace=False”。是否与正在更新的多个输出层有关?
    • @Kroshtan 这不应该是因为这个......很可能。如果您可以共享代码,我可以看到可能导致问题的地方?
    【解决方案2】:

    如果您使用的是内置 - 或自定义 - torch.optim.Optimizer,则无需手动执行参数更新。您可以简单地冻结您不想更新的层(通过停用他们的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

    【讨论】:

      猜你喜欢
      • 2017-06-01
      • 2021-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-28
      • 1970-01-01
      相关资源
      最近更新 更多