【问题标题】:pytorch freeze weights and update param_groupspytorch 冻结权重并更新 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 列表上运行过滤器,或者有没有办法单独将张量添加到优化器?

【问题讨论】:

    标签: python machine-learning computer-vision pytorch


    【解决方案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 值。
    猜你喜欢
    • 2020-01-28
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    • 2019-06-24
    • 2020-12-17
    • 2022-11-01
    • 2018-12-08
    • 2015-07-15
    相关资源
    最近更新 更多