LoginSignup
0
0

More than 1 year has passed since last update.

Adamで勾配を伝えていないはずなのにパラメータ更新がされる場合

Posted at

はじめに

Adamを使ったニューラルネットワークの訓練をしていた際に,ぶつかった問題とその原因についてせつめいする.

事例

ざっくり書くと下記のような二又構造のモデルを使っていた.場面によって2つの経路のどちらかのモデルを選択して使うといった感じ.このとき,片方のモデルを使わずに誤差逆伝播をしてパラメータ更新を行ったにも関わらず,使っていない方のパタメータも更新されているという事象に遭遇した.

Class Model(nn.Module):
    def __init__(self):
        self.fc = Linear()
        self.fc1 = Linear()
        self.fc2 = Linear()
    def forward(self, x, right=False):
        h = self.fc(x)
        if right:
            return self.fc1(h)
        else:
            return self.fc2(h)

原因・解決方法

Adam内に含まれているMomentumが原因だった.Adamの仕様上,直近で行ったパラメータ更新時の勾配が保存されているため,たとえ勾配を逆伝播させておらず勾配が0であっても,残っているMomentumの項のみを使ってパラメータの更新が行われてしまう.そのため,使っていないモデルのパラメータも変化する.SGDなどのMomentum項を持たないoptimizerを使うと,上記の問題が発生しない.どうしてもAdamを使いたい場合はモデルの構造をいじる必要がある.

簡単な再現例

import torch
import torch.nn as nn
import torch.optim as optim


class Model(nn.Module):
	def __init__(self):
		super().__init__()
		self.fc = nn.Linear(1, 1)

	def forward(self, x):
		return self.fc(x)


model = Model()
optimizer = optim.Adam(model.parameters())

x = torch.tensor([2.])
y = torch.tensor([0.])

p = model(x)
loss = nn.functional.mse_loss(p, y)
print(model.fc.weight) # tensor([[0.5239]], requires_grad=True)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(model.fc.weight) # tensor([[0.5229]], requires_grad=True)

optimizer.zero_grad()
optimizer.step()
print(model.fc.weight) # tensor([[0.5222]], requires_grad=True)

実際に出力される値は異なるかもしれないが,この例でもbackwardしていないのにも関わらず,パラメータの値が変化する.

おわりに

細かい仕様を意識して書かないと面倒くさいことになりますね.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0