web-dev-qa-db-ja.com

pytorchフリーズウェイトとparam_groupsの更新

param_groups設定のためにpytorchでウェイトを凍結します。

トレーニング中にウェイトをフリーズしたい場合:

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 alistofdicsが定義されて渡されるSGDは次のとおりです。

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

個々のウェイトを凍結することでこれをどのように達成できますか? DICのリストに対してフィルターを実行しますか、またはオプティマイザーに個別にテンソルを追加する方法はありますか?

7
Benedict K.

実際、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であり、勾配を非アクティブ化すると、Noneではなくp1tensor([[0., 0., 0., 0., 0.]])になります。 。

これは、p1.weight.gradbackward()およびoptimizer.zero_grad()によって変更される単なる変数であるためです。

そのため、最初にp1.weight.gradNoneで初期化されます。勾配がこの変数に書き込まれるか、累積されると、それらは自動的にクリアされません。しかし、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]])

これがあなたにとって理にかなっていることを願っています!

8
blue-phoenox