【初心者向け】基礎&実践プログラミング

初心者がつまづきやすいところ、最短で実力が身につく方法をお伝えします。

【PyTorch】サンプル② 〜TENSOR (テンソル) 〜

f:id:AIProgrammer:20200413020244p:plain

目的

PyTorchのチュートリアルPyTorch: Tensorsを参考にPyTorch Tensor(テンソル)を使って、損失(loss)や重み(weight)の計算をする。

PyTorchの特徴の一つである、テンソルとNumPyの違いを手を動かしながら感じる。

NumPyを用いた例は、こちらから。

前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

コマンドラインの「>>>」の行がPythonで実行するコマンドです。 それ以外の行は、コマンドの実行結果です。

>>> print("PyTorch")    # コマンドラインに打ち込む
PyTorch    # 出力結果

関連記事

チュートリアル

サンプル

TENSOR(テンソル)

PyTorchのtensor(テンソル)は、基本的にはnumpy配列と同じです。

しかし、PyTorchのテンソルは、deep learning(深層学習)や計算グラフ、勾配といった概念に合わせた配列です。

PyTorchテンソルだけがCPUとGPUのどちらでも計算が実行でき、これが、numpy配列とPyTorchテンソルの最も大きな違いです。

PyTorchのインポート

import torch

データタイプとデバイスの選択

これから作成するテンソルのデータ型dtypefloatにします。

テンソル計算をするデバイスは、torch.deviceで指定することができます。 今回は、CPUを使用することにします。

dtype = torch.float
device = torch.device("cpu")

GPUを使う方は、deviceの部分を以下のコードに変えて下さい。

device = torch.device("cuda:0") # Uncomment this to run on GPU

使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

PyTorchテンソルを定義する場合には、どのデバイスでdevice、どのデータ型dtypeかを指定することができます。

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

重み付けの初期化

乱数を使って重みを初期化します。

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

データの入力

二次元テンソルの入力(x)と重み(w1)をmmで掛け算することで重み付けをします(h)。(一次元テンソルの積は、dotで計算できます。)

重み付けした値(h)の要素から、clamp(min=~0)で、0以上のものは残し、0以下のものは0とします。numpyでは、numpy.maximum(h,0)と書きます。

最後に、重み(w2)を掛け合わせて重み付けしますmm(w2)。 この値がパーセプトロンの予測値(y_pred)となります。

    # Forward pass: compute predicted y
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。 numpyでは、二乗計算をnumpy.squareで実行していましたが、PyTorchテンソルでの二乗計算はpow(2)と記述します。

損失の値を、tensor配列からスカラー値で取得したい場合には、item()メソッドを用います。

各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

今回は、結果を見やすくするためにif t % 100 == 99:の部分で、学習回数100回ごとに結果を出力します。 やっていることは、学習回数(t)を100で割ったときのあまりが99であるときにprint(t, loss)を実行です。

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

まず、重み(w1, w2)の勾配(grad_w1, grad_w2)を計算します。

numpyと違う点は、表のとおりです。

PyTorch NumPy
配列の転置 .t() .T
二次元配列の掛け算 .mm() .dot()
配列のコピー .clone() .copy()
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate * gradient

SGDは、以下のコードで実行できます。 ここの部分は、numpyもPyTorchテンソルも同じコードです。

    # Update weights using gradient descent
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

実行

以下のコードを2_tensor.pyとして保存します。

2_tensor.py

import torch


dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

    # Update weights using gradient descent
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 2_tensor.py 
99 306.79400634765625
199 0.4559670686721802
299 0.0014809096464887261
399 6.634114834014326e-05
499 1.7021899111568928e-05



頑張れ!喝!!の代わりにB!ブックマークを押していただけるとただただうれしいです(^^)! ↓