PyTorch 1.5 : PyTorch の学習 : Autograd: 自動微分 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/27/2020 (1.5.0)
* 本ページは、PyTorch 1.5 Tutorials の以下のページを翻訳した上で適宜、補足説明したものです:
- Learning PyTorch : Autograd: Automatic Differentiation
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ |
Facebook: https://www.facebook.com/ClassCatJP/ |
PyTorch の学習 : Autograd: 自動微分
PyTorch で総てのニューラルネットワークの中心は autograd パッケージです。最初にこれを簡単に見てましょう、そしてそれから最初のニューラルネットワークの訓練へと進みます。
autograd パッケージは Tensor 上の全ての演算に対して自動微分を提供します。それは define-by-run フレームワークで、これはどのようにコードが実行されるかによって backprop が定義されてそして総ての単一の反復が異なる可能性があるを意味します。
これを幾つかのサンプルでより単純な用語で見てみましょう。
Tensor
torch.Tensor はパッケージの中心的なクラスです。その属性 .requires_grad を True に設定すると、それはその上の総ての演算を追跡し始めます。計算を終了するとき .backward() を呼び出して総ての勾配を自動的に計算させることが可能です。この tensor のための勾配は .grad 属性に累積されます。
履歴の追跡を tensor にやめさせるために、計算履歴からそれを切り離して将来の計算が追跡されることを回避するために .detach() を呼び出すことができます。
履歴の追跡 (そしてメモリ使用) を回避するために、コードブロックを “with torch.no_grad():” 内にラップすることもできます。これはモデルを評価するときに特に有用です、何故ならばそのモデルは “requires_grad=True” を持つ訓練可能なパラメータを持つかもしれませんが、そのためには (訳注: モデル評価のためには) 勾配は必要ありません。
autograd 実装に対して非常に重要なもう一つのクラスがあります – Function です。
Tensor と Function は相互接続され非巡回グラフを構築します、これは計算の完全な履歴をエンコードします。各 Tensor は .grad_fn 属性を持ち、これは Tensor を作成した Function を参照します (ユーザにより作成された Tensor は除きます – それらの grad_fn is None です)。
導関数を計算することを望むのであれば、Tensor 上で .backward() を呼び出すことができます。もし Tensor がスカラーである (i.e. それが一つの要素データを保持する) ならば、backward() にどのような引数も指定する必要はありません、けれどもそれがより多くの要素を持つならば、適合する shape の tensor である gradient 引数を指定する必要があります。
import torch
tensor を作成してそれに伴う計算を追跡するために “requires_grad=True” を設定します。
x = torch.ones(2, 2, requires_grad=True) print(x)
tensor([[1., 1.], [1., 1.]], requires_grad=True)
tensor 演算を行ないます :
y = x + 2 print(y)
tensor([[3., 3.], [3., 3.]], grad_fn=)
y は演算の結果として作成されましたので、grad_fn を持ちます。
print(y.grad_fn)
<AddBackward0 object at 0x7f0ebeba5128>
y 上で更に演算を行ないます。
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_( … ) は既存の Tensor の requires_grad フラグを in-place で変更します。入力フラグが与えられない場合には False にデフォルト設定されます。
a = torch.randn(2, 2) a = ((a * 3) / (a - 1)) print(a.requires_grad) a.requires_grad_(True) print(a.requires_grad) b = (a * a).sum() print(b.grad_fn)
False True <SumBackward0 object at 0x7f0ebeba5d30>
勾配
さて今は backprop を行ないましょう、out は単一のスカラーを含むので、out.backward() は out.backward(torch.Tensor(1.)) と同値です。
out.backward()
勾配 d(out)/dx をプリントします。
print(x.grad)
tensor([[4.5000, 4.5000], [4.5000, 4.5000]])
4.5 の行列を得たはずです。out Tensor を “$o$” と呼称しましょう。$o = \frac{1}{4}\sum_iz_i$, $z_i = 3(x_i + 2)^2$ そして $z_i\mid_{x_i=1}=27$ を持ちます。従って $\frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i + 2)$ 更に $\frac{\partial o}{\partial x_i}\mid_{x_i=1} = \frac{9}{2} = 4.5$ となります。
数学的には、ベクトル値の関数 $\vec{y}=f(\vec{x})$ を持つのであれば、$\vec{x}$ に関する $\vec{y}$ の勾配はヤコビ行列です :
\[
\begin{split}J=\left(\begin{array}{ccc}
\frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}}\\
\vdots & \ddots & \vdots\\
\frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
\end{array}\right)\end{split}
\]
一般的に言えば、torch.autograd はベクトルとヤコビアンの積を計算するためのエンジンです。つまり、任意のベクトル $v=\left(\begin{array}{cccc} v_{1} & v_{2} & \cdots & v_{m}\end{array}\right)^{T}$ が与えられたとき、積 $v^{T}\cdot J$ を計算します。もし $v$ が偶々スカラー関数 $l=g\left(\vec{y}\right)$ の勾配、つまり $v=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right)^{T}$ であれば、連鎖率により、ベクトル-ヤコビアン積は $\vec{x}$ に関する $l$ の勾配になります :
\[
\begin{split}J^{T}\cdot v=\left(\begin{array}{ccc}
\frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\
\vdots & \ddots & \vdots\\
\frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
\end{array}\right)\left(\begin{array}{c}
\frac{\partial l}{\partial y_{1}}\\
\vdots\\
\frac{\partial l}{\partial y_{m}}
\end{array}\right)=\left(\begin{array}{c}
\frac{\partial l}{\partial x_{1}}\\
\vdots\\
\frac{\partial l}{\partial x_{n}}
\end{array}\right)\end{split}
\]
($v^{T}\cdot J$ は行ベクトルを与えることに注意してください、これは $J^{T}\cdot v$ を取ることによりカラムベクトルとして扱うことができます。)
ベクトル-ヤコビアン積のこの特質は非スカラー出力を持つモデルに外部勾配を供給することを非常に便利にします。
今はベクトル-ヤコビアン積の例を見てみましょう :
x = torch.randn(3, requires_grad=True) y = x * 2 while y.data.norm() < 1000: y = y * 2 print(y)
tensor([ -333.6394, 402.5522, -1427.5533], 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([1.0240e+02, 1.0240e+03, 1.0240e-01])
"with torch.no_grad():" 内にコードブロックをラッピングすることにより .requires_grad = True を持つ Tensor 上の履歴を追跡することから autograd を停止することもできます :
print(x.requires_grad) print((x ** 2).requires_grad) with torch.no_grad(): print((x ** 2).requires_grad)
True True False
あるいは同じ内容で、しかし勾配を必要としない新しい Tensor を得るために .detach() が使用できます :
print(x.requires_grad) y = x.detach() print(y.requires_grad) print(x.eq(y).all())
True False tensor(True)
[Read Later]
autograd.Function についてののドキュメントは https://pytorch.org/docs/stable/autograd.html#function にあります。
以上