<jupyter_start><jupyter_text>``torch.autograd`` による自動微分ニューラルネットワークを学習する際、最もよく使われるアルゴリズムは**back propagation**です。このアルゴリズムでは、パラメータ（モデルの重み）はこのアルゴリズムでは、パラメータ（モデルの重み）は、与えられたパラメータに対する損失関数の**勾配**に応じて調整されます。**gradient**に応じてパラメータ（モデルの重み）を調整します。この勾配を計算するために、PyTorchは`torch.autogradat`という微分エンジンを内蔵しています。このエンジンは、任意の勾配の自動計算をサポートします。入力 `x`、パラメータ `w` および `b` と、何らかの損失関数を持つ最も単純な1層のニューラルネットワークを考えてみましょう。これは次のようにPyTorchで定義できます。<jupyter_code>import torch

x = torch.ones(5)  # input tensor
y = torch.zeros(3)  # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w) + b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)<jupyter_output><empty_output><jupyter_text>テンソルと関数と計算グラフこのコードは、以下の**計算グラフ**を定義しています。![損失の勾配を計算するために、2つのパラメータ'w'と'b'を持つ計算グラフを示す図](images/computational-graph.png)このネットワークでは、`w`と`b`は**パラメータ**であり、これを最適化する必要があります。そのため、これらの変数に対する損失関数の勾配を計算する必要があります。これを行うためにはテンソルの`requires_grad`プロパティを設定します。> **Note:** `requires_grad` の値は，テンソルの作成時に設定することもできますし、後で `x.requirees_grad_(True)` メソッドを使って設定することもできます。計算グラフを構築するためにテンソルに適用する関数は実際にはクラス `Function` のオブジェクトです。このオブジェクトは、関数を *forward* 方向に計算する方法と、その微分を *backward* 方向に微分計算する方法を知っています。逆伝搬関数への参照は、テンソルの `grad_fn` プロパティに格納されています。`Function`の詳細については[ドキュメント](https://pytorch.org/docs/stable/autograd.htmlfunction)を参照してください。<jupyter_code>print('Gradient function for z =', z.grad_fn)
print('Gradient function for loss =', loss.grad_fn)<jupyter_output>Gradient function for z = <AddBackward0 object at 0x12bc2c8e0>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward object at 0x12bc2c5e0><jupyter_text>勾配計算ニューラルネットワークのパラメータの重みを最適化するためにはパラメータに対する損失関数の導関数を計算する必要があります。具体的にはある固定値の`x`と`y`の下で$\frac{\partial loss}{\partial w}$と$\frac{\partial loss}{\partial b}$が必要です。これらの導関数を計算するために、`loss.backward()`を呼び出し、`w.grad`と`b.grad`から値を取得します。<jupyter_code>loss.backward()
print(w.grad)
print(b.grad)<jupyter_output>tensor([[0.0424, 0.0851, 0.3282],
        [0.0424, 0.0851, 0.3282],
        [0.0424, 0.0851, 0.3282],
        [0.0424, 0.0851, 0.3282],
        [0.0424, 0.0851, 0.3282]])
tensor([0.0424, 0.0851, 0.3282])<jupyter_text>> Note: 計算グラフのリーフノードのうち`requires_grad` プロパティが `True` に設定されているノードに対してのみ`grad` プロパティを取得することができます。グラフ内の他のすべてのノードでは勾配を利用することはできません。さらにパフォーマンス上の理由から`backward`を用いた勾配計算は1つのグラフに対して1回しか行うことができません。もし同じグラフに対して複数回の`backward`呼び出しを行う必要がある場合には`retain_graph=True`を`backward`呼び出しに渡す必要があります。 勾配トラッキングの無効化デフォルトでは`requires_grad=True`となっているすべてのテンソルはその計算履歴を追跡し、勾配計算をサポートします。しかし勾配計算が必要ない場合もあります。例えば、モデルを学習した後そのモデルをある入力データに適用したい場合、つまり例えばモデルをトレーニングした後、入力データに適用したい場合、つまり、ネットワークを介して*forward* 計算だけを行いたい場合などです。追跡計算を止めるには計算コードを`torch.no_grad()`ブロックで囲むことで計算を停止することができます。<jupyter_code>z = torch.matmul(x, w) + b
print(z.requires_grad)

with torch.no_grad():
    z = torch.matmul(x, w) + b
print(z.requires_grad)<jupyter_output>True
False<jupyter_text>同じ結果を得るためのもう一つの方法は，テンソルに`detach()`メソッドを使うことです。<jupyter_code>z = torch.matmul(x, w) + b
z_det = z.detach()
print(z_det.requires_grad)<jupyter_output>False<jupyter_text>勾配トラッキングを無効にしたい理由はいくつかあります。  - ニューラルネットワークのいくつかのパラメータを **凍結パラメータ** にする。これは[学習済みネットワークの微調整](https://pytorch.org/tutorials/beginner/finetuning_torchvision_models_tutorial.html)に一般的なシナリオです。  - フォワードパスのみを行っている場合に**計算を高速化**する。なぜならばテンソルの計算は勾配を追跡しない方が効率的だからです。 計算機上のグラフの詳細----------------------------概念的には、autogradはデータ（テンソル）と、実行されたすべての演算（およびその結果としての新しいテンソル）を[関数](https://pytorch.org/docs/stable/autograd.htmltorch.autograd.Function)からなる有向非巡回グラフ（DAG）に記録します。このDAGでは、葉が入力テンソル、根が出力テンソルです。このグラフを根から葉へとたどっていくと連鎖律を使って自動的に勾配を計算することができます。フォワードパスでは、autogradは2つのことを同時に行います。- 要求された演算を実行して結果のテンソルを計算する- DAGの中で演算の*勾配関数*を維持する。バックワードパスは、DAGのルートで`.backward()`が呼ばれたときに起動します。`autograd` は次に- 各 `.grad_fn` から勾配を計算します。- それらをそれぞれのテンソルの `.grad` 属性に蓄積する。- 連鎖律を利用して、リーフテンソルにまで伝搬します。**PyTorchではDAGは動的なものです**。重要なことは、グラフがゼロから再作成されるということです。`.backward()`を呼び出すたびに、autogradは新しいグラフの作成を開始します。これこそが、モデルで制御フロー文を使用できる理由です。必要に応じて、反復ごとに形状、サイズ、操作を変更することができます。  Optional reading: ヤコビアンとベクトル勾配の積多くの場合スカラーの損失関数があり、いくつかのパラメータに対する勾配を計算する必要があります。しかし，場合によっては出力関数が任意のベクトルである場合があります。このような場合、PyTorchは実際の勾配ではなく、いわゆる **ヤコビアン積** を計算することができます。ベクトル関数 $\vec{y}=f(\vec{x})$ に対してここで、$\vec{x}=\langle x_1,\dots,x_n\rangle$ と $\vec{y}=\langle y_1,\dots,y_m\rangle$ とすると、その勾配は次の式で与えられます *ヤコビアン行列*:$\begin{align}\begin{align}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{align}\end{align}$ヤコビアン行列を計算する代わりに、PyTorchではヤコビアン積 **$v^T\cdot J$** を計算します。これは引数に $v^T\cdot J$ を指定して `backward` を呼び出すことで実現できます。$v$ のサイズは元のテンソルのサイズと同じでなければなりません．積を計算したい元のテンソルのサイズと同じである必要があります。<jupyter_code>inp = torch.eye(5, requires_grad=True)
out = (inp + 1).pow(2)
out.backward(torch.ones_like(inp), retain_graph=True)
print("First call\n", inp.grad)
out.backward(torch.ones_like(inp), retain_graph=True)
print("\nSecond call\n", inp.grad)
inp.grad.zero_()
out.backward(torch.ones_like(inp), retain_graph=True)
print("\nCall after zeroing gradients\n", inp.grad)<jupyter_output>First call
 tensor([[4., 2., 2., 2., 2.],
        [2., 4., 2., 2., 2.],
        [2., 2., 4., 2., 2.],
        [2., 2., 2., 4., 2.],
        [2., 2., 2., 2., 4.]])

Second call
 tensor([[8., 4., 4., 4., 4.],
        [4., 8., 4., 4., 4.],
        [4., 4., 8., 4., 4.],
        [4., 4., 4., 8., 4.],
        [4., 4., 4., 4., 8.]])

Call after zeroing gradients
 tensor([[4., 2., 2., 2., 2.],
        [2., 4., 2., 2., 2.],
        [2., 2., 4., 2., 2.],
        [2., 2., 2., 4., 2.],
        [2., 2., 2., 2., 4.]])