機械学習
MachineLearning
TensorFlow

TensorFlowのコード分割の考え方

More than 3 years have passed since last update.


概要

TensorFlowのコードをどう分割していけばいいのか、公式のTensorFlow Mechanics 101に基づき整理しました。

公式の例はネットワークがやや複雑で見通しが悪いので、minimum working exampleとして簡単な線形問題を取り扱います。


取り扱う問題

じゃんけんです。

入力はグー、チョキ、パーに対応する以下の1-hot labelsです。

input = [

[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]
]

教師ラベルは、入力に「勝つ」ラベルなので以下の通りです。

winning_hands = [

[0., 1., 0.],
[0., 0., 1.],
[1., 0., 0.]
]

別にニューラルネットワークで解かなくても、バイアスをゼロベクトル、重み行列を以下のように取れば出力が最適化されるのは自明ですが、極端に簡単なのでコードの見通しは良くなると思います。

W = 

\begin{bmatrix}
0 & 1 & 0 \\
0 & 0 & 1 \\
1 & 0 & 0
\end{bmatrix}


とりあえず適当に書く場合

以前の記事で紹介した内容とほぼ同じです。

import tensorflow as tf

input = [
[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]
]

winning_hands = [
[0., 1., 0.],
[0., 0., 1.],
[1., 0., 0.]
]

x = tf.placeholder("float", [None, 3])

W = tf.Variable(tf.zeros([3, 3]))
b = tf.Variable(tf.zeros([3]))

y = tf.nn.softmax(tf.matmul(x, W) + b)

y_ = tf.placeholder("float", [None, 3])
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

init = tf.initialize_all_variables()
feed_dict={x: input, y_: winning_hands}

with tf.Session() as sess:
sess.run(init)

for step in range(1000):
sess.run(train_step, feed_dict=feed_dict)
if step % 100 == 0:
print sess.run(cross_entropy, feed_dict=feed_dict)

これだけ簡単な例ならこのままでも受け入れられるような気がしますが、TensorBoardを活用して学習過程やデータフローを可視化しようとするとコードが膨らみ、すぐに見通しが悪くなります。

そこで登場するのがInference, Loss, Trainingの概念です。


Inference, Loss, Training

公式で推奨されているコード分割は以下の通りです。


  • Inference : 「推論」に該当する部分。入力から、分類結果や回帰結果を予想する部分。

  • Loss : 「目標値との誤差」に該当する部分。最適化したい値。

  • Training: 最適化のアルゴリズム。

この考え方に基づきコードを分割すると、以下のようになります。

import tensorflow as tf

input = [
[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]
]

winning_hands = [
[0., 1., 0.],
[0., 0., 1.],
[1., 0., 0.]
]

def inference(input_placeholder):
W = tf.Variable(tf.zeros([3, 3]))
b = tf.Variable(tf.zeros([3]))

y = tf.nn.softmax(tf.matmul(input_placeholder, W) + b)
return y

def loss(output, supervisor_labels_placeholder):
cross_entropy = -tf.reduce_sum(supervisor_labels_placeholder * tf.log(output))
return cross_entropy

def training(loss):
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
return train_step

supervisor_labels_placeholder = tf.placeholder("float", [None, 3])
input_placeholder = tf.placeholder("float", [None, 3])
feed_dict={input_placeholder: input, supervisor_labels_placeholder: winning_hands}

with tf.Session() as sess:
output = inference(input_placeholder)
loss = loss(output, supervisor_labels_placeholder)
training_op = training(loss)

init = tf.initialize_all_variables()
sess.run(init)

for step in range(1000):
sess.run(training_op, feed_dict=feed_dict)
if step % 100 == 0:
print sess.run(loss, feed_dict=feed_dict)


実行結果

どちらも同じロジックなので同じ結果となります。クロスエントロピー(loss)が最適化されていく様子が確認できます。

$ python main.py

3.27587
1.85469
1.17299
0.821553
0.619587
0.492207
0.40589
0.3441
0.297949
0.2623


Tips



  • initはすべての変数を定義した後にしないとエラーがでます。

  • 動いてるかよくわからなかったら、とりあえずprint sess.run(variable)でprintf debugしました(もっと良いデバッグをご存知の方いらっしゃいましたら教えてください)


今後の展望

更にこの例を拡張し、以下の内容の記事を書こうと思います。


  • Summaryを使って、TensorBoardでLossを確認できるようにする

  • Graphを使って、TensorBoardでデータフローを描画できるようにする