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のリストに対してフィルターを実行しますか、またはオプティマイザーに個別にテンソルを追加する方法はありますか?
実際、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
ではなくp1
のtensor([[0., 0., 0., 0., 0.]])
になります。 。
これは、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]])
これがあなたにとって理にかなっていることを願っています!