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