pytorch 冻结权重并更新 param_groups

Posted

技术标签:

【中文标题】pytorch 冻结权重并更新 param_groups【英文标题】:pytorch freeze weights and update param_groups 【发布时间】:2019-04-09 02:38:06 【问题描述】:

在 pytorch 中为 param_groups 设置冻结权重。

所以如果一个人想在训练期间冻结重量:

for param in child.parameters():
    param.requires_grad = False

还必须更新优化器以不包括非梯度权重:

optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=opt.lr, amsgrad=True)

如果想使用不同的weight_decay/学习率来表示偏差和权重/这也允许不同的学习率:

param_groups = ['params': model.module.bias_parameters(), 'weight_decay': args.bias_decay,
                'params': model.module.weight_parameters(), 'weight_decay': args.weight_decay]

param_groups dicslist 被定义并传递给SGD,如下所示:

optimizer = torch.optim.Adam(param_groups, args.lr,
                                 betas=(args.momentum, args.beta))

如何通过冻结单个重量来实现这一点?在 dics 列表上运行过滤器,或者有没有办法单独将张量添加到优化器?

【问题讨论】:

【参考方案1】:

其实我认为你不必更新optimizer。交给optimizerParameters 只是参考。

因此,当您更改 requires_grad 标志时,它将立即更新。

但即使由于某种原因并非如此 - 一旦您将 requires_grad 标志设置为 False 您就不能再计算任何渐变新渐变 (见底部None 和零梯度)这个权重,所以梯度不会再改变,如果你使用optimizer.zero_grad(),它将保持zero

所以如果没有渐变,那么也没有必要从optimizer 中排除这些。因为没有梯度,optimizer 将什么都不做,无论你使用什么学习率。

这是一个展示这种行为的小例子:

import torch
import torch.nn as nn
import torch.optim as optim

n_dim = 5

p1 = nn.Linear(n_dim, 1)
p2 = nn.Linear(n_dim, 1)

optimizer = optim.Adam(list(p1.parameters())+list(p2.parameters()))
p2.weight.requires_grad = False
for i in range(4):
    dummy_loss = (p1(torch.rand(n_dim)) + p2(torch.rand(n_dim))).squeeze()
    optimizer.zero_grad()
    dummy_loss.backward()
    optimizer.step()
    print('p1: requires_grad =', p1.weight.requires_grad, ', gradient:', p1.weight.grad)
    print('p2: requires_grad =', p2.weight.requires_grad, ', gradient:', p2.weight.grad)
    print()

    if i == 1:
        p1.weight.requires_grad = False
        p2.weight.requires_grad = True

输出:

p1: requires_grad = True , gradient: tensor([[0.8522, 0.0020, 0.1092, 0.8167, 0.2144]])
p2: requires_grad = False , gradient: None

p1: requires_grad = True , gradient: tensor([[0.7635, 0.0652, 0.0902, 0.8549, 0.6273]])
p2: requires_grad = False , gradient: None

p1: requires_grad = False , gradient: tensor([[0., 0., 0., 0., 0.]])
p2: requires_grad = True , gradient: tensor([[0.1343, 0.1323, 0.9590, 0.9937, 0.2270]])

p1: requires_grad = False , gradient: tensor([[0., 0., 0., 0., 0.]])
p2: requires_grad = True , gradient: tensor([[0.0100, 0.0123, 0.8054, 0.9976, 0.6397]])

在这里您可以看到没有计算梯度。您可能已经注意到 p2 的渐变在开始时是 None,后来在停用渐变后,p1tensor([[0., 0., 0., 0., 0.]]) 而不是 None

之所以如此,是因为p1.weight.grad 只是一个被backward()optimizer.zero_grad() 修改的变量。

所以一开始p1.weight.grad 只是用None 初始化,在梯度写入或累积到这个变量之后,它们不会被自动清除。但是因为optimizer.zero_grad() 被调用,所以它们被设置为零并保持这样,因为backward() 不能再用requires_grad=False 计算新的梯度。

您还可以将if-statement 中的代码更改为:

if i == 1:
    p1.weight.requires_grad = False
    p1.weight.grad = None
    p2.weight.requires_grad = True

因此,一旦重置为None,它们将保持不变并保持None

p1: requires_grad = True , gradient: tensor([[0.2375, 0.7528, 0.1501, 0.3516, 0.3470]])
p2: requires_grad = False , gradient: None

p1: requires_grad = True , gradient: tensor([[0.5181, 0.5178, 0.6590, 0.6950, 0.2743]])
p2: requires_grad = False , gradient: None

p1: requires_grad = False , gradient: None
p2: requires_grad = True , gradient: tensor([[0.4797, 0.7203, 0.2284, 0.9045, 0.6671]])

p1: requires_grad = False , gradient: None
p2: requires_grad = True , gradient: tensor([[0.8344, 0.1245, 0.0295, 0.2968, 0.8816]])

我希望这对你有意义!

【讨论】:

有道理,尽管在计算图中使用张量但不优化它们是否会产生额外的开销? @BenedictK。如果requires_gradFalse,它们不会添加到图表中。这个标志实际上将它们添加到图表中。这意味着,对于带有requires_grad=True 的张量,autograd 会跟踪已完成的计算等。这些信息保存在缓冲区中。但是对于requires_grad=False,这些信息不会添加到图表中。 Quote:“在冻结的基础中切换requires_grad标志就足够了,不会保存任何中间缓冲区” - 这就是这个标志的实际用途,你可以看看这里:@ 987654321@ above linked site of the PyTorch-Documentation 中的这句话更清楚地表明:“每个张量都有一个标志:requires_grad,它允许从梯度计算中细粒度地排除子图,并可以提高效率。 " 我想到的另一件事可能会更好地解释它:优化器本身实际上与graph无关。它只是使用已经计算好的 graph 来改变权重。所以无论你在 optimizer 中放什么,都不会改变 graph。但是您可以通过相应地设置requires_grad 标志来控制图表,以在图表中包含或排除某些计算。 @blue-phoenox 因此,如果使用requires_grad=False 设置了一个中间实体,它将计算一个临时的 grad 值,而实际上并没有强它。这将解释前面的层如何设法获得grad 值。

以上是关于pytorch 冻结权重并更新 param_groups的主要内容,如果未能解决你的问题,请参考以下文章

[深度学习][pytorch]pytorch实现一个简单得线性回归模型并训练

如何正确更新 PyTorch 中的权重?

Pytorch冻结网络部分参数

Yolov5 冻结网络层进行迁移学习

Pytorch冻结部分层的参数

pytorch 在向后()中崩溃并冻结屏幕,你能帮帮我吗?