web-dev-qa-db-fra.com

pytorch geler les poids et mettre à jour les param_groups

Congélation de poids dans pytorch pour param_groups réglage.

Donc, si l'on veut geler des poids pendant l'entraînement:

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

l'optimiseur doit également être mis à jour pour ne pas inclure les poids non dégradés:

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

Si l'on veut utiliser différents weight_decay/taux d'apprentissage pour les biais et les pondérations/cela permet également des taux d'apprentissage différents:

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

param_groups une liste de dics est définie et passée dans SGD comme suit:

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

Comment cela peut-il être obtenu en gelant des poids individuels? Exécuter le filtre sur une liste de photos ou existe-t-il un moyen d'ajouter des tenseurs à l'optimiseur séparément?

7
Benedict K.

En fait, je pense que vous n'avez pas à mettre à jour le optimizer. Les Parameters remis aux optimizer ne sont que des références.

Ainsi, lorsque vous modifiez l'indicateur requires_grad, Il sera immédiatement mis à jour.

Mais même si ce n'est pas le cas pour une raison quelconque - dès que vous définissez le drapeau requires_grad Sur False, vous ne pouvez plus calculer de dégradés ( nouveaux dégradés (voir en bas avec None et zéro dégradés) pour ce poids, donc le gradient ne changera plus et si vous utilisez optimizer.zero_grad() il restera simplement zero.

Donc, s'il n'y a pas de gradient, il n'est pas non plus nécessaire de les exclure du optimizer. Parce que sans gradient, le optimizer ne fera rien, quel que soit le taux d'apprentissage que vous utilisez.

Voici un petit exemple pour montrer ce comportement:

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

Production:

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]])

Ici, vous pouvez voir qu'aucun dégradé n'est calculé. Vous avez peut-être remarqué que le gradient pour p2 Est None au début et plus tard, il est tensor([[0., 0., 0., 0., 0.]]) pour p1 Au lieu de None après avoir désactivé les dégradés.

C'est le cas car p1.weight.grad N'est qu'une variable qui est modifiée par backward() et optimizer.zero_grad().

Donc au début, p1.weight.grad Est juste initialisé avec None, après que les dégradés soient écrits ou accumulés dans cette variable, ils ne seront pas effacés automatiquement. Mais comme optimizer.zero_grad() est appelé, ils sont mis à zéro et restent ainsi puisque backward() ne peut plus calculer de nouveaux gradients avec requires_grad=False.

Vous pouvez également modifier le code de l'instruction if- en:

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

Ainsi, une fois réinitialisés sur None, ils restent inchangés et restent 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]])

J'espère que cela a du sens pour vous!

8
blue-phoenox