PyTorch公式チュートリアル Deep Learning with PyTorch #1

[PyTorch](http://pytorch.org/)の公式チュートリアルである[Deep Learning withPyTorch: A 60 Minute Bitz](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)の日本語訳といくつか補足したものです。
自分はこれまで[Tensorflow](https://www.tensorflow.org/)をメインに研究などのコードを作成したのでChainerについてはあまり知識がありません。

## PyTorchとは何か
Pythonの科学計算パッケージ。ターゲットとなっているのは以下の二つの層。
1. GPUを使用するためにNumPyからの以降。
2. 柔軟かつ素早い実装が可能な深層学習研究プラットフォーム。

## Getting Started
まずはPyTorchで用いられている基本的な概念について。

### Tensor
TensorはNumpyのndarrayと似ているものの、GPUで利用されることができ、計算を加速することができる。
例えば、5x3の行列を作成したい場合は[`torch.Tensor`](http://pytorch.org/docs/master/tensors.html)を使って以下のように初期化できる。ちなみに`torch.tensor`は実際には`torch.FloatTensor`のエイリアスになっており、32-bit signed integerなどを初期化したい場合には`torch.IntTensor`などを用いる。

```py
import torch
x = torch.Tensor(5,3) # 5X3初期化されていないの行列が生成される。
```
また`torch.rand()`でランダムに初期化された行列を生成することもできる。

```py
x = torch.rand(5,3)
print(x)
```
Tensorのサイズを確認したい時は`size()`というクラスメソッドでTensorのサイズを表す`torch.Size`が返される。

```python

print(x.size()) # torch.Size([5,3])が出力される
```
ちなみにこの`torch.Size`はtupleとなっているため、Pythonの通常のtupleと同様の操作が可能。例えば `x.size()[0]`とすればTensorの一次元目の大きさを取得できる。

### Operations
PyTorchには様々演算記法があり、以下でそれらを紹介する。
#### 和（addition）
まず基本的な`+`を用いた和の演算と`torch.add(a,b)`を用いた以下の方法がある。

```python
x = torch.rand(5,3)
y = torch.rand(5,3)
print(x+y)
print(torch.add(x,y)) #どちらでも同様の演算結果を得ることができる
```
また、`torch.add(a,b)`を用いる場合、オプショナルな引数`out`を用いて結果を別のtensorに格納することも可能。

```python
result = torch.Tensor(5,3)
torch.add(x,y, out=result)
print(result) # 先ほどのx+yと同様の結果がresultに格納される。
```

またPyTorchにおいて、Tensorをin-placeに変更する（つまり元のTensorの中身を書き換えてしまう）ような演算には関数名の後に接尾辞（postfix）として`_`がつく。
例えば、あるTensorをin-plceに転置する`Tensor.t_()`メソッドなど。

```python
>>> x = torch.rand(3,5)
>>> x.t_()
 0.9643  0.6642  0.7152
 0.8060  0.8571  0.8347
 0.5978  0.3552  0.2373
 0.3823  0.3224  0.9044
 0.7525  0.7579  0.0206
[torch.FloatTensor of size 5x3]
>>> x
 0.3229  0.8450  0.7299
 0.7590  0.0953  0.8313
 0.4026  0.4635  0.1336
 0.0777  0.3386  0.9635
 0.0899  0.3295  0.1540
[torch.FloatTensor of size 5x3]
# 3x5のTensorだったxが5x3に転置されているのが確認できる。

>>> x = torch.rand(3,5)
>>> x.t()
 0.3229  0.8450  0.7299
 0.7590  0.0953  0.8313
 0.4026  0.4635  0.1336
 0.0777  0.3386  0.9635
 0.0899  0.3295  0.1540
[torch.FloatTensor of size 5x3]
>>> x
 0.3229  0.7590  0.4026  0.0777  0.0899
 0.8450  0.0953  0.4635  0.3386  0.3295
 0.7299  0.8313  0.1336  0.9635  0.1540
[torch.FloatTensor of size 3x5]
# x自体は3x5のまま。
```
`add()`演算についても、in-placeで以下のように書きあらわすことができる。

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

#### Tensorの大きさを変更する
Tensorの大きさを変える際には`tensor.view`を用いる。（tensorflowと比べるとちょっと名前がわかりにくいですね）
`torch.view()`はin-placeな演算ではないので、元のtensorは変更されない。

```python
x = torch.randn(4,4)
y = x.view(16) # 4x4の行列が16x1のに変形される
z = x.view(-1,8) # -1が含まれる場合、他の次元に合わせて整形される。ここでは一方の次元が8なので16/8=2となる。
```

### Numpy Bridge
NumpyのarrayをTorchのtensorに変換したり、その逆を行うことも可能。
#### TensorをNumpyのarrayに変換する
```python
>>> a = torch.ones(5) # 要素が全てfloatの1の5x1のベクトルが出力される
 1
 1
 1
 1
 1
[torch.FloatTensor of size 5]
>>> b = a.numpy() # TensorをNumpy化する
>>> print(b)
[1. 1. 1. 1. 1.]
```

#### NumpyのarrayをTorch Tensorに変換する

```python
import numpy as np
a = np.ones(5) # 5x1の要素が全て1のNumpy配列を作成
b = torch.from_numpy(a) # bはサイズが5のdoubleのTTensorになる
np.add(a, 1, out=a)
print(a)
print(b)
```