pytorch замораживает веса и обновляет param_groups - PullRequest
0 голосов
/ 05 ноября 2018

Вес замораживания в 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 a список из dics определяется и передается в SGD следующим образом:

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

Как этого добиться, замораживая отдельные веса ? Запуск фильтра по списку диков или есть способ добавить тензоры в оптимизатор отдельно?

1 Ответ

0 голосов
/ 06 ноября 2018

На самом деле я думаю, что вам не нужно обновлять optimizer. Parameters, переданный optimizer - это просто ссылки.

Поэтому, когда вы измените флаг 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, а позже он будет tensor([[0., 0., 0., 0., 0.]]) для p1 вместо None после деактивации градиентов.

Это так, потому что p1.weight.grad - это просто переменная, которая изменяется на backward() и optimizer.zero_grad().

Таким образом, в начале p1.weight.grad просто инициализируется с None, после того как градиенты записаны или накоплены в эту переменную, они не будут очищаться автоматически. Но поскольку вызывается optimizer.zero_grad(), они устанавливаются в ноль и остаются такими, поскольку backward() больше не может вычислять новые градиенты с requires_grad=False.

Вы также можете изменить код в if заявлении на:

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

Надеюсь, это имеет смысл для вас!

...