LoginSignup
3
4

More than 3 years have passed since last update.

PyTorchの書籍が分かりにくかったので補足してみた

Last updated at Posted at 2020-06-12

どうしてこの記事を書いたの?

PyTorchについて勉強しようと思い、この本を買ってみました。

いくつか見比べてみて、初学者向けのものを選んだのですが、、、よくわからん!!!となりました。
出力を書いていないから何が出力されて、どういう観点を確認すればわからないんです!!

なので、おそらく言わんとしていることはこうだろうという補足をまとめてみました。
実行環境はGoogle Colaboratoryとしています。

下準備として、PyTorchをインストールします。

IN
import torch

PyTorchではGPUを使用します。GPUが使用可能かどうか確認してみましょう。

IN
# GPUが使えているかどうか確認する
print(torch.cuda.is_available())
OUT
True

Tensorって何?

書籍によるとTensorとは、

  • 多次元配列を扱うためのデータ構造である
  • Numpyのndarrayとほぼ同様のAPIを有する
  • GPU上での計算もサポートする

だそうです。

要するにPyTorchの多次元配列です。
書籍ではごたごたとデータ型云々の説明とサンプルコードがありましたが、定義だけして全く中身を確認していません
なので、データの確認をしていきます

IN
# 入れ子のlistを渡して作成する
t = torch.tensor([[1, 2], [3, 4.]])
print("dtype: {}\ndevice: {}".format(t.dtype, t.device))
t
OUT
dtype: torch.float32
device: cpu
tensor([[1., 2.],
        [3., 4.]])

dtypeはデータ型、deviceはTensorがCPU / GPUどちらにあるかです。
GPU上にTensorを作ってみます。

IN
t = torch.tensor([[1, 2], [2, 4.]], device="cuda:0")
print("dtype: {}\ndevice: {}".format(t.dtype, t.device))
t
OUT
dtype: torch.float32
device: cuda:0
tensor([[1., 2.],
        [2., 4.]], device='cuda:0')

torch.cuda.FloatTensorでもGPU上にTensorを作ることができるようです。

IN
t = torch.cuda.FloatTensor([[1, 2], [2, 4.]])
print("dtype: {}\ndevice: {}".format(t.dtype, t.device))
t
dtype: torch.float32
device: cuda:0
tensor([[1., 2.],
        [2., 4.]], device='cuda:0')

ちなみに、cudaはWikipediaによると、

CUDA(Compute Unified Device Architecture:クーダ)とは、NVIDIAが開発・提供している、GPU向けの汎用並列コンピューティングプラットフォーム(並列コンピューティングアーキテクチャ)およびプログラミングモデルである

だそうです。

今度は、データ型を変えてみます。

IN
# 64bitのデータ型を指定する
t = torch.tensor([[1, 2], [2, 4.]], dtype=torch.float64)
print("dtype: {}\ndevice: {}".format(t.dtype, t.device))
t
OUT
dtype: torch.float64
device: cpu
tensor([[1., 2.],
        [2., 4.]], dtype=torch.float64)

64bitの符号付整数はtorch.LongTensorでも定義できます。

IN
t = torch.LongTensor([[1, 2], [2, 4.]])
print("dtype: {}\ndevice: {}".format(t.dtype, t.device))
t
OUT
dtype: torch.int64
device: cpu
tensor([[1, 2],
        [2, 4]])

データ型についてはこちらのドキュメントでまとまっていました。
https://pytorch.org/docs/stable/tensors.html

CPU上に作成したTensorをGPUへ転送してみます。

IN
t = torch.zeros(100, 10).to("cuda:0")
print("dtype: {}\ndevice: {}".format(t.dtype, t.device))
t[:2] # Tensorでもスライスが利用できます
OUT
dtype: torch.float32
device: cuda:0
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], device='cuda:0')

Tensorのshapeを確認する場合は、shape関数ではなく、size関数を使用します
この点はNumPyと違います。

IN
# shapeはsizeで取得する
# t.shape() # コンパイルエラー
t.size()
OUT
torch.Size([100, 10])

TensorをNumpyのndarrayに変換するにはnumpy関数を使用します。

IN
t = torch.tensor([[1, 2], [3, 4.]])
x = t.numpy()
print("type: {}".format(type(x)))
x
OUT
type: <class 'numpy.ndarray'> 
array([[1., 2.],
       [3., 4.]], dtype=float32)

GPU上のTensorはCPUへ変換する必要があります。

IN
t = torch.cuda.FloatTensor([[1, 2], [3, 4.]])
x = t.to("cpu").numpy()
print("type: {}".format(type(x)))
x
OUT
type: <class 'numpy.ndarray'>
array([[1., 2.],
       [3., 4.]], dtype=float32)

Tensorの変換

Tensorを変換する方法のうち、紛らわしいものをやってみます。

Numpyで言うところのreshapeはview関数で実行します。

IN
# 2 * 2を4 * 1に変更する
# viewはndarrayのreshapeと同様
x1 = torch.tensor([[1, 2], [3, 4.]])
x1.view(4, 1)
OUT
tensor([[1.],
        [2.],
        [3.],
        [4.]])

転置の書き方はいくつかあります。

IN
x2 = torch.tensor([[10, 20, 30], [40, 50, 60.]])
x2.T
OUT
tensor([[10., 40.],
        [20., 50.],
        [30., 60.]])
IN
x2.t()
OUT
tensor([[10., 40.],
        [20., 50.],
        [30., 60.]])

transposeも転置を行うことができますが、転置以外に画像データのデータ形式をHWC(縦、横、色)からCHW(色、横、縦)に並べ替える際にも利用できるそうです。

IN
hwc_img_data = torch.rand(1, 5, 4, 3)
hwc_img_data
OUT
tensor([[[[0.9248, 0.7545, 0.5603],
          [0.5339, 0.6627, 0.7652],
          [0.7082, 0.5146, 0.9273],
          [0.8437, 0.7064, 0.1053]],

         [[0.2080, 0.8018, 0.6833],
          [0.5892, 0.9264, 0.9315],
          [0.0872, 0.1898, 0.5745],
          [0.2192, 0.1187, 0.7537]],

         [[0.9680, 0.9239, 0.8698],
          [0.2339, 0.9918, 0.3446],
          [0.6669, 0.4148, 0.2037],
          [0.1055, 0.0353, 0.3679]],

         [[0.7079, 0.4069, 0.1181],
          [0.1983, 0.0452, 0.5788],
          [0.6378, 0.7050, 0.1148],           
          [0.3960, 0.1924, 0.2714]],

         [[0.3127, 0.1320, 0.7232],
          [0.3484, 0.7617, 0.4725],
          [0.4863, 0.9178, 0.3092],
          [0.6279, 0.4259, 0.3828]]]])

自分は1 * 5 * 4 * 3から 1 * 4 * 5 * 3に変わる(5と4が入れ替わる)と考えると何となく理解できました。

IN
# 1 * 5 * 4 * 3から 1 * 4 * 5 * 3に変わる
hwc_img_data.transpose(1, 2)
OUT
tensor([[[[0.9248, 0.7545, 0.5603],
          [0.2080, 0.8018, 0.6833],
          [0.9680, 0.9239, 0.8698],
          [0.7079, 0.4069, 0.1181],
          [0.3127, 0.1320, 0.7232]],

         [[0.5339, 0.6627, 0.7652],
          [0.5892, 0.9264, 0.9315],
          [0.2339, 0.9918, 0.3446],
          [0.1983, 0.0452, 0.5788],
          [0.3484, 0.7617, 0.4725]],

         [[0.7082, 0.5146, 0.9273],
          [0.0872, 0.1898, 0.5745],
          [0.6669, 0.4148, 0.2037],
          [0.6378, 0.7050, 0.1148],
          [0.4863, 0.9178, 0.3092]],

         [[0.8437, 0.7064, 0.1053],
          [0.2192, 0.1187, 0.7537],
          [0.1055, 0.0353, 0.3679],
          [0.3960, 0.1924, 0.2714],
          [0.6279, 0.4259, 0.3828]]]])
IN
# さらに1 * 4 * 5 * 3 から1 * 3 * 4 * 5に変わる
# これでhwcからcwhへ変換できる
hwc_img_data.transpose(1, 2).transpose(1, 3)
OUT
tensor([[[[0.9248, 0.5339, 0.7082, 0.8437],
          [0.2080, 0.5892, 0.0872, 0.2192],
          [0.9680, 0.2339, 0.6669, 0.1055],
          [0.7079, 0.1983, 0.6378, 0.3960],
          [0.3127, 0.3484, 0.4863, 0.6279]],

         [[0.7545, 0.6627, 0.5146, 0.7064],
          [0.8018, 0.9264, 0.1898, 0.1187],
          [0.9239, 0.9918, 0.4148, 0.0353],
          [0.4069, 0.0452, 0.7050, 0.1924],
          [0.1320, 0.7617, 0.9178, 0.4259]],

         [[0.5603, 0.7652, 0.9273, 0.1053],
          [0.6833, 0.9315, 0.5745, 0.7537],
          [0.8698, 0.3446, 0.2037, 0.3679],
          [0.1181, 0.5788, 0.1148, 0.2714],
          [0.7232, 0.4725, 0.3092, 0.3828]]]])

Tensorと自動微分

requires_grad属性をTrueにすると自動微分を行います。
書籍ではどこで何が出現するのか詳しく書いていないので、補足をしています。

IN
x = torch.randn(100, 3)
a = torch.tensor([1, 2, 3.], requires_grad=True)

y = torch.mv(x, a)
print(y.grad_fn) # 勾配を計算するための計算グラフが格納されていることを確認する

o = y.sum()

# 勾配を獲得する
# backwardを実行する変数はスカラーである必要がある
o.backward()

# 自動微分が実行されて、a.gradに勾配が獲得される
print(a.grad)

a.grad != x.sum(0)
OUT
<MvBackward object at 0x7f9c7998e3c8>
tensor([ 2.5016,  6.3925, -6.3674])
tensor([False, False, False])
3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4