Pytorch Tensorについて

# Pytorch Turtorial

## TL;DR
PytorchのTensorについての自分なりのまとめです。追記していくかもしれません。

## Tensor
TensorはGPUで動くように作成されたPytorchでの行列のデータ型です。Tensorはnumpy likeの動きをし、numpyと違ってGPUで動かすことができます。基本的にnumpy likeの操作が可能です。(インデックスとかスライスとかそのまま使えます)

### Tensorとnumpy
```python
import numpy as np
import torch

# Tensor用にdtypeとdeviceを定義
dtype = torch.float
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("device:", device)

# 10*10行列の作成
np_arr=np.random.randn(10,10)
tensor=torch.randn(10,10,device=device,dtype=dtype)

# データ型の確認
print("np_arr:", type(np_arr))
print("tensor:", type(tensor))

# データの形状確認
print("np_arr shape:", np_arr.shape)
print("tensor shape:", tensor.size()) #tensor.shapeもできるけどこっちが推奨っぽい

# データの色々確認
print("np_arr dtype:", np_arr.dtype)
print("tensor device:", tensor.device)
print("tensor dtype:", tensor.dtype)
```
```
#device
device: cuda:0
#データ型
np_arr: <class 'numpy.ndarray'>
tensor: <class 'torch.Tensor'>
#データの形状
np_arr shape: (10, 10)
tensor shape: torch.Size([10, 10])
#データの詳細
np_arr dtype: float64
tensor device: cuda:0
tensor dtype: torch.float32
```

### numpyとTensorの相互変換
Tensorがどのdeviceに乗っているかの注意が必要です。numpyに変換するときはcpuに乗ってないとエラーが起こります。また、`torch.from_numpy()`でTensorに変換するとdeviceはCPUになりdtypeはtorch.float64になるので注意が必要です。GPUかCPUは`is cuda`を使っても確認できます。CPU, GPUの移動は`to()`メソッドで実装できます。

```python
new_np_arr=tensor.cpu().numpy()
new_tensor=torch.from_numpy(np_arr)

# データ型の確認
print("new_np_arr:", type(new_np_arr))
print("new_tensor:", type(new_tensor))

# データの形状確認
print("new_np_arr shape:", new_np_arr.shape)
print("new_tensor shape:", new_tensor.size()) 

# データの色々確認
print("new_np_arr dtype:", new_np_arr.dtype)
print("new_tensor device:", new_tensor.device)
print("new_tensor dtype:", new_tensor.dtype)

# TensorをGPUに飛ばし、dytpeをfloat32に変換
new_tensor=new_tensor.to(device)
new_tensor=new_tensor.to(dtype)
print("new_tensor device:", new_tensor.device)
print("new_tensor dtype:", new_tensor.dtype)
```

```
# 変換後のデータ型の確認
new_np_arr: <class 'numpy.ndarray'>
new_tensor: <class 'torch.Tensor'>
# 変換後のデータの形状
new_np_arr shape: (10, 10)
new_tensor shape: torch.Size([10, 10])
# 変換後のデータの情報
new_np_arr dtype: float32
new_tensor device: cpu
new_tensor dtype: torch.float64
# 変換後のデータをGPU、torch.float32になっているかの確認
new_tensor device: cuda:0
new_tensor dtype: torch.float32
```

### Tensorの生成
大体numpyと同じです。numpyからの変換もこれでいいんじゃないのか？
最悪numpyで作ってTensorに変換すればいい説。
基本的に元のデータのコピーからTensorを生成します。データをTensorに置き換えたいなら`requires_grad=True`するとできます。(defaultはFalse)

```python
# リストから生成
tensor=torch.tensor([[1,2,3],[4,5,6]], device=device, dtype=dtype)
print("liset:\n", tensor,"\n")

#numpyから生成
np_tensor=torch.tensor(np.array([[1, 2, 3], [4, 5, 6]]) , device=device, dtype=dtype)
print("np:\n",np_tensor,"\n")

#ゼロ行列の作成
zero_tensor=torch.zeros([2,2], device=device, dtype=dtype)
print("zero:\n",zero_tensor,"\n")

#一行列？
one_tensor=torch.ones([2,2], device=device, dtype=dtype)
print("one:\n",one_tensor,"\n")

#空行列
empty_tensor=torch.empty([2,2] , device=device, dtype=dtype)
print("empty:\n",empty_tensor,"\n")

#ランダム(正規分布)
randn_tensor=torch.randn([2,2], device=device, dtype=dtype)
print("randn:\n",randn_tensor,"\n")

#ランダム（一様分布）
rand_tensor=torch.rand([2,2], device=device, dtype=dtype)
print("rand:\n",rand_tensor,"\n")

#arange
arange_tensor=torch.arange(4, device=device, dtype=dtype)
print("arange:\n",arange_tensor,"\n")

#reshape
reshape_tensor=torch.reshape(arange_tensor, (2,2))
print("rehape:\n",reshape_tensor,"\n")
```

```
list:
 tensor([[1., 2., 3.],
        [4., 5., 6.]], device='cuda:0') 

np:
 tensor([[1., 2., 3.],
        [4., 5., 6.]], device='cuda:0') 

zero:
 tensor([[0., 0.],
        [0., 0.]], device='cuda:0') 

one:
 tensor([[1., 1.],
        [1., 1.]], device='cuda:0') 

empty:
 tensor([[1., 1.],
        [1., 1.]], device='cuda:0') 

randn:
 tensor([[-0.4707,  0.2586],
        [-2.1400, -0.7504]], device='cuda:0') 

rand:
 tensor([[0.6047, 0.8802],
        [0.5218, 0.9891]], device='cuda:0') 

arange:
 tensor([0., 1., 2., 3.], device='cuda:0') 

rehape:
 tensor([[0., 1.],
        [2., 3.]], device='cuda:0') 
```

## Tensorの演算
よく使いそうなものを

|methods||
|---|---|
|torch.abs()|絶対値|
|torch.mean()|平均値|
|torch.std()|偏差値|
|Tensor.t()|Tensorの転置行列|
|torch.cat((x1,x2),dim=1)|dim=1でx1,x2を結合|
|Tensor.transpose()|numpyのtransposeと同じ|
|dot|vectorの内積|
|mv|matrixとvectorの積|
|mm|matrixの積|
|matmul|matrixとvectorをいい感じに計算|
|gesv|LU分解による連立方程式の解|
|eig|固有値分解|
|symeig|対称行列の時の固有値分解|
|svd|特異値分解|
|x.backward()|自動微分|

##結論
大体numpyって思っとけば使えそう, cpuとgpuたまにエラー吐く原因になるので注意するかdeviceを毎回ちゃんと指定しておくと良さそう。