目次
1. 目的
2. AUTOGRADとは
3. AUTOGRADを体験
1. 目的
PyTorchのチュートリアルAutograd: Automatic Differentiationを参考にPyTorchでAUTOGRADの扱いになれること。
2. AUTOGRADとは
autogradパッケージは、PyTorchのニューラルネットワークの要です。
autogradパッケージによって、テンソル使ったあらゆる計算を自動的に微分することができます。
これはいわゆる、define-by-runフレームワーク(動的グラフ)というものに分類されます。
静的グラフと動的グラフの説明についてはこちらをご覧ください。
簡潔に言うと、入力するデータによってニューラルネットワーク構造を変化させることだそうです。
一方で、TensorFlowやTheanoといったフレームワークは、静的グラフであり計算グラフを事前に決めてから実行します。
PyTorchは、ニューラルネットワーク構造を定義する時点で固定する必要ななく、入力されたデータに対して例えば活性化関数を変えてネットワーク構造を変化させるため、柔軟な学習や推測が可能となります。
3. AUTOGRADを体験
ここらの話は難しいので手を動かしてAUTOGRADを感じてみます。
まずは、torchをimportします。
import torch
次に、テンソルを作りますがこのとき、requires_grad=True
にしておきます。requires_grad=True
にすることでこれからのテンソル計算をトレース(計算過程を記録)することができます。
x = torch.ones(2, 2, requires_grad=True) print(x)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
テンソルの足し算をしてみます。
print(y)
tensor([[3., 3.],
[3., 3.]], grad_fn=)
テンソル計算をした結果、yにgrad_fnができたことがわかります。
print(y.grad_fn)
>
さらに、yを使って計算していきます。
zはyの掛け算(multiply)で算出されたのでgrad_fn
はMulBackward0
。
outはzの平均(mean)で算出されたのでgrad_fn
はMeanBackward0
となっている。
z = y * y * 3 out = z.mean() print(z, out)
tensor([[27., 27.],
[27., 27.]], grad_fn=) tensor(27., grad_fn=)
requires_grad
のデフォルトはFalseですが、テンソルを定義するときにrequires_grad=True
をしていなくても途中からTrueに変えてテンソル計算過程を記録することができます。
こちらは、.requires_grad_()
で変更することができます。
a = torch.randn(2,2) a = ((a * 3) / (a - 1)) print(a.requires_grad)
False
a.requires_grad_(True)
tensor([[ 1.5285, 1.8603],
[ 2.0531, -1.0013]], requires_grad=True)
print(a.requires_grad)
True
b = (a * a).sum() print(b.grad_fn)
>
次に逆伝播してみます。
今、テンソルoutには一つのスカラーが格納されているのでout.backward()
は、out.backward(torch.tensor(1.))
と同じになります。
.backward()
で微分対象を設定して、なにで微分するかを.grad
で指定します。
ここでは、テンソルoutをxで微分(d(out)/dx)してみます。
print(out)
tensor(27., grad_fn=)
out.backward() print(x)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
実際に、手計算して考えてみる。
ここでテンソルoutをo
とすると
torch.autograd
の役割はベクトルーヤコビアンの積を計算することです。このベクトルーヤコビアンの積によって、スカラーを出力しないモデルに外部勾配を組み込みやすくなります。
ここで、ベクトルーヤコビアンの積の例を見てみます。
y.data.norm()
はy
のL2ノルム(ユークリッドノルム)を返します。
以下の例では、y
のL2ノルムが1000を超えるまでy
を2倍し続けています。
x = torch.randn(3, requires_grad=True) y = x * 2 while y.data.norm() < 1000: y = y * 2 print(y)
tensor([-0.6997, -0.0351, -5.1186], grad_fn=)
ここでyはスカラーではないので、torch.autograd
ではヤコビアンを直接的に計算することができません。
ですが、単にベクトルーヤコビアンの積の結果だけが欲しいので単純に.backward()
メソッドを使ってやります。
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float) y.backward(v) print(x.grad)
tensor([2.0000e-01, 2.0000e+00, 2.0000e-04])
autogradを止めるには、with torch.no_grad():
でテンソルをくくって.requires_grad
をFalseにします。
print(x.requires_grad)
True
print((x ** 2).requires_grad)
True
with torch.no_grad(): print((x ** 2).requires_grad)
False
あるいは、.detach()
を使うことで、同じテンソルを新たに作ることができます。
ですが、require_grad
はFalse
になります。
x.eq(y)
は、各テンソルの各要素が同じ(equal)であるかをbool型で返します。
.all
を最後に付け加えることで、すべての要素が同じであるかどうかをbool型で返します。
print(x.requires_grad)
True
y = x.detach() print(y.requires_grad)
False
print(x.eq(y).all())
tensor(True)