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

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

【PyTorch】サンプル⑤ 〜Static Graphs(静的グラフ)〜

f:id:AIProgrammer:20200413020244p:plain

目的

Deep Learning(ディープラーニング)で、よく使われるTensorFlowを使って計算グラフの設計からニューラルネットワークの学習をする。

PyTorchが採用している動的グラフを理解するために、静的グラフの代表であるTensorFlowに触れる。

前準備

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

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

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

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

これまで、勉強してきた内容は以下。

チュートリアル

サンプル

静的グラフ動的グラフ

TensorFlowとPyTorchの大きな違いは、TensorFlowが静的計算グラフ(define-by-run)を採用しているのに対し、PyTorchは動的計算グラフ(define-and-run)を使用している点です。

静的グラフと動的グラフの説明についてはこちらをご覧ください。

簡潔に言うと、静的グラフは、事前にネットワーク構造を決めてから学習・推定を実行します。

これに対し、動的グラフは、ネットワーク構造を定義する時点でネットワーク構造を固定する必要はなく、ネットワークに入力されるデータの様子をみて、ネットワークを構造を適宜変えることができます。故に、動的な計算グラフになります。

この動的グラフにより、柔軟な学習や推測が可能となります。

パッケージのインポート

TensorFlowとNumPyをimportする。

import tensorflow as tf
import numpy as np

使用するデータとプレースフォルダplaceholdersの用意

バッチサイズ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の入れ物tf.placeholdersを用意する。

x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))

重み付けの初期化

乱数random_normalメソッドを使って重みwを初期化します。

w1 = tf.Variable(tf.random_normal((D_in, H)))
w2 = tf.Variable(tf.random_normal((H, D_out)))

データ入力 (Forward pass)

入力xと重みw1を掛け算matmulすることで重み付けをしますh

重み付けした値(h)の要素から、maximum(h,tf.zeros(1))で、0以上のものは残し、0以下のものは0とします。

最後に、重みw2を掛け合わせてmatmul重み付けします。この値がモデルの予測値y_predとなります。

h = tf.matmul(x, w1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, w2)

損失の計算

モデルが予測した値y_predと答えyとの間の二乗誤差を計算しこれを損失lossとします。

tf.reduce_sumは、テンソルの足し算総和の計算をします。

loss = tf.reduce_sum((y - y_pred) ** 2.0)

重み(Weight)の勾配計算

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

具体的には、重みw1, w2の勾配(grad_w1, grad_w2)を計算します。

TensorFlowでは、gradientsメソッドで勾配が計算できます。

grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])

重み(w)の更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()で対象のパラメータをくくることで計算できます。

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

weight = weight - learning_rate * gradient

今回は、学習率を1e-6、新しい重みw1, w2new_w1new_w2にして、SGDを用いて重みを更新します。

learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate * grad_w1)
new_w2 = w2.assign(w2 - learning_rate * grad_w2)

計算グラフの実行

設計した計算グラフをこのコード部分で実行する。

with tf.Session() as sess:
    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)
    for t in range(500):
        # Execute the graph many times.
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

TensorFlowで、計算グラフを実行するには、with tf.Session() as sess:でSessionオブジェクトを作成する。

with tf.Session() as sess:

重みw1w2を初期化するため、Sessionをrunする。

    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

ニューラルネットワークに入力するデータx`_valueとニューラルネットワークが予測すべき値y_valueを乱数np.random.randnで決める。

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)

今回は、学習回数を500回とする。

各学習ごとに、損失関数loss、更新された重みw1w2から損失値(二乗誤差)を計算する。

100回学習するごとに、学習回数tと二乗誤差loss_valueを出力する。

    for t in range(500):
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

実行

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

5_static_graphs.py

import tensorflow as tf
import numpy as np

# First we set up the computational graph:

# 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 placeholders for the input and target data; these will be filled
# with real data when we execute the graph.
x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))

# Create Variables for the weights and initialize them with random data.
# A TensorFlow Variable persists its value across executions of the graph.
w1 = tf.Variable(tf.random_normal((D_in, H)))
w2 = tf.Variable(tf.random_normal((H, D_out)))

# Forward pass
h = tf.matmul(x, w1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, w2)

# Compute loss using operations on TensorFlow Tensors
loss = tf.reduce_sum((y - y_pred) ** 2.0)

# Compute gradient of the loss with respect to w1 and w2.
grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])

# Update the weights using gradient descent.
learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate * grad_w1)
new_w2 = w2.assign(w2 - learning_rate * grad_w2)

# actually execute the graph.
with tf.Session() as sess:
    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)
    for t in range(500):
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

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

左の数字が学習回数、右の数値がパーモデルの推定値と実際の答えと二乗誤差です。

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

$ python3 5_static_graphs.py
99 1597.0376
199 16.325699
299 0.24026018
399 0.0046504317
499 0.00029210464



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