パラメータの増減に際しいちいちLossの再計算をするのはめんどう
`autograd`パッケージは、自動微分の機能を提供することでこれを低減できる
他の多くのパッケージでは自動微分にはコンパイルが必要だがmxnetではpytorchのように、宣言的に、通常のコード内に記述可能


```python
import mxnet as mx
import mxnet.ndarray  as nd
import mxnet.autograd as ag # 0.10.0 （一応最新版）でもmxnet直下へはまだ取り込まれていない模様
```

# Attaching gradients

$$
f = 2x^2
$$
のxについての微分について考える


```python
x = mx.nd.array([[1,2],[3,4]])
```

一度fのxについての傾きを計算すると、どこかにそれを保持しておく必要がある  
mxnetでは`attach_grad()`メソッドで起動できる


```python
x.attach_grad()
```

この上でfを定義するとMXNetは計算グラフを自動生成する…あたかもレコーディングマシンを起動し、変数が生成された正しい経路をキャプチャするかのように
計算グラフの生成には少なくない計算資源が必要となるため、明示的に宣言しないとMXNetはグラフを生成しない
`with autograd.record()`によりレコーディングを開始する


```python
with ag.record():
    y = x * 2
    z = y * x
```

`z.backward()`で逆伝播の計算を行う
zが複数のエントリーを持つとき、`z.backward()`は`mx.nd.sum(z).backward()` に相当する


```python
z.backward()
```

$$
\begin{align}
y = 2x\\
z = xy
\end{align}
$$

なので

$$
z = 2x^2
$$

$$
\begin{align}
\frac{dy}{dx} = 2 \\
\frac{dz}{dx} = 4x
\end{align}
$$


```python
print(x.grad)

```

    
    [[  4.   8.]
     [ 12.  16.]]
    <NDArray 2x2 @cpu(0)>


# Head gradients and the chain rule
yがxの関数であるとき、xに対するyの導関数のみに関心がある。
一方でzがxの関数であるときは、xに対するzの傾きのみに関心がある。
連鎖則を用いると（TutorialだとTrain ruleになってる…雑）

$$
\frac{dz}{dx} = \frac{dz}{dy} \frac{dy}{dx}
$$

yはzの一部と言え、`x.grad`にdz/dxを格納したいとき、先頭の傾きとしてdz/dyを`backward()`の入力として渡すことができる。デフォルトの引数は`nd.ones_like(y)`となる


```python
with ag.record():
    y = x * 2
    z = y * x
    
head_gradient = nd.array([[10,1.],[.1,.01]])
z.backward(head_gradient)
print(x.grad)
```

    
    [[ 40.           8.        ]
     [  1.20000005   0.16      ]]
    <NDArray 2x2 @cpu(0)>


> わかりづらい原因はこれまでの説明文: xでの傾き(4,8,12,16)について、重み付けの行列(10,1,0.1,0.01)を渡せるよ、ということでいいのだと思う（多分） 何も指定しない場合は1が初期値になるという話

基礎がわかったところで、Pythonicな制御フローでの計算例


```python
a = nd.random_normal(shape=3) # 1行3列、正規分布で初期化された配列
a.attach_grad()

with ag.record():
    b = a * 2
    while (nd.norm(b) < 1000).asscalar():  # 行列のL2ノルムが1000以下
        b = b * 2
        
    if (mx.nd.sum(b) > 0).asscalar():
        c = b
    else:
        c = 100 * b
```


```python
head_gradient = nd.array([0.01,1.0,.1])
c.backward(head_gradient)
```


```python
print(a.grad)  # チュートリアルだとx.gradを出力してる…
```

    
    [   5.11999989  512.           51.20000076]
    <NDArray 3 @cpu(0)>



```python
print(a)
print(b)
print(c)
```

    
    [ 2.21220636  1.16307867  0.7740038 ]
    <NDArray 3 @cpu(0)>
    
    [ 1132.6496582    595.49627686   396.28994751]
    <NDArray 3 @cpu(0)>
    
    [ 1132.6496582    595.49627686   396.28994751]
    <NDArray 3 @cpu(0)>



```python
nd.norm(b)

```




    
    [ 1339.61071777]
    <NDArray 1 @cpu(0)>



c = 2^T a なのでだいたいこんな感じになる:512や1024などなど（Pythonic...なのか？）

95cb127a429ca0b5eb855b93d50ec238ee3c47cc
ipynb
Jupyter Notebook
notebooks/01_crashcourse/Automatic differentiation with autograd.ipynb
leo-mon/gluon-tutorials
9060e2d53fc66eecb19d8decb2e0980647d25ebe
Apache-2.0
notebooks/01_crashcourse/Automatic differentiation with autograd.ipynb
leo-mon/gluon-tutorials
9060e2d53fc66eecb19d8decb2e0980647d25ebe
Apache-2.0
notebooks/01_crashcourse/Automatic differentiation with autograd.ipynb
leo-mon/gluon-tutorials
9060e2d53fc66eecb19d8decb2e0980647d25ebe
Apache-2.0
Qwen/Qwen-72B
1. YES
2. YES