【PyTorch入門】第1回 PyTorchとは

#はじめに
研究室でPyTorchを使うということで，PyTorchの勉強を始めました．
[PyTorchの公式チュートリアル](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)を参考に，自分なりにPyTorchの使い方を基礎からまとめることで勉強していこうかと思います．公式チュートリアルが割と親切なので，とても助かります．

#Tensor
PyTorchではTensorという型で行列を表現します．
TensorはNumpyのndarrayと似ていますが，ndarrayと比べて，GPUを使うことで処理を高速化できるらしいです．

#行列の作成
```python
from __future__ import print_function
import torch
```
###初期化されていない5x3行列
PyTorchでは初期化しないと，何かしらの値が入っている．

```python
x = torch.empty(5, 3)
print(x)
```

```text:実行結果
tensor([[-1.6809e+27,  4.5759e-41, -1.6809e+27],
        [ 4.5759e-41, -1.6808e+27,  4.5759e-41],
        [-1.0937e+14,  3.0683e-41, -1.0983e+14],
        [ 3.0683e-41, -1.6808e+27,  4.5759e-41],
        [ 4.8761e+19,  1.8522e+28,  1.8057e+28]])
```

###乱数で初期化された5x3行列

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

```text:実行結果
tensor([[0.9196, 0.4354, 0.0992],
        [0.6225, 0.3860, 0.0710],
        [0.4529, 0.0680, 0.2155],
        [0.6509, 0.8464, 0.9153],
        [0.6973, 0.3458, 0.6611]])
```

###ゼロで満たされた5x3行列
dtypeにlongを指定．

```python
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
```

```text:実行結果
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
```

###データから作成

```python
x = torch.tensor([5.5, 3])
print(x)
```

```text:実行結果
tensor([5.5000, 3.0000])
```

###既存のTensorから作成

```python
# 行列xを1で埋める（double型）
x = x.new_ones(5, 3, dtype=torch.double)
print(x)

# 行列xを平均0，分散1の正規分布で埋める（float型）
# このように型の上書きもできる
x = torch.randn_like(x, dtype=torch.float)
print(x)    
```

```text:実行結果
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)

tensor([[-1.0114,  0.9043, -0.0096],
        [ 0.7027, -0.0650,  0.3233],
        [ 0.4848, -0.7181,  0.1159],
        [-2.1770,  1.1517, -1.4411],
        [-0.5862,  1.0985, -0.7677]])
```

### Tensorサイズの取得

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

```text:実行結果
torch.Size([5, 3])
```

# Tensorの操作

### 加算

加算にはいくつかの方法があります．

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

```text:実行結果
tensor([[0.8883, 0.6499, 0.7337],
        [0.4430, 0.2177, 0.8254],
        [0.3867, 0.5194, 0.7142],
        [0.0081, 0.0794, 0.0565],
        [0.3625, 0.7039, 0.0115]])

tensor([[0.4755, 0.9994, 0.6331],
        [0.3408, 0.7533, 0.4965],
        [0.7359, 0.4444, 0.8851],
        [0.3004, 0.0765, 0.7828],
        [0.3236, 0.1944, 0.2417]])
```

方法1：

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

```text:実行結果
tensor([[1.3638, 1.6493, 1.3668],
        [0.7839, 0.9710, 1.3218],
        [1.1226, 0.9638, 1.5993],
        [0.3084, 0.1559, 0.8393],
        [0.6861, 0.8983, 0.2532]])
```

方法2：

```python
print(torch.add(x, y))
```

```text:実行結果
tensor([[1.3638, 1.6493, 1.3668],
        [0.7839, 0.9710, 1.3218],
        [1.1226, 0.9638, 1.5993],
        [0.3084, 0.1559, 0.8393],
        [0.6861, 0.8983, 0.2532]])
```

方法3：引数として出力Tensorを提供

```python
result = torch.empty(5, 3) # 出力Tensorの作成
torch.add(x, y, out=result)
print(result)
```

```text:実行結果
tensor([[1.3638, 1.6493, 1.3668],
        [0.7839, 0.9710, 1.3218],
        [1.1226, 0.9638, 1.5993],
        [0.3084, 0.1559, 0.8393],
        [0.6861, 0.8983, 0.2532]])
```

方法4：入力Tensorを直接書き換える

```python
y.add_(x) # y=x+y
print(y)
```

```text:実行結果
tensor([[1.3638, 1.6493, 1.3668],
        [0.7839, 0.9710, 1.3218],
        [1.1226, 0.9638, 1.5993],
        [0.3084, 0.1559, 0.8393],
        [0.6861, 0.8983, 0.2532]])
```
### リサイズ
Tensorをリサイズするときは`view()`を使います．
同じ要素を持った，違う形の行列を作成します．

```python
x = torch.randn(4, 4) # 4x4の乱数行列
y = x.view(16) # 16次元の行列
z = x.view(-1, 8)  # -1を使うと，自動で調整してくれる
print(x.size(), y.size(), z.size())
```
```text:実行結果
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
```

### 要素数1のTensor

要素数1のTensorに対して`item()`を使うと，値をpythonの値として取得するとこができます．

```python
x = torch.randn(1)
print(x)
print(x.item())
```

```text:実行結果
tensor([1.6671])
1.6670634746551514
```

# Torch Tensor から Numpy Array への変化

### Torch Tensor → Numpy Array

```python
a = torch.ones(5)
b = a.numpy()
print(a)
print(b)
```
```text:実行結果
tensor([1., 1., 1., 1., 1.])
[1. 1. 1. 1. 1.]
```

Torch Tensor とNumpy Array は同じメモリアドレスを共有しています．そのため，一方を変更するとも一方も変更されます．

```python
a.add_(1)
print(a)
print(b)
```
```text:実行結果
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
```

### Numpy Array → 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)
```

```text:実行結果
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
```