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
dics 的 list 被定义并传递给SGD
,如下所示:
optimizer = torch.optim.Adam(param_groups, args.lr,
betas=(args.momentum, args.beta))
如何通过冻结单个重量来实现这一点?在 dics 列表上运行过滤器,或者有没有办法单独将张量添加到优化器?
【问题讨论】:
【参考方案1】:其实我认为你不必更新optimizer
。交给optimizer
的Parameters
只是参考。
因此,当您更改 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
,后来在停用渐变后,p1
是 tensor([[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_grad
是False
,它们不会添加到图表中。这个标志实际上将它们添加到图表中。这意味着,对于带有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的主要内容,如果未能解决你的问题,请参考以下文章