Help us understand the problem. What is going on with this article?

TensorFlowでMNISTの学習をしてみる

More than 1 year has passed since last update.

はじめに

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とは? スカラー値やデータフローグラフの可視化

SwitchBlade
業務は組込み系の開発を中心にやってきましたが、2020年7月~株式会社マイスター・ギルドにてWeb系の開発を始めました。
m-gild
最先端技術のMEISTERを目指し、お互い切磋琢磨するGUILD。Webシステム/サービス開発、スマホアプリ開発、AR/VR/MR開発など、様々なニーズに応えます。
https://www.m-gild.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away