4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PyTorchの基礎 (1) -Tensorの使い方-

Last updated at Posted at 2020-04-23

PyTorch とは

概要

Python のオープンソースの機械学習ライブラリ.
PyTorch は Tensortorch.Tensor)と呼ばれるクラスを定義しており,多次元配列の保存と演算に利用している.Numpy の配列 Array に似ているが,CUDA が有効な Nvidia の GPU 上での演算も可能になっている.
[参照元] --> PyTorch - Wikipedia

他のライブラリとの違い

機械学習ライブラリは,Define by RunDefine and Run の2種類に大別される.

  • Define by Run
    実行しながらネットワークを定義する.
    ネットワークを動的に変更できるため,柔軟な設計が可能である.
    たとえば,データのサイズによってネットワークを切り替えたり,イテレーションごとに設計変更したりすることも可能.
    有名なライブラリは,PyTorch や Chainer など.

  • Define and Run
    先にネットワークを定義してから実行する.
    レゴブロックのようにパーツを組み合わせるだけで簡単にネットワークを構成できる.
    簡潔でわかりやすい.
    有名なライブラリは,Keras や Tensorflow など.

PyTorch は,実行しながらネットワークを構築する Define by Run の機械学習ライブラリに属する.
このような違いがあるため,これらのライブラリは適材適所で使うのがよさそう.
通常のデータ分析業務では Define and Run の Keras が簡潔であり,詳細な設計が必要な研究や困難なタスクでは Define by Run の PyTorch が優位か.

[参照元] --> 『PyTorch入門』使い方&Tensorflow, Keras等との違いとは? - プロクラシスト

PyTorch と Chainer

PyTorch と Chainer の最大の違いは,PyTorch が海外のコミュニティで盛んに使用されるのに対して,Chainer は日本が主流であること.これは,Chainer が日本発の Preferred Networks (PFN)という会社が開発したことに起因する.
しかしながら,2019年12月に PFN が Chainer のメジャーアップデートを終了し PyTorch の研究開発に移行すると発表したことで,2つのライブラリの関係性は大きく一変した.詳しくはこちら
そのため,今後 Define by Run の機械学習ライブラリを使用する場合は PyTorch を選択するのが無難だろう.

PyTorch 入門

PyTorch の公式チュートリアルを引用しながら,PyTorch の仕様を確認する.

Tensor の使い方(前編:定義・演算)

What is PyTorch? -- PyTorch Tutorials 1.4.0 documentation

PyTorch で使用する Tensor は Numpy のndarrayに似ているが,Tensorは計算の高速化のために GPU を用いて計算できる.
以下では,Numpy と比較しつつ PyTorch のTensorの使い方をまとめる.

ライブラリのインポート

import torch
import numpy as np

配列の定義

全ての要素がゼロの配列.

# Tensor
x_t = torch.zeros(2, 3)
# Numpy
x_n = np.zeros((2,3))

print('Tensor:\n',x_t,'\n')
print('Numpy:\n',x_n)

# ---Output---
#Tensor:
# tensor([[0., 0., 0.],
#        [0., 0., 0.]]) 
#Numpy:
# [[0. 0. 0.]
# [0. 0. 0.]]

全ての要素が1の配列.

# Tensor
x_t = torch.ones(2,3)
# Numpy
x_n = np.ones((2,3))

print('Tensor:\n',x_t,'\n')
print('Numpy:\n',x_n)

# ---Output---
#Tensor:
# tensor([[1., 1., 1.],
#        [1., 1., 1.]]) 
#Numpy:
# [[1. 1. 1.]
# [1. 1. 1.]]

要素の値を指定した配列.

# Tensor
x_t = torch.tensor([[5,3],[10,6]])
# Numpy
x_n = np.array([[5,3],[10,6]])

print('Tensor:\n',x_t,'\n')
print('Numpy:\n',x_n)

# ---Output---
#Tensor:
# tensor([[ 5,  3],
#        [10,  6]]) 
#Numpy:
# [[ 5  3]
# [10  6]]

要素の値を乱数で指定した配列.

# Tensor
x_t = torch.rand(2,3)
# Numpy
x_n = np.random.rand(2,3)

print('Tensor:\n',x_t,'\n',x12_t,'\n')
print('Numpy:\n',x_n,'\n',x12_n)

# ---Output---
#Tensor:
# tensor([[0.5266, 0.1276, 0.6704],
#        [0.0412, 0.5800, 0.0312]]) 
# tensor(0.3370) 
#Numpy:
# [[0.08877971 0.51718009 0.99738679]
# [0.35288525 0.68630145 0.73313903]] 
# 0.1799177580940461

配列の要素の取得

配列の各要素へのアクセスはx[0,1]のように行えばよい(これで,配列xの1行2列目の要素が取得できる.)

# Tensor
x12_t = x_t[0,1]
# Numpy
x12_n = x_n[0,1]

print('Tensor:\n',x12_t,'\n')
print('Numpy:\n',x12_n)

# ---Output---
#Tensor:
# tensor(0.1276) 
#Numpy:
# 0.5171800941956144

ここで注意が必要なのは,配列の要素を取得した際に,Numpy は数値を取得するが,PyTorch では数値ではなく Tensor を取得する点.そのため,PyTorch ではこのように抜き出した配列の要素をそのままスカラー量のように扱うことができない.Numpy と同様に数値を取り出す場合は,Tensor.item()を実行する必要がある.

x12_value = x12_t.item()
print(x12_t)
print(x12_value)

# ---Output---
# tensor(0.1276)
# 0.12760692834854126

四則演算

PyTorch でも Numpy と同じ感覚で四則演算を行うことができる.

# Tensor
x_t = torch.Tensor([1,2,3])
y_t = torch.Tensor([2,2,2])
add_t = x_t + y_t
sub_t = x_t - y_t
mul_t = x_t * y_t
div_t = x_t / y_t
print('Tensor:\nAddition:\n',add_t,'\nSubtraction:\n',sub_t,
'\nMultiplication:\n',mul_t,'\nDivision:\n',div_t,'\n')

# Numpy
x_n = np.array([1,2,3])
y_n = np.array([2,2,2])
add_n = x_n + y_n
sub_n = x_n - y_n
mul_n = x_n * y_n
div_n = x_n / y_n
print('Numpy:\nAddition:\n',add_n,'\nSubtraction:\n',sub_n,
'\nMultiplication:\n',mul_n,'\nDivision:\n',div_n)

# ---Output---
#Tensor:
#Addition:
# tensor([3., 4., 5.]) 
#Subtraction:
# tensor([-1.,  0.,  1.]) 
#Multiplication:
# tensor([2., 4., 6.]) 
#Division:
# tensor([0.5000, 1.0000, 1.5000]) 
#
#Numpy:
#Addition:
# [3 4 5] 
#Subtraction:
# [-1  0  1] 
#Multiplication:
# [2 4 6] 
#Division:
# [0.5 1.  1.5]

Tensor の使い方(後編:変換・自動微分)

Autograd: Automatic Differentiation -- PyTorch Tutorials 1.4.0 documentation

配列の形状操作

配列の形状情報(行数,列数)はshapeメソッドで取得可能.Numpy と同様の挙動を示す.

# Tensor
x_t = torch.rand(4,3)
row_t = x_t.shape[0]
column_t = x_t.shape[1]
print('Tensor:\n','row: ',row_t,'column: ',column_t)

# Numpy
x_n = np.random.rand(4,3)
row_n = x_n.shape[0]
column_n = x_n.shape[1]
print('Numpy:\n','row: ',row_n,'column: ',column_n)

# ---Output---
#Tensor:
# row:  4 column:  3
#Numpy:
# row:  4 column:  3

配列の形状を変更したい場合は,PyTorch では.view(),Numpy では.reshape()を使うことが多い.ただし,PyTorch の Tensor に対しても,Numpy と同様に.reshape()が使用できる.

# Tensor
x_t = torch.rand(4,3)
y_t = x_t.view(12)
z_t = x_t.view(2,-1)
print('Tensor:\n',x_t,'\n',y_t,'\n',z_t,'\n')

# Numpy
x_n = np.random.rand(4,3)
y_n = x_n.reshape(12)
z_n = x_n.reshape([2,-1])
print('Numpy:\n',x_n,'\n',y_n,'\n',z_n)

# ---Output---
#Tensor:
# tensor([[0.5357, 0.2716, 0.2651],
#        [0.6570, 0.0844, 0.9729],
#        [0.4436, 0.9271, 0.4013],
#        [0.8725, 0.2952, 0.1330]]) 
# tensor([0.5357, 0.2716, 0.2651, 0.6570, 0.0844, 0.9729, 0.4436, 0.9271, 0.4013,
#        0.8725, 0.2952, 0.1330]) 
# tensor([[0.5357, 0.2716, 0.2651, 0.6570, 0.0844, 0.9729],
#        [0.4436, 0.9271, 0.4013, 0.8725, 0.2952, 0.1330]]) 
#
#Numpy:
# [[0.02711389 0.24172801 0.01202486]
# [0.59552453 0.49906154 0.81377212]
# [0.24744639 0.58570244 0.26464142]
# [0.14519645 0.03607043 0.46616757]] 
# [0.02711389 0.24172801 0.01202486 0.59552453 0.49906154 0.81377212
# 0.24744639 0.58570244 0.26464142 0.14519645 0.03607043 0.46616757] 
# [[0.02711389 0.24172801 0.01202486 0.59552453 0.49906154 0.81377212]
# [0.24744639 0.58570244 0.26464142 0.14519645 0.03607043 0.46616757]]

.reshape()を使用した場合.

# Tensor
x_t = torch.rand(4,3)
y_t = x_t.reshape(2,-1)
#y_t = torch.reshape(x_t,[2,-1]) <-- Also works
print('Tensor:\n',y_t,'\n')

# Numpy
x_n = np.random.rand(4,3)
y_n = x_n.reshape(2,-1)
#y_n = np.reshape(x_n,[2,-1]) <-- Also works
print('Numpy:\n',y_n)

# ---Output---
#Tensor:
#tensor([[0.0617, 0.4898, 0.4745, 0.8218, 0.3760, 0.1556],
#        [0.3192, 0.5886, 0.8385, 0.5321, 0.9758, 0.8254]])
#
#Numpy:
#[[0.60080911 0.55132561 0.75930606 0.03275005 0.83148483 0.48780054]
# [0.10971541 0.02317271 0.22571149 0.95286975 0.93045979 0.82358474]]

配列の転置は,PyTorch では.transpose()または.t()で,Numpy では.transpose()または.Tで行う.

# Tensor
x_t = torch.rand(3,2)
xt_t = x_t.transpose(0,1)
#xt_t = torch.transpose(x_t,0,1)
#xt_t = x_t.t()
print('Tensor:\n',x_t,'\n',xt_t)

# Numpy
x_n = np.random.rand(3,2)
xt_n = x_n.transpose()
#xt_n = np.transpose(x_n)
#xt_n = x_n.T
print('Numpy:\n',x_n,'\n',xt_n)
# ---Output---
#Tensor:
# tensor([[0.8743, 0.8418],
#        [0.6551, 0.2240],
#        [0.9447, 0.2824]]) 
# tensor([[0.8743, 0.6551, 0.9447],
#        [0.8418, 0.2240, 0.2824]])
#Numpy:
# [[0.80380702 0.81511741]
# [0.29398279 0.78025418]
# [0.19421487 0.43054298]] 
# [[0.80380702 0.29398279 0.19421487]
# [0.81511741 0.78025418 0.43054298]]

Numpy との変換

  • Tensor --> ndarray

Tensorからndarrayに変換するときは,Tensor.numpy()とすればよい.
なお,変換後のndarrayは参照元のTensorの変更の影響を受けない.(ndarryaTensorのコピーになっている.)連動させる場合は,in-place operation(各関数の最後に_を付ける.例えばadd_().)を使用する必要がある.

a = torch.ones(5)
b = a.numpy()
a = a + 1
print('a = ',a)
print('b = ',b)

# ---Output---
# a =  tensor([2., 2., 2., 2., 2.])
# b =  [1. 1. 1. 1. 1.]

a = torch.ones(5)
b = a.numpy()
a.add_(1)
#torch.add(a,1,out=a) <-- Same operation
print('a = ',a)
print('b = ',b)

# ---Output---
# a =  tensor([2., 2., 2., 2., 2.])
# b =  [2. 2. 2. 2. 2.]
  • Tensor <-- ndarray

ndarrayからTensorに変換するときは,torch.from_numpy(ndarray)とすればよい.

a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print('a = ',a)
print('b = ',b)
# ---Output---
# a =  [2. 2. 2. 2. 2.]
# b =  tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

CUDA Tensor

Tensor.to()メソッドを使うことで,計算領域を移動させることができる.
これにより,Tensorを CPU メモリから GPU メモリへ移動し計算を実行することができる.

x = torch.rand(2,3)

if torch.cuda.is_available():
    device = torch.device("cuda")          # a CUDA device object
    y = torch.ones_like(x, device=device)  # directly create a tensor on GPU
    x = x.to(device)                       # or just use strings ``.to("cuda")``
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))       # ``.to`` can also change dtype together!

# ---Output---
#tensor([[1.1181, 1.1125, 1.3122],
#        [1.1282, 1.5595, 1.4443]], device='cuda:0')
#tensor([[1.1181, 1.1125, 1.3122],
#        [1.1282, 1.5595, 1.4443]], dtype=torch.float64)

自動微分

torch.Tensorrequires_grad属性をTrueにすることで,全ての計算履歴を追跡できるようになる.計算が終了した時にbackward()メソッドを呼び出すことで,自動的に全ての微分を実行してくれる.微分係数はgrad属性に格納される.
計算履歴の追跡を止めたいときはdetach()メソッドを呼び出すことで,その先の計算履歴の追跡から切り離される.

個々のTensorgrad_fn属性を持っている.この属性は,Tensorを作成するFunctionクラスを参照している.(厳密には,ユーザが定義したTensorgrad_fn属性を持っておらず,計算結果のTensorgrad_fn属性が付与される.)

x = torch.ones(2, 2, requires_grad=True)
print(x)
# ---Output---
#tensor([[1., 1.],
#        [1., 1.]], requires_grad=True)

y = x + 2
print(y)
# ---Output---
#tensor([[3., 3.],
#        [3., 3.]], grad_fn=<AddBackward0>)

print(x.grad_fn)
print(y.grad_fn)
# ---Output---
# None
# <AddBackward0 object at 0x7f2285d93940>]

z = y * y * 3
out = z.mean()
print(z)
print(out)
# ---Output---
#tensor([[27., 27.],
#        [27., 27.]], grad_fn=<MulBackward0>)
#tensor(27., grad_fn=<MeanBackward0>)

print(z.grad_fn)
# ---Output---
#<MulBackward0 object at 0x7f2285d93ac8>

out.backward()
print(x.grad)
# ---Output---
#tensor([[4.5000, 4.5000],
#        [4.5000, 4.5000]])

最終結果について,計算を実際に行ってみると,

out = \frac{1}{4} \sum_{i} z_i \\
z_i = y_i \cdot y_i \cdot 3 = 3 \cdot (x_i+2)^2

したがって,

\frac{\partial out}{\partial x_i} = \frac{1}{4} \cdot 3 \cdot 2 \cdot (x_i+2) = 4.5

となることが確かめられる.

まとめ

以下にこの記事の要点をまとめる.

  • PyTorch は Define by Run の機械学習ライブラリ.
  • 高速計算や自動微分が可能な torch.Tensor という配列を利用する.これは,Numpy の numpy.ndarayとほとんど同じような使い方(定義・演算)ができるほか,簡単に相互変換が行える.
  • torch.Tensorrequires_grad属性をTrueにすることで計算履歴が追跡可能になり,計算終了時にbackward()メソッドを呼び出すことで自動微分が行われる.これは,ニューラルネットワークの誤差逆伝播法によるパラメータ更新に大いに役立つ.
4
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
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?