1. 目的
2. 前準備
3. 静的グラフと動的グラフ
4. パッケージのインポート
5. 使用するデータとプレースフォルダplaceholders
の用意
6. 重み付けの初期化
7. データ入力 (Forward pass)
8. 損失の計算
9. 重み(Weight)の勾配計算
10. 重み(w)の更新
11. 計算グラフの実行
12. 実行
12.1. 5_static_graphs.py
1. 目的
Deep Learning(ディープラーニング)で、よく使われるTensorFlowを使って計算グラフの設計からニューラルネットワークの学習をする。
PyTorchが採用している動的グラフを理解するために、静的グラフの代表であるTensorFlowに触れる。
2. 前準備
PyTorchのインストールはこちらから。
初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。
3. 静的グラフと動的グラフ
TensorFlowとPyTorchの大きな違いは、TensorFlowが静的計算グラフ(define-by-run)を採用しているのに対し、PyTorchは動的計算グラフ(define-and-run)を使用している点です。
静的グラフと動的グラフの説明についてはこちらをご覧ください。
簡潔に言うと、静的グラフは、事前にネットワーク構造を決めてから学習・推定を実行します。
これに対し、動的グラフは、ネットワーク構造を定義する時点でネットワーク構造を固定する必要はなく、ネットワークに入力されるデータの様子をみて、ネットワークを構造を適宜変えることができます。故に、動的な計算グラフになります。
この動的グラフにより、柔軟な学習や推測が可能となります。
4. パッケージのインポート
TensorFlowとNumPyをimport
する。
import tensorflow as tf import numpy as np
5. 使用するデータとプレースフォルダ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))
6. 重み付けの初期化
乱数random_normal
メソッドを使って重みw
を初期化します。
w1 = tf.Variable(tf.random_normal((D_in, H))) w2 = tf.Variable(tf.random_normal((H, D_out)))
7. データ入力 (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)
8. 損失の計算
モデルが予測した値y_pred
と答えy
との間の二乗誤差を計算しこれを損失loss
とします。
tf.reduce_sum
は、テンソルの足し算総和
の計算をします。
loss = tf.reduce_sum((y - y_pred) *2.0)
9. 重み(Weight)の勾配計算
これより先は、モデルが予測した値y_pred
と答えy
を見比べて、正しく答えy
を予測できるようにモデルのパラメータを更新していきます。
具体的には、重みw1
, w2
の勾配(grad_w1
, grad_w2
)を計算します。
TensorFlowでは、gradients
メソッドで勾配が計算できます。
grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])
10. 重み(w)の更新
計算した勾配(grad_w1
, grad_w2
)をもとに、重み(w1
, w2
)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()
で対象のパラメータをくくることで計算できます。
確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。
weight = weight - learning_rate gradient
今回は、学習率を1e-6
、新しい重みw1
, w2
をnew_w1
とnew_w2
にして、SGDを用いて重みを更新します。
learning_rate = 1e-6 new_w1 = w1.assign(w1 - learning_rate grad_w1) new_w2 = w2.assign(w2 - learning_rate grad_w2)
11. 計算グラフの実行
設計した計算グラフをこのコード部分で実行する。
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:
重みw1
とw2
を初期化するため、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
、更新された重みw1
、w2
から損失値(二乗誤差)を計算する。
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)
12. 実行
以下のコードを5_static_graphs.py
として保存します。
12.1. 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