というわけで、Tensorflow 2で痛い目に遭ったので、PyTorchはじめました。ひとまずDEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZから見てみましょう。(以下翻訳です)
PyTorchとは ?
PyTorchはPython baseの数理計算packageで大きく二つの目的があります:
- GPUやその他acceleratorを使うためにnumpyを置き換える
- Neural networkの実装に有益なautomatic differentiation(自動微分)libraryを提供する
このTutorialのgoal
- PyTorchのTensor libraryとneural networkの概要を理解する
- 小さなneural networkで画像分類してみる
Note: torchとtorchvision libraryをinstallしておいてね
Tensor
Tensorはある特別なdata構造のことで、data arrayや行列にとても似ています。PyTorchでは入力情報をencodeしmodelそのものや内部parameterを出力するためにtensorを使います。
TensorはNumPyのndarrayに似ていますが、高速演算のためにGPUや特別なhardware上で動作するものです。なので、ndarrayに慣れていればTensor APIは難しくありません。慣れていなくてもこのquick API walkthroughに沿って勉強すれば大丈夫です。
import torch
import numpy as np
Tensorの設定
Tensorの初期化にはいろいろなやり方があります。次の例を見てみましょう:
Dataから直接設定する方法
Tensorはdataから直接つくることができます。Data typeは自動的に設定されます。
data = [[1, 2],[3, 4]]
x_data = torch.tensor(data)
NumPy arrayから設定する方法
TensorはNumPy arrayからも作れます。(逆もまた然りです – Bridge with NumPy参照)
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
別のtensorから設定する方法
明示的に上書きしない限り、新しいtensorは元のtensorのproperty (shape, datatype) を維持します。
x_ones = torch.ones_like(x_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")
Out:
Ones Tensor:
tensor([[1, 1],
[1, 1]])
Random Tensor:
tensor([[0.2839, 0.9382],
[0.6150, 0.2890]])
乱数や定数を設定する方法
shape
はtensorの次元のtuple表現です。下の関数では出力tensorの次元を定義します。
shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
Out:
Random Tensor:
tensor([[0.0647, 0.1161, 0.8277],
[0.4194, 0.9457, 0.5271]])
Ones Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
Zeros Tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])
Tensor属性
Tensor属性はtensorのshape, datatype, 配置されるdevice情報から構成されます。
tensor = torch.rand(3,4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
Out:
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu
Tensor演算
転置, indexing, slicing, 数値演算, 線形代数, random samplingなど100以上のtensor演算についてここで網羅的に説明されています。
これらはGPUで(普通はCPUよりも高速に)動作します。もしColabを使っていたら、Notebookの設定でGPUにallocateしてみて下さい。
# We move our tensor to the GPU if available
if torch.cuda.is_available():
tensor = tensor.to('cuda')
Tensor演算を試してみましょう。NumPyに慣れていればTensor APIはとてもたやすいと思います。
標準numpy-likeなindexingとslicing
tensor = torch.ones(4, 4)
tensor[:,1] = 0
print(tensor)
Out:
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
Tensorの継ぎ足し
torch.cat
を使うと、一連のtensorをつないで所定の次元のtensorをつくることができます。torch.stackでも類似の操作が可能です。
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)
Out:
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])
Tensorの掛け算
# This computes the element-wise product(要素ごとの積)
print(f"tensor.mul(tensor) \n {tensor.mul(tensor)} \n")
# Alternative syntax:
print(f"tensor * tensor \n {tensor * tensor}")
Out:
tensor.mul(tensor)
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
tensor * tensor
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
二つのTensorで通常の行列の掛け算を行うには以下のようにします。
print(f"tensor.matmul(tensor.T) \n {tensor.matmul(tensor.T)} \n")
# Alternative syntax:
print(f"tensor @ tensor.T \n {tensor @ tensor.T}")
Out:
tensor.matmul(tensor.T)
tensor([[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]])
tensor @ tensor.T
tensor([[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]])
In-place演算
example: x.copy_(y)
, x.t_()
のような_
suffix付きの演算をin-place演算と呼びます。
print(tensor, "\n")
tensor.add_(5)
print(tensor)
Out:
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
tensor([[6., 5., 6., 6.],
[6., 5., 6., 6.],
[6., 5., 6., 6.],
[6., 5., 6., 6.]])
Note: In-place演算はmemoryの節約になるが、すぐに履歴が消えてしまうため導関数の計算で問題が起きることがあるので、使用の際は注意が必要です。
NumPyとのBridge
CPU上のtensorとNumPy arrayはmemoryを共有でき、一方が変化するともう一報も変化します。
TensorからNumPy array
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
Out:
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]
Tensorの変化がNumPy arrayに反映されます。
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
Out:
t: tensor([2., 2., 2., 2., 2.])
n: [2. 2. 2. 2. 2.]
NumPy arrayからTensor
n = np.ones(5)
t = torch.from_numpy(n)
NumPy arrayの変化がtensorに反映されます。
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
Out:
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]
全scriptの計算時間: ( 0 minutes 5.950 seconds)
さいごに
どうでしたか ? とても簡単だったかも知れませんが、基本は大切なので引き続きTutorialを勉強して行きましょう !