TensorFlowは昨年Googleがリリースしたニューラルネットワークをデータフローグラフを使って手軽に開発できるようにした数値計算ライブラリです。 今回はそんなTensorFlowの数値計算と機械学習過程を可視化するTensorBoardを触ってみようと思います。

大規模で複雑なニューラルネットワークを研究・開発することは非常に大変です。複雑なデータフローと計算を理解しやすくし、開発サイクルを速めるためにGoogleはTensorBoardという可視化ツールを提供しています。

TensorBoardを実行し、可視化してみると以下のような図を描画することができます。これでTensorFlowグラフを視覚化することで、機械学習の収束状況などを直感的に把握することができ開発者の理解の手助けをしているのです。

インストール

TensorFlowをpipからインストールすると、同時にインストールされているはずです。

Macの場合は

$ pip install --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.9.0-py2-none-any.whl

Linuxの場合はこちらからマシンスペックに合わせてURLを調べてインストールしてください。

起動

TensorBoardの起動の仕方は以下のコマンドでできます。

$ tensorboard --logdir=/path/to/log-directory

ブラウザからlocalhost:6006を開くとTensorBoardのこんなGUIが表示されるはずです。

Hello TensorBoard

使い方

TensorFlowでFizzBuzzのコードを修正して試してみましょう。 このサンプルではFizzBuzzの結果を4クラスの分類問題と見て101~1023の数字の実行結果を畳み込みニューラルネットで学習させています。

import numpy as np
import tensorflow as tf

NUM_DIGITS = 10

def binary_encode(i, num_digits):
    return np.array([i >> d & 1 for d in range(num_digits)])

def fizz_buzz_encode(i):
    if i % 15 == 0:
        return np.array([0, 0, 0, 1])
    elif i % 5 == 0:
        return np.array([0, 0, 1, 0])
    elif i % 3 == 0:
        return np.array([0, 1, 0, 0])
    else:
        return np.array([1, 0, 0, 0])

trX = np.array([binary_encode(i, NUM_DIGITS) for i in range(101, 2 **
    NUM_DIGITS)])
trY = np.array([fizz_buzz_encode(i) for i in range(101, 2 ** NUM_DIGITS)])

def init_weights(shape):
    return tf.Variable(tf.random_normal(shape, stddev=0.01))

def model(X, w_h, w_o):
    h = tf.nn.relu(tf.matmul(X, w_h))
    return tf.matmul(h, w_o)

X = tf.placeholder("float", [None, NUM_DIGITS])
Y = tf.placeholder("float", [None, 4])

NUM_HIDDEN = 100

w_h = init_weights([NUM_DIGITS, NUM_HIDDEN])
w_o = init_weights([NUM_HIDDEN, 4])

py_x = model(X, w_h, w_o)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x, Y))
train_op = tf.train.GradientDescentOptimizer(0.05).minimize(cost)

predict_op = tf.argmax(py_x, 1)

def fizz_buzz(i, prediction):
    return [str(i), "fizz", "buzz", "fizzbuzz"][prediction]

BATCH_SIZE = 128

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

    for epoch in range(10000):
        p = np.random.permutation(range(len(trX)))
        trX, trY = trX[p], trY[p]

        for start in range(0, len(trX), BATCH_SIZE):
            end = start + BATCH_SIZE
            sess.run(train_op, feed_dict={X: trX[start:end], Y: trY[start:end]})

        if epoch % 100 == 0:
            print(epoch, np.mean(np.argmax(trY, axis=1) == sess.run(predict_op,
                feed_dict={X: trX, Y: trY})))

    numbers = np.arange(1, 101)
    teX = np.transpose(binary_encode(numbers, NUM_DIGITS))
    teY = sess.run(predict_op, feed_dict={X: teX})
    output = np.vectorize(fizz_buzz)(numbers, teY)

    print(output)

まずtf.initialize_all_variables()の後に

summary_op = tf.merge_all_summaries()
summary_writer = tf.train.SummaryWriter('fizzbuzz_data',
    graph = sess.graph)

として、出力を統合しネットワーク構成を保存します。

すると、以下のようにグラフが表示されるのが分かると思います。 graph

クロスエントロピーをイベントとしてグラフに描画するため、tf.scalar_summary('cross_entropy', result)で宣言します。 ついでに、関数に切り分けて綺麗にします。

def cost(py_x, Y):
    result = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x, Y))
    tf.scalar_summary("cross_entropy", result)
    return result

def cross_entropy(cost):
    return tf.train.GradientDescentOptimizer(0.05).minimize(cost)

cost = cost(py_x, Y)
train_op = cross_entropy(cost)

そして、 if epoch % 100 == 0: 以下を

print(epoch, np.mean(np.argmax(trY, axis=1) == sess.run(predict_op,
    feed_dict={X: trX, Y: trY})))
result = sess.run([summary_op, cost], feed_dict={X: trX, Y: trY})
summary_writer.add_summary(result[0], epoch)

と変更して実行してみます。

すると、イベントが記録され、誤差が収束しているのが分かると思います。 event

これを見ながらパラメータの調整などをしてモデルの構築と認識率を高めていきます。