Edited at

TensorFlowでMNISTの学習をしてみる


はじめに

DeepLearningの手始めとして、TensorFlowでMNISTの学習をしてみました。

いろんなサイトのコードを参考にさせていただきながら、組み上げました。

今回は以下も目的にコーディングしてみました。

- 学習モデルをクラス化する

- 学習結果を保存して後で使えるようにする

- TensorBoardに表示する学習経過を保存する


学習モデル


DeepConvNet.py

class CNN:

""" 予測モデルを作成する関数
引数:
images_placeholder: 画像のplaceholder
keep_prob: dropout率のplaceholder
image_size: 画像サイズ
num_clesses:識別数
返り値:
y_conv: 各クラスの確率の配列 ([tensorflow.python.framework.ops.Tensor]型)
"""

def makeMnistCNN(imegs_placeholder, keep_prob , image_size , num_clesses):
# 重みを初期化
def weight_variable(shape):
# 重みを標準偏差0.1の正規分布で初期化
inital = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(inital)

# バイアスを初期化
def bias_variable(shape):
# 定数0.0で初期化
inital = tf.constant(0.0, shape=shape)
return tf.Variable(inital)

# 畳み込み層
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding="SAME")

# プーリング層
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding="SAME")

# 入力層を28*28*1に変形
x_image = tf.reshape(imegs_placeholder, [-1, image_size, image_size, 1])

# 畳み込み層1の作成
with tf.name_scope("conv1") as scope:
W_conv1 = weight_variable([3,3,1,16])
b_conv1 = bias_variable([16])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

# プーリング層1の作成
with tf.name_scope("pool1") as scope:
h_pool1 = max_pool_2x2(h_conv1)

# 畳み込み層2の作成
with tf.name_scope("conv2") as scope:
W_conv2 = weight_variable([3,3,16,32])
b_conv2 = bias_variable([32])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

# プーリング層2の作成
with tf.name_scope("pool2") as scope:
h_pool2 = max_pool_2x2(h_conv2)

# 畳み込み層3の作成
with tf.name_scope("conv3") as scope:
W_conv3 = weight_variable([3,3,32,32])
b_conv3 = bias_variable([32])
h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)

# 畳み込み層4の作成
with tf.name_scope("conv4") as scope:
W_conv4 = weight_variable([3,3,32,32])
b_conv4 = bias_variable([32])
h_conv4 = tf.nn.relu(conv2d(h_conv3, W_conv4) + b_conv4)

# 結合層1の作成
with tf.name_scope("fc1") as scope:
# 1次元に変形
h_flat = tf.reshape(h_conv4, [-1, 7*7*32])
W_fc1 = weight_variable([7*7*32, 512])
b_fc1 = bias_variable([512])
h_fc1 = tf.nn.relu(tf.matmul(h_flat, W_fc1) + b_fc1)

# dropout1の設定
h_fc_1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 結合層2の作成
with tf.name_scope("fc2") as scope:
W_fc2 = weight_variable([512, num_clesses])
b_fc2 = bias_variable([num_clesses])

# ソフトマックス関数による正規化
with tf.name_scope("softmax") as scope:
y_conv = tf.nn.softmax(tf.matmul(h_fc_1_drop, W_fc2) + b_fc2)

return y_conv


tf.name_scope()で名前空間を設定すると、TensorBoardのグラフにその名前が使用されます。

TensorBoardで表示されるグラフではこんな感じになります。

graph_run=.png


学習コード


mnist_train.py

print('mnist_train.py START')

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import datetime

import DeepConvNet as CNN

IMAGE_SIZE = 28 # 画像サイズ
NUM_CLASSES = 10 # 識別数

print('MNIST Download Start')
# MNISTデータのダウンロード
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)
print('MNIST Download End')

""" 損失関数
引数:
logits: ロジットのtensor, float - [batch_size, NUM_CLASSES]
labels: ラベルのtensor, int32 - [batch_size, NUM_CLASSES]
返り値:
cross_entropy: 交差エントロピーのtensor, float
"""

def loss(logits, labels):
cross_entropy = -tf.reduce_sum(labels*tf.log(logits))
return cross_entropy

""" 訓練のopを定義する関数
引数:
loss: 損失のtensor, loss()の結果
learning_rate: 学習係数
返り値:
train_step: 訓練のop
"""

def training(loss, learning_rate):
# 勾配降下法(Adam)
train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)
return train_step

"""正解率(accuracy)を計算する関数
引数:
logits: ロジットのtensor, float - [batch_size, NUM_CLASSES]
labels: ラベルのtensor, int32 - [batch_size, NUM_CLASSES]
返り値:
accuracy: 正解率(float)
"""

def accuracy(logits, labels):
correct_prediction = tf.equal(tf.argmax(logits, 1), tf.arg_max(labels, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
return accuracy

if __name__=="__main__":
with tf.Graph().as_default():
print('設定 START')
x_image = tf.placeholder("float", shape=[None, IMAGE_SIZE * IMAGE_SIZE]) # 入力
y_label = tf.placeholder("float", shape=[None, NUM_CLASSES]) # 出力
keep_prob = tf.placeholder("float") #ドロップアウト

# モデルを作成
logits = CNN.CNN.makeMnistCNN(x_image, keep_prob , IMAGE_SIZE , NUM_CLASSES)

# opを定義
loss_value = loss(logits, y_label)
train_op = training(loss_value,1e-4)
accur = accuracy(logits, y_label)

sess = tf.Session()
sess.run(tf.global_variables_initializer())

#TensorBoardで追跡する変数を定義
accuracy_op_train = tf.summary.scalar("Accuracy on Train", accur)
accuracy_op_test = tf.summary.scalar("Accuracy on Test", accur)
summary_op_train = tf.summary.merge([accuracy_op_train])
summary_op_test = tf.summary.merge([accuracy_op_test])
summary_writer = tf.summary.FileWriter("./TensorBoard", graph=sess.graph)

# 訓練したモデルを保存
# (tf.train.Saver()が呼ばれる前までに呼ばれた引数が対象になる)
saver = tf.train.Saver()
print('設定 END')

print('学習 START : ' + str(datetime.datetime.now()))
#学習の実行
for epoch in range(5000):
#訓練データセットから 50 のランダムなデータの “バッチ” を取得 [0]に画像の配列、[1]に結果の配列
batch = mnist.train.next_batch(50)

# 学習の途中経過の表示・TensorBoard書き込み
if epoch % 100 == 0:
train_accury = sess.run(accur, feed_dict={x_image: batch[0], y_label: batch[1], keep_prob: 1.0})

# テストデータ(検証データ)で評価
test_batch = mnist.validation.next_batch(500, shuffle=False)
test_accury = sess.run(accur, feed_dict={x_image: test_batch[0], y_label: test_batch[1], keep_prob: 1.0})
# ↓ Jupiterで実行するとコンソールが落ちる (メモリ不足?)
#test_accury = sess.run(accur, feed_dict={x_image: mnist.validation.images, y_label: mnist.validation.labels, keep_prob: 1.0})
print("epoch:%d, train_accury : %g test_accury : %g"%(epoch, train_accury , test_accury))

summary_str_train = sess.run(summary_op_train, feed_dict={x_image: batch[0], y_label: batch[1], keep_prob: 1.0})
summary_writer.add_summary(summary_str_train, epoch)

summary_str_test = sess.run(summary_op_test, feed_dict={x_image: test_batch[0], y_label: test_batch[1], keep_prob: 1.0})
#summary_str = sess.run(summary_op_test, feed_dict={x_image: mnist.validation.images, y_label: mnist.validation.labels, keep_prob: 1.0})
summary_writer.add_summary(summary_str_test, epoch)
summary_writer.flush()

# 学習
sess.run(train_op, feed_dict={x_image: batch[0], y_label: batch[1], keep_prob:0.5})

print('学習 END : ' + str(datetime.datetime.now()))

#結果表示 (テストデータで評価)
test_batch = mnist.test.next_batch(500, shuffle=False)
print("test accuracy : %g" %sess.run(accur, feed_dict={x_image: test_batch[0], y_label: test_batch[1], keep_prob: 1.0}))
#print("test accuracy : %g" %sess.run(accur, feed_dict={x_image: mnist.test.images, y_label: mnist.test.labels, keep_prob: 1.0}))

save_path = saver.save(sess, "./ckpt/model.ckpt") # 変数データ保存
print('Save END : ' + save_path )

summary_writer.close()
sess.close()

print('mnist_train.py END')



TensorBoard用のコード

今回は訓練データを用いたときの認識精度と、テストデータを用いた認識精度をグラフ表示させました。

        #TensorBoardで追跡する変数を定義

accuracy_op_train = tf.summary.scalar("Accuracy on Train", accur)
accuracy_op_test = tf.summary.scalar("Accuracy on Test", accur)
summary_op_train = tf.summary.merge([accuracy_op_train])
summary_op_test = tf.summary.merge([accuracy_op_test])
summary_writer = tf.summary.FileWriter("./TensorBoard", graph=sess.graph)

スカラー値のグラフを作りたいときは、tf.summary.scalar()を使用して、第1引数に名称、第2引数にスカラー値を設定します。Summary出力用のopが出力されます。

tf.summary.merge()で出力対象のopをマージします。今回は1個ずつなので、マージはなくても大丈夫です。

tf.summary.FileWriter()で出力先を指定します。

                summary_str_train = sess.run(summary_op_train, feed_dict={x_image: batch[0], y_label: batch[1], keep_prob: 1.0})

summary_writer.add_summary(summary_str_train, epoch)

test_batch = mnist.validation.next_batch(500, shuffle=False)
summary_str_test = sess.run(summary_op_test, feed_dict={x_image: test_batch[0], y_label: test_batch[1], keep_prob: 1.0})
summary_writer.add_summary(summary_str_test, epoch)
summary_writer.flush()

sess.run()で、訓練データとテストデータのそれぞれで、認識精度を求め、TensorBoard用の文字列が出力します。

summary_writer.add_summary()でファイルに書き込みます。第2引数が横軸になります。

※なお、テストデータすべてを使用すると、SpyderのIPythonコンソールが落ちてしまったので、テストデータ数を少なくしています。Colaboratoryではすべて使用しても問題なく動作しました。

この結果をTensorBordで表示させた結果が以下になります。

Accuracy_on_Train2.JPG

Accuracy_on_Test2.JPG

認識精度が同じように上昇していて、過学習が発生していないことが分かります。

学習は以上になります。


テストコード

学習結果を使って、手書き文字の判定を行います。


mnist_test.py

print('mnist_test.py START')

import tensorflow as tf
import cv2
import numpy as np

import DeepConvNet as CNN

IMAGE_SIZE = 28 # 画像サイズ
NUM_CLASSES = 10 # 識別数

if __name__ == "__main__":
# https://www.tensorflow.org/api_docs/python/tf/reset_default_graph
tf.reset_default_graph()

print('設定 START')
x_image = tf.placeholder("float", shape=[None, IMAGE_SIZE * IMAGE_SIZE]) # 入力
y_label = tf.placeholder("float", shape=[None, NUM_CLASSES]) # 出力
keep_prob = tf.placeholder("float")

# モデルを作成
logits = CNN.CNN.makeMnistCNN(x_image, keep_prob , IMAGE_SIZE , NUM_CLASSES)

sess = tf.InteractiveSession()

saver = tf.train.Saver()
#変数を初期化して実行
sess.run(tf.global_variables_initializer())
print('設定 END')

print('Restore Param Start')
ckpt = tf.train.get_checkpoint_state('./ckpt')
if ckpt: # checkpointがある場合
last_model = ckpt.model_checkpoint_path # 最後に保存したmodelへのパス
print ("Restore load:" + last_model)
saver.restore(sess, last_model) # 変数データの読み込み
else:
print('Restore Failed')
print('Restore Param End')

# 画像読み込み
inputNum = input("判定したい回数を入力してください >")
for count in range(int(inputNum)):
fileName = input("画像パスを入力してください >")
fileName = 'test_data/' + fileName
print('fileName:' + fileName)

ximage = []

# 画像読み込み
image = cv2.imread(fileName, cv2.IMREAD_GRAYSCALE)
if not image is None:
# 28*28にリサイズ
image = cv2.resize(image, (IMAGE_SIZE, IMAGE_SIZE))
ximage = image.flatten().astype(np.float32)/255.0
else:
print('Error:File Read Failed !!')

if len(ximage)!=0:
pred = np.argmax(logits.eval(feed_dict={x_image: [ximage], keep_prob: 1.0})[0])

print("result:" + str(pred))

sess.close()
print('mnist_test.py END')



学習結果のリストアに関して

tf.reset_default_graph()がないと、SpyderやColaboratoryでのmnist_test.pyの1回目の実行はうまくいきますが、2回目以降で以下のようなエラーがでました。

NotFoundError (see above for traceback): Restoring from checkpoint failed. This is most likely due to a Variable name or other graph key that is missing from the checkpoint. Please ensure that you have not altered the graph expected based on the checkpoint. Original error:

Key conv1_1/Variable not found in checkpoint
[[node save_1/RestoreV2 (defined at C:/Users/takayuki/Python/TensorFlow/input_mnist.py:133) ]]

以下を参考にして解決しました。SpyderやColaboratoryも高速化のために、余計なことをしてくれてそうです。3パターンくらい解決方法がありそうですが、とりあえず一番手っ取り早そうなのでを選びました。

Tensorflow + Jupyterのsave & restore時のトラブルとその回避方法


おわりに

2019.06.15時点でColaboratory上のTensorBoardでデータの読み込みの失敗で、グラフの表示ができませんした。前はできていたんですが。。。原因不明です。

No dashboards are active for the current data set.


学習時間に関して

ローカル環境(Spyder)とColaboratoryで5000 epochの学習時間の計測を行いました。GPUの速さが際立ってますね!!

環境
時間

Spyder (intel Core i5 2.5GHz*2)
06分00秒

Colaboratory(CPU)
4分20秒

Colaboratory(GPU)
15秒

※2019.06.15時点での時間です。


参考にしたサイト

TensorFlowのMNISTチュートリアルを画像入力に対応させた

TensorflowでCNNを作る際に使いそうな関数を列挙してみた

TensorFlowを遊び倒す! 2-1. MNIST For Experts


TensorBoard

TensorFlowを使ってみる 〜その3〜 TensorBoard

TensorBoardとは? スカラー値やデータフローグラフの可視化