【初心者向け】基礎&実践プログラミング

初心者がつまづきやすいところ、最短で実力が身につく方法をお伝えします。

【PyTorch】チュートリアル(日本語版 )② 〜AUTOGRAD〜

f:id:AIProgrammer:20200413020244p:plain

目的

PyTorchのチュートリアルAutograd: Automatic Differentiationを参考にPyTorchでAUTOGRADの扱いになれること。

前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

コマンドラインの「>>>」の行がPythonで実行するコマンドです。 それ以外の行は、コマンドの実行結果です。

これまで、勉強してきた内容は以下。

AUTOGRADとは

autogradパッケージは、PyTorchのニューラルネットワークの要です。

autogradパッケージによって、テンソル使ったあらゆる計算を自動的に微分することができます。 これはいわゆる、define-by-runフレームワーク(動的グラフ)というものに分類されます。 静的グラフと動的グラフの説明についてはこちらをご覧ください。 簡潔に言うと、入力するデータによってニューラルネットワーク構造を変化させることだそうです。 一方で、TensorFlowやTheanoといったフレームワークは、静的グラフであり計算グラフを事前に決めてから実行します。 PyTorchは、ニューラルネットワーク構造を定義する時点で固定する必要ななく、入力されたデータに対して例えば活性化関数を変えてネットワーク構造を変化させるため、柔軟な学習や推測が可能となります。

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=<AddBackward0>)

テンソル計算をした結果、yにgrad_fnができたことがわかります。

>>> print(y.grad_fn)
<AddBackward0 object at 0x7fa2fc4b3470>

さらに、yを使って計算していきます。 zはyの掛け算(multiply)で算出されたのでgrad_fnMulBackward0。 outはzの平均(mean)で算出されたのでgrad_fnMeanBackward0となっている。

>>> z = y * y * 3
>>> out = z.mean()
>>> 
>>> print(z, out)
tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)

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)
<SumBackward0 object at 0x7fa2fc4b3470>

次に逆伝播してみます。

今、テンソルoutには一つのスカラーが格納されているのでout.backward()は、out.backward(torch.tensor(1.))と同じになります。 .backward()で微分対象を設定して、なにで微分するかを.gradで指定します。

ここでは、テンソルoutをxで微分(d(out)/dx)してみます。

>>> print(out)
tensor(27., grad_fn=<MeanBackward0>)
>>> 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とすると


z_{i} = 3  *  y_{i}  *  y_{i} \\
y_{i} = x_{i} + 2 より \\
o = \frac{1}{4} \sum_{i} z_{i} \\
=  \frac{1}{4} \sum_{i} 3 * (x_{i} + 2)^2 \\
\frac{\partial o}{\partial x_{i}} = \frac{3}{2}(x_{i} + 2) \\
\frac{\partial o}{\partial x_{i}}|_{x_{i}=1} = \frac{9}{2} = 4.5

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=<MulBackward0>)

ここで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_gradFalseになります。

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)



頑張れ!喝!!の代わりにB!ブックマークを押していただけるとただただうれしいです(^^)! ↓