TensorFlowでCNN実装(TensorBoardでの可視化有り)
初めてCNNを実装したので備忘録として。
目標
- TensorFlowではじめるDeepLearning実装入門の第3章に従い、TensorFlowでMNISTを分類するCNNを実装してみる。
- ついでに、TensorBoardに正解率等を表示する。
※ CNNの理論的詳細については定番のConvolutional Neural Networkをゼロから理解するを御覧ください。
方針
- ネットワークはおおまかに畳み込み層1、畳み込み層2、全結合層、出力層に分かれる
- 畳み込み層では畳込みとプーリングを行い、画像から特徴量を抽出
- 全結合層では、畳み込み層で生成された特徴量の次元を落とす
- 出力層で、10の数値のいずれかに分類
- TensorBoardに下記を表示する
- 各層の重みのヒストグラム
- 学習途中のaccuracyとlossの推移
- ネットワークのグラフ構造
コード
import tensorflow as tf import tensorflow.examples.tutorials.mnist as mnist # MNISTを保存 mnist_data = mnist.input_data.read_data_sets("../data/mnist/", one_hot=True) def train_cnn(): # TensorBoardに載せるsummaryのリスト summaries = [] # 入力 with tf.name_scope("data"): # 入力画像 x = tf.placeholder(tf.float32, [None, 784], name="x") # 正解ラベル y = tf.placeholder(tf.float32, [None, 10], name="y") # 畳み込み層1 with tf.name_scope("conv_1"): # 畳み込み可能なshapeに変換 img = tf.reshape(x, [-1, 28, 28, 1]) # 畳み込みフィルタ f_1 = tf.Variable(tf.truncated_normal([5, 5, 1, 32], stddev=0.1), name="f_1") # ログにf_1のヒストグラムを出力 summaries.append(tf.summary.histogram("f_1", f_1)) # 畳み込み演算 conv_1 = tf.nn.conv2d(img, f_1, strides=[1, 1, 1, 1], padding="SAME", name="conv_1") # バイアス b_1 = tf.Variable(tf.constant(0.1, shape=[32])) # 活性化関数 h_conv_1 = tf.nn.relu(conv_1 + b_1) # プーリング演算 h_pool_1 = tf.nn.max_pool(h_conv_1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME") # 畳み込み層2 with tf.name_scope("conv_2"): # 畳み込みフィルタ f_2 = tf.Variable(tf.truncated_normal([5, 5, 32, 64], stddev=0.1), name="f_2") # ログにf_2のヒストグラムを出力 summaries.append(tf.summary.histogram("f_2", f_2)) # 畳み込み演算 conv_2 = tf.nn.conv2d(h_pool_1, f_2, strides=[1, 1, 1, 1], padding="SAME", name="conv_2") # バイアス b_2 = tf.Variable(tf.constant(0.1, shape=[64])) # 活性化関数 h_conv_2 = tf.nn.relu(conv_2 + b_2) # プーリング演算 h_pool_2 = tf.nn.max_pool(h_conv_2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME") # 全結合層 with tf.name_scope("fc"): # フラットな形に変換 h_pool_2_flat = tf.reshape(h_pool_2, [-1, 7*7*64]) # 全結合層の重み w_fc = tf.Variable(tf.truncated_normal([7*7*64, 1024], stddev=0.1), name="w_fc") # ログにw_fcのヒストグラムを出力 summaries.append(tf.summary.histogram("w_fc", w_fc)) # バイアス b_fc = tf.Variable(tf.constant(0.1, shape=[1024])) # 活性化関数 h_fc = tf.nn.relu(tf.matmul(h_pool_2_flat, w_fc) + b_fc) # 出力層 with tf.name_scope("out"): # 全結合層の重み w_out = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1)) # ログにw_outのヒストグラムを出力 summaries.append(tf.summary.histogram("w_out", w_out)) # バイアス b_out = tf.Variable(tf.constant(0.1, shape=[10])) # 活性化関数 out = tf.nn.softmax(tf.matmul(h_fc, w_out) + b_out) # 誤差 with tf.name_scope("loss"): # 誤差 loss = tf.reduce_mean(-tf.reduce_sum(y * tf.log(out + 1e-5), axis=[1])) # 誤差をログに出力 summaries.append(tf.summary.scalar("loss", loss)) # 訓練 with tf.name_scope("train"): train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss) # 評価 with tf.name_scope("accuracy"): # 正解率 correct = tf.equal(tf.argmax(out, 1), tf.argmax(y, 1)) accuracy = tf.reduce_mean(tf.cast(correct, tf.float32)) # 正解率をログに出力 summaries.append(tf.summary.scalar("accuracy", accuracy)) # 初期化 init = tf.global_variables_initializer() # 実行 with tf.Session() as sess: # ログをひとまとめにする設定 summary_op = tf.summary.merge(summaries) # ログの保管場所 summary_writer = tf.summary.FileWriter("../logs", sess.graph) # 初期化 sess.run(init) # テストデータ test_images, test_labels = mnist_data.test.images, mnist_data.test.labels # 学習 for i in range(1000): # 訓練データ train_images, train_labels = mnist_data.train.next_batch(50) # ミニバッチ学習 sess.run(train_step, feed_dict={x: train_images, y: train_labels}) # 定期的にログ書き出し step = i + 1 if step % 100 == 0: pass # ログのテキスト取得 summary_text, acc_val = sess.run([summary_op, accuracy], feed_dict={x: test_images, y: test_labels}) # ログに書き出し summary_writer.add_summary(summary_text, step) print("step: {} acc: {: .3}".format(step, acc_val)) train_cnn()
出力
tensorboard --logdir logs/mnist
を実行し、ブラウザからlocalhost:6006
にアクセスすると、可視化結果が見られる。
Accuracy
Loss
出力層の活性化関数を間違えてSoftmaxではなくReluにしてしまっていたため、最初全く精度が出ずに焦った。。。