pytorchでテンソルを作成する

参考にした本：

https://www.amazon.co.jp/PyTorch%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-Compass-Books%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Eli-Stevens-ebook/dp/B08V8HFCPZ/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=2ZLY9B4EFPTI2&keywords=pytorch+%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80&qid=1673922385&sprefix=pytorch+%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80%2Caps%2C201&sr=8-1


# テンソルとは
多次元配列のこと。k次元テンソルは、１つの値の位置を特定するのにk個のインデックスが必要になる。

# pytorchのテンソルのメリット
・GPUでの高速な計算が可能
・複数のデバイスに計算を分散できる
・計算グラフで計算過程を追跡できる。

# 準備
macのGPUを使えるようにする。最後にTrueが出れば、OK
```python
# m１のMACでGPUを使えるように設定する
pip install --pre torch torchvision --extra-index-url https://download.pytorch.org/whl/nightly/cpu

# m１のMACでGPUを使えるように設定する
conda install pytorch torchvision torchaudio -c pytorch

# m１のMACでGPUを使えるか確認する
torch.backends.mps.is_available()
```

import
```python
import torch
```

# テンソルの作成
画像のテンソルをイメージして作成する。
画像は縦横と色（RGB）の３種類の要素から構成されている。
画像の大きさを５✖️５とした場合のテンソルを作る。
```python
img = torch.randn(3,5,5)
img
```

weightsの作成
例えば、RGBの画像からグレースケールにするにあたって輝度を計算したい場合に、RGBに重みをかけることで求めることができる。
```python
weights = torch.tensor([0.2126,0.7152,0.0722])
weights
```

画像に重みを掛け合わせるために重みテンソルに影響のない次元を追加する
```python
unsqueezed_wights = weights.unsqueeze(-1).unsqueeze(-1)
weights.shape,unsqueezed_wights.shape
# 結果：(torch.Size([3]), torch.Size([3, 1, 1]))
```
各色を重みで調整する
```python
img_weights = img_t * unsqueezed_wights
img_weights
```
画像が１０枚の場合(つまりバッチサイズが１０の場合)
```python
img2 = torch.randn(10,3,5,5) # (バッチ、RGB、行、列)
img2
```
# テンソルのコピーの作り方
cloneを利用
```python
test = torch.tensor(list(range(9))).view(3,3)
test2 = test.clone()
test2
# 出力結果
# tensor([[0, 1, 2],
#         [3, 4, 5],
#         [6, 7, 8]])
```

cloneを利用する理由
以下のコードを実行するとわかる。
結果を見ると、test2だけではなく、testの値も書き換えられている。原因はtestとtest2がCPU上で同じストレージ（場所）を共有しているから。後ほど出てくる転置したテンソルも同じストレージを共有しているのでテンソルが書き換えると、他のサブテンソルも書き換えられるので注意。なぜこのような仕組みをtorchが採用しているかというと、１つのストレージで複数のテンソルを表現できるので効率良くCPUを使えるため。
```python
test = torch.tensor(list(range(9))).view(3,3)
test2 = test
test2[0,1] = 10
print(test2)
print(test)
```

# サイズ、オフセット、ストライド
各要素でテンソルを定義している。
サイズ：各次元の要素の大きさ。
```python:サイズ
exsample.shape
```
オフセット:CPU内にテンソルの元となる、１列の数値があるとき、確認したいテンソルの最初の値の位置はその数列のどこにあるかを示す。
```python:オフセット
c = torch.tensor([[10,4],[4,2],[9,3]])
b = c[2] #ここの値をいじると結果が変わる。
b.storage_offset()
```
ストライド：データがCPUに保存される場合は0,1,2,3,,,のように１次元のデータで保存される。具体的にストライドは(2, 1)のように表現され、前半の２は２進めば次の行まで行けることを表す。後半の１は次の列に進むために必要な移動を表す。
```python:ストライド
exsample.stride()
```
# テンソルに名前をつける
```python:名前をテンソル作成後に付け足す
img_named = img.refine_names(...,'channels','rows','columns') # ...,は省略の意味。今回の例で言えば前半部分に次元が何個あるかわからないが、後ろから３番目までに名前をつけるという意味
img_named.names # 次元の名前を確認する。ない場合はNoneと表記される。
```
```python:テンソル作成時に名前を追加する
weights_names = torch.tensor([0.2126,0.7152,0.0722], names=['channels'])
```

# テンソルの転置
２次元の場合
```python:
a = torch.ones(3,2)
a_t = a.transpose(0,1) # a.Tでもおk
a.shape, a_t.shape
```
３次元以上の場合。転置する次元を指定する必要がある
```python:
some_t = torch.ones(3,4,5)
transpose_t = some_t.transpose(0,1)
some_t.shape, transpose_t.shape
# 結果：(torch.Size([3, 4, 5]), torch.Size([4, 3, 5]))
```

# GPUにテンソルを置く
Macの場合、結果がTrueだと可能。
```python
torch.backends.mps.is_available()
```
```python:gpuを利用する
# 引数のデバイスにmps（macのGPU）を設定する
points_gpu = torch.tensor([[4,1],[5,3],[2,1]], device='mps')
# または、cpuで作成したテンソルをgpuに送る
points = torch.ones(2,3)
points_gpu = points.to(device='mps')
points_gpu
```

# 参考サイト
https://zenn.dev/hidetoshi/articles/20220731_pytorch-m1-macbook-gpu

https://pytorch.org/docs/stable/tensors.html

https://github.com/deep-learning-with-pytorch/ImageCaptioning.pytorch