PyTorch Tutorials 1/5, 2/5

## 注意！

PyTorch 0.4 以降、`Variable`は非推奨となり、`Tensor`に統合されました。
>Welcome to the migration guide for PyTorch 0.4.0. In this release we introduced many exciting new features and critical bug fixes, with the goal of providing users a better and cleaner interface. In this guide, we will cover the most important changes in migrating existing code from previous versions:
・ Tensors and Variables have merged

よって、本記事の内容は一部サポートされていませんので、0.4向けに改変された[元のTutorial](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)を見てください！


# Pytorch Tutorial
### References:
公式ドキュメント：[**PyTorch Tutorials**](http://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html#sphx-glr-beginner-blitz-tensor-tutorial-py)
このノートのソース：[Github]
(https://github.com/kenmikanmi/PyTorch_Tutorials_in_hand/blob/master/Pytorch_Tutorials_.ipynb)


## 本記事について

基本的に，PyTorch初心者向けの[公式ドキュメント](http://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html#sphx-glr-beginner-blitz-tensor-tutorial-py)に記載してあるコードを動かしたものです。公式ドキュメントには，以下の５つのチュートリアルがあり，一応60分で全体をさらうことができるみたいです。

![Screenshot from 2018-01-19 19-50-44.png](https://qiita-image-store.s3.amazonaws.com/0/182818/d7b4aa12-4170-9d6f-62ae-92e17c409a5d.png)


このノートは，公式ドキュメントに公開されているチュートリアルのうち，最初の２つ：
- What is PyTorch?
- Autograd: automatic differentiation

の日本語訳と，適宜説明を追加しています。

余談ですが，PyTorchがなんたるかを知るにはこちらの記事が良いと思います：
https://qiita.com/miyamotok0105/items/1fd1d5c3532b174720cd

## いきさつ（読まなくても良い）
研究でこれまで色々論文を読んできたけれど，学習済みモデルをベースに，自分のタスクに対応した学習を行う必要がでてきました。論文著者の公開している学習済みモデルが`Torch7`の形式(`.t7`)で保存されていたのですが，`Torch7`ライブラリを使うにはプログラミング言語`lua`を新しく学ばなくてはならない…

そんななか，`Torch7`のモデルを簡単に利用できる別の`Python`ベースのライブラリ`PyTorch`があることを知り，軽く使ってみた所`PyTorch`いけそう・・・と思ったので，一から勉強することにしました。

PyTorchの公式ドキュメントのチュートリアルがわかりやすいという情報を得たので，jupyter notebookでちまちま動かしてみたものを，後で参照しやすいようにまとめました。このノートの[ソース](https://github.com/kenmikanmi/PyTorch_Tutorials_in_hand/blob/master/Pytorch_Tutorials_.ipynb)の方が見やすいと思います。ツッコミ歓迎ですm(._.)m


## PyTorchとは

- 深層学習研究のプラットフォーム
- numpyよりも優れた，GPU上での計算の仕組みを提供する

なるほど。

## はじめに

### Tensors

PyTorchの基本は`torch.Tensor`に含まれる`Tensor`型です。
`Tensor`は`numpy.ndarray`に似ているが，`Tensor`はGPU上での計算において優れた型です。

これから`Tensor`の使い方を軽く見てみましょう：

```python
from __future__ import print_function
import torch
```

***
初期化されていない（5, 3）行列を`Tensor`型で宣言すると：


```python
x = torch.Tensor(5, 3)
x
```




    
    -1.9742e-22  4.5800e-41  4.0313e+05
     3.0777e-41  1.6255e-43  1.5554e-43
     1.5975e-43  1.3873e-43  1.4574e-43
     6.4460e-44  1.5975e-43  1.3593e-43
     1.5414e-43  1.4013e-43  1.5414e-43
    [torch.FloatTensor of size 5x3]



***
乱数で初期化した（5, 3）行列を`Tensor`型で宣言すると


```python
x = torch.rand(5, 3)
x
```




    
     0.3983  0.2964  0.2377
     0.9519  0.3093  0.3955
     0.4799  0.2055  0.6707
     0.8251  0.8141  0.7262
     0.9655  0.8692  0.6213
    [torch.FloatTensor of size 5x3]



***
サイズの確認は：


```python
x.size()
```




    torch.Size([5, 3])



***

### Operations：演算

`Tensor`は様々な方法で四則演算できる。

例えば,加算１つとっても４つくらいの方法がある。

加算方法１：


```python
y = torch.rand(5, 3)
x + y
```




    
     0.6591  0.3766  1.1684
     1.8462  0.7029  0.9779
     1.4392  0.5668  0.9335
     1.1356  1.1658  0.9240
     1.8273  1.0639  1.0738
    [torch.FloatTensor of size 5x3]



***
加算方法２：


```python
print(x + y)
```

    
     0.6591  0.3766  1.1684
     1.8462  0.7029  0.9779
     1.4392  0.5668  0.9335
     1.1356  1.1658  0.9240
     1.8273  1.0639  1.0738
    [torch.FloatTensor of size 5x3]
    


***
加算方法３：


```python
result = torch.Tensor(5, 3)      # make empty (5, 3) Tensor
torch.add(x, y, out = result)    # input x + y to Tensor
result
```




    
     0.6591  0.3766  1.1684
     1.8462  0.7029  0.9779
     1.4392  0.5668  0.9335
     1.1356  1.1658  0.9240
     1.8273  1.0639  1.0738
    [torch.FloatTensor of size 5x3]



***
加算方法４：


```python
y.add_(x)     # "add_(x)" -> y = y + x 
y
```




    
     0.6591  0.3766  1.1684
     1.8462  0.7029  0.9779
     1.4392  0.5668  0.9335
     1.1356  1.1658  0.9240
     1.8273  1.0639  1.0738
    [torch.FloatTensor of size 5x3]



### Note
基本的に，演算の関数の後に `_` がついているものは代入の操作を表す。
上記の例で`y.add_(x)`は，`y = y+x`を行うことと同じ。
***

`numpy`と同じように，次のようにインデックスを指定できる。


```python
x[:, 1]
```




    
     0.2964
     0.3093
     0.2055
     0.8141
     0.8692
    [torch.FloatTensor of size 5]



### あとで読みたい
`Tensor`の演算は他にも，インデックス，スライス，線形代数の演算などが100以上ある。詳細は[こちら](http://pytorch.org/docs/torch)。

***

## `Torch.Tensor`と`numpy`を行ったり来たり

`Torch.Tensor`と`numpy`の要素は，同じメモリの場所に格納されており，相互の変換は簡単にできる。

### `numpy`への変換


```python
a = torch.ones(5)
a
```




    
     1
     1
     1
     1
     1
    [torch.FloatTensor of size 5]




```python
b = a.numpy()
b
```




    array([ 1.,  1.,  1.,  1.,  1.], dtype=float32)



***
ここで，面白いことに…


```python
a.add_(1)
print(a)
print(b)
```

    
     2
     2
     2
     2
     2
    [torch.FloatTensor of size 5]
    
    [ 2.  2.  2.  2.  2.]


***
### `torch.Tensor`に変換


```python
import numpy as np

a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
```

    [ 2.  2.  2.  2.  2.]
    
     2
     2
     2
     2
     2
    [torch.DoubleTensor of size 5]
    


CPU上の`Tensor`は，全て`numpy`に変換できるらしい（GPU上のTensorは変換できないということだろうか?）。

## `autograd`と`Variable`

Autogradは”automatic differentiation”のことで，そのパッケージである`autograd`はPyTorchの中核を担っている。これから，`autograd`に少し触れてみて，それをもとにニューラルネットワークを学習させてみよう。

`autograd`パッケージは，Tensorの全ての演算に対して自動微分を行うためのもの。これは，PyTorchの特徴である"define-by-run"を実現している。つまり，順伝播のコードを書くだけで逆伝播が定義できる。

### `Variable`

- `autograd.Variable`が，`autograd`の中心的なパッケージである。`Variable`は`Tensor`のラッパーであり，`Tensor`のほぼ全ての演算が含まれている。
- ネットワークを定義してしまえば，`.backward()`を呼び出すだけで勾配計算を自動的に行うことができる。

Tensorの生データには`.data`でアクセスできる。そして，`Variable`に関する勾配は`.grad`に蓄積されている。`Variable`の概念図を以下に示す。

![概念図](http://pytorch.org/tutorials/_images/Variable.png)

### `Function`

autogradに関して，もうひとつ重要なクラスがあります。それは`Function`と呼ばれるパッケージです。
`Variable`と`Function`は内部でつながっていて，この２つによってニューラルネットワークのグラフが構築されます。そしてこのグラフに，ニューラルネットの計算の全ての履歴が残ります。

生成されたvariableのそれぞれに`.grad_fn`という属性があり，この属性によってどの`Function`によってvariableが生成されたのかを参照できる。ただし，ユーザによって作られたvariableの場合`grad_fn`は`None`となる。

variableの導関数を計算したいのであれば，variableがもっている`.backward()`を呼び出すと良い。もしvariableが単一の要素だけのとき，`backward()`には特に引数を指定する必要はないが，複数の要素をもつときは引数`grad_output`を指定してやる必要がある（←どういうこと？）。

と言っても分かりづらいので，具体例を交えながら見ていきましょう。

***


```python
import torch
from torch.autograd import Variable
```

***
variableを作ってみます：


```python
x = Variable(torch.ones(2, 2), requires_grad = True)
x
```




    Variable containing:
     1  1
     1  1
    [torch.FloatTensor of size 2x2]



***
次に，variableの加算をしてみましょう：


```python
y = x + 2
y
```




    Variable containing:
     3  3
     3  3
    [torch.FloatTensor of size 2x2]



***
さっき言ったように，何らかの演算の後に生成されたvariableには属性`grad_fn`が付与されます。`y`の`grad_fn`を参照してみると：


```python
y.grad_fn
```




    <AddBackward0 at 0x7fac69a8d320>



***
`y`にもっと色んな演算をしていきます：


```python
z = y * y * 3
out = z.mean()
print(z, out)
```

    Variable containing:
     27  27
     27  27
    [torch.FloatTensor of size 2x2]
     Variable containing:
     27
    [torch.FloatTensor of size 1]
    


***
## 勾配計算

いよいよ逆伝播の計算です。
次に行う`out.backward()`は，`out.backward(torch.tensor([1.0]))`と等価です。


```python
out.backward()
```

***
勾配を出力してみましょう。勾配とはすなわち $\frac{d({\rm out})}{dx}$　のことです。


```python
x.grad
```




    Variable containing:
     4.5000  4.5000
     4.5000  4.5000
    [torch.FloatTensor of size 2x2]



`4.5`の要素をもつ行列（テンソル）が得られました。これはどういうことなのでしょうか。

`out` Variable を"$o$"とします。$o$は，`out = z.mean()`で定義したので，次式で得られます。

$o = \frac{1}{4}\sum_i z_i$

ここで，$z_i = 3(x_i + 2)^2$ですから，

$\frac{\partial o}{\partial x_i}=\frac{3}{2}(x_i + 2)$

$x_i$の各要素は$1$なので，


$\frac{\partial o}{\partial x_i}|_{x_i = 1} = 4.5$

確かに，Variableには過去の計算の履歴が残っていましたね！

***

### autogradを使えば，もっとクレイジーなことができる！


```python
import torch
x = torch.randn(3)
x = Variable(x, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)
```

    Variable containing:
    -1453.1710
      229.0913
      -37.1547
    [torch.FloatTensor of size 3]
    



```python
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)

print(x.grad)
```

    Variable containing:
      51.2000
     512.0000
       0.0512
    [torch.FloatTensor of size 3]
    


この上記の操作は何を意味しているのでしょうか…？（疑問）

***

`Python`歴1年半で，プログラミング言語やライブラリのリテラシーがあまりない僕にもなんとなくわかったような気がします。

[**次のチュートリアルへ**](https://qiita.com/kenmikamin/items/a31937cbe6385a19b82f)