ごはんと飲み物は紙一重

このブログはモチベーションアップのために情報工学関連について記事を更新していこうと思います。三日坊主にならないよう頑張ります。

【俺妹】大好きな黒猫をディープラーニングで認識しないわけがない【学習編】

前回の記事

twdlab.hatenablog.com


最終章である【学習編】です.ここまで記事を読んでくれた方なら,なんとなーく察するところがあると思いますが,下準備はものすごく手間がかかって時間がかかりますね.この記事だけ読むとすれば深層学習の華やかな部分がしっかり見えるのではないのかな?w

今回学習を行うにあたって,さまざまなライブラリがあったのですが,今回は・・・

Keras

を用いた開発をしました!キャラクター認識の記事を色々と眺めるとChainerやTensorflowを用いた学習はあったのですが,Kerasはなかったのでここで一つ作り上げてしまえば,Kerasの良さも広められるし,Kerasユーザで困っている方の助け?になってくれるのではないかと.

Kerasを用いた開発にしようと思った理由は,

  • Tensorflowに比べてコーディングしたあとのコードがわかりやすい

というところが非常に大きいです.実際,自分が独学でTensorflowを勉強したとき,モデル式がどういう構造をしていて,データに対してどう処理しているかを理解するのに時間がかかってしまっていたのにたいし,Kerasはソースを見るだけでどのモデル式を使っていて何をしているのかが比較的わかりやすいように感じました.

そしてなにより,

  • PyDataOkinawaの勉強会にてKerasを使用した回があった

というところは非常に大きいですね.実際にMNISTのチュートリアルに対しKerasをもちいた学習と可視化等の基本的な動かし方を教えてもらったので書きやすかったです.

というわけで長話もここまでとして,学習をさせていこうと思います!モデル式にはおなじみCNNを用いて特徴を掴ませながらひたすら学習させてます.

実際に学習に使ったコードがこちらになります.

学習プログラム

# -*- coding: utf-8 -*-

##
## oreimo_detection.py
##
## CNNを利用して俺妹のキャラクターを識別するプログラムを作成し学習を行う
##

# モデルの読み込み
from keras.models import model_from_json
from keras.models import Model

# 最適化手法に使うクラスを読み込む
from keras.optimizers import SGD, Adadelta, Adam, RMSprop

# データセットの読み込み
import numpy as np
import oreimoface as of
from keras.utils import np_utils

# 時間の計測
import time

# モデルの可視化用モジュールの読み込み
import keras
#from IPython.display import SVG
from keras.utils.visualize_util import model_to_dot
#import plotGraph

##(1)予測式(モデル)の構築
# イメージのピクセルサイズ
img_rows, img_cols = 28,28
# 畳み込みフィルタの数
nb_filters = 32
# max poolingのサイズ
nb_pool = 2
# 畳み込みカーネルのサイズ
nb_conv = 3
# キャラクターの分類数
nb_classes = 8

# データの入力
inputs = Input(shape=(3,img_rows, img_cols)) # 1はチャンネル数

# 畳み込みによるConvolution Layerの作成(Reluによる正規化(0より小さい値を0とする関数))
x = Convolution2D(nb_filters, nb_conv, nb_conv, border_mode='valid', activation='relu')(inputs)
x = Convolution2D(nb_filters, nb_conv, nb_conv, border_mode='valid', activation='relu')(x)

# MaxPoolingによって各領域の最大値をとって画像を圧縮する
x = MaxPooling2D(pool_size=(nb_pool, nb_pool))(x)

# 過学習を防ぐためにデータに対し制約をかける(1/4だけ減らす)
x = Dropout(0.25)(x)

x = Flatten()(x)

x = Dense(128, activation='relu')(x)
x = Dropout(0.5)(x)

outputs = Dense(nb_classes, activation='softmax')(x)

# JSON形式で保存されたモデルの呼び出し
json_string = open('1000learn/oreimo_cnn_model.json', 'r').read()
model = model_from_json(json_string)
model.load_weights('1000learn/oreimo_cnn_last_weight.hdf5')

# 初回のモデル呼び出し
#model = Model(inputs, outputs)

# モデルの可視化
model.summary()
#SVG(model_to_dot(model,show_shapes=True).create(prog='dot', format='svg'))

##(2)予測式(モデル)のコンパイル
sgd_nesterov=SGD(lr=0.01, momentum=0.9, nesterov=True)

# モデルのコンパイル
model.compile(loss='categorical_crossentropy', # 誤差関数
        optimizer='adadelta',# 最適化手法(デフォルト設定)
        #optimizer=sgd_nesterov, # 最適化手法(手動設定)
        metrics=['accuracy'] # 評価指標
        )

# データセットの読み込み
(X_train, y_train), (X_test, y_test) = of.load_data()

# 読み込んだデータを0~1に正規化,numpy.arrayに変換
X_train = np.array(X_train).astype(np.float32).reshape((len(X_train), 3, 28, 28)) / 255
X_test = np.array(X_test).astype(np.float32).reshape((len(X_test), 3, 28, 28)) / 255
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)
#print('X_train shape: {0}'.format(X_train.shape))
#print('X_test shape: {0}'.format(X_test.shape))
#print('{0} train samples'.format(X_train.shape[0]))
#print('{0} test samples'.format(X_test.shape[0]))

##(3)学習実行
nb_epoch = 1
batch_size = 128

tic = time.time()

"""
# TensorBoardで学習の進捗状況をみる
tb_cb = keras.callbacks.TensorBoard(log_dir='/tmp/oreimo_cnn_detection', histogram_freq=1)
# 1エポック(1回の学習)毎にチェックしたいことを実行することで過学習を防いだり,学習の進捗状況を確認することができる

# バリデーションロスが下がれば、エポックごとにモデルを保存
cp_cb = keras.callbacks.ModelCheckpoint(filepath='./oreimo_cnn_best_weight.hdf5',
      monitor='val_loss', verbose=1, save_best_only=True, mode='auto')

# バリデーションロスが5エポック連続で上がったら、ランを打ち切る
es_cb = keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=0, mode='auto')

cbks = [tb_cb, cp_cb, es_cb]
"""

# 学習を実行し,学習途中の損失関数の値などはhistory.historyに保存される.
history = model.fit(X_train, Y_train,
        batch_size = batch_size, nb_epoch = nb_epoch,
        verbose=1, validation_data=(X_test, Y_test)) #callbacks=cbks
        # I/Oエラーが出るようならverboseの値を0にするとよいかも

toc = time.time()

# 学習にかかった時間を表示
print("Execution time: {0:.2f} [sec]".format(toc - tic))

##(4)学習結果の評価
# テストデータに対する評価値
score = model.evaluate(X_test, Y_test, verbose=0)
print('Test score:', score[0]) # 誤差関数の値
print('Test accuracy:', score[1]) # 精度

# 予測値
Y_test_pred = model.predict(X_test)
# 学習の可視化
plotGraph.plot(history)

#plotGraph.predict(X_test, Y_test_pred)

##(5)モデルとパラメータの保存
# JSON形式でモデルを保存
json_string = model.to_json()
open('./oreimo_cnn_model.json', 'w').write(json_string)

# 最終ウエイトの保存
model.save_weights('./oreimo_cnn_last_weight.hdf5', overwrite=True)

グラフ作成プログラム

# -*- coding: utf-8 -*-

##
## plotGraph.py
##
## 実行結果のグラフを出力するためのプログラム
##

import matplotlib.pyplot as plt
from keras.models import Model

def plot(z):
    fig,ax = plt.subplots(1,2, figsize=(12,6))
    ax[0].set_title('Training performance (Loss)')
    ax[0].plot(z.epoch, z.history['loss'], label='loss')
    ax[0].plot(z.epoch, z.history['val_loss'], label='val_loss')
    ax[0].set(xlabel='Epoch', ylabel='Loss')
    ax[0].legend()

    ax[1].set_title('Training performance (Accuracy)')
    ax[1].plot(z.epoch, z.history['acc'], label='acc')
    ax[1].plot(z.epoch, z.history['val_acc'], label='val_acc')
    ax[1].set(xlabel='Epoch', ylabel='Accuracy')
    ax[1].legend(loc='best')

def predict(x,y):
    # 予測の形
    y.shape
    # 予測の可視化(最初の10枚)
    plt.imshow(y[:10], cmap='gray', interpolation='nearest', vmin=0, vmax=1)
    plt.show()

    # 入力データを可視化(最初の10枚)
    fig, ax = plt.subplots(1, 10, figsize=(28, 28))
    for ii in range(10):
        ax[ii].imshow(x[ii].reshape(28, -1), cmap='gray')
        ax[ii].axis('off')
        #ax[ii].show()

    # 予測の可視化(290から295枚目)
    plt.imshow(y[290:295], cmap='gray', interpolation='nearest', vmin=0, vmax=1)
    plt.show()

    # 入力データを可視化(30~39枚目までの10枚)
    fig, ax = plt.subplots(1, 10, figsize=(28, 28))
    for ii in range(10):
        ax[ii].imshow(x[ii+30].reshape(28, -1), cmap='gray')
        ax[ii].axis('off')

基本的なコードは勉強会で貰った資料を参考に,最小限の機能をつけて学習を行いました.データは前回の記事で作成したデータを読み込ませるためにモジュールとして用いています.グラフの描画については実行環境に応じて適宜変更してやってください.Jupyter notebook での開発をするのであればplotGraph.predictのコメントを外してあげると学習段階でのテスト結果が覗けるので幸せになれるかなと.Mac上ならエラーはいてしまうのでpredictはコメントを付けたままで(デバッグ力の無さががが).どうしても動かないって人は予測はせずにウエイトだけ保存しましょう((爆

とりあえず1000回学習すればいいでしょと安易な考えで学習させた結果MBPで21時間かかりました・・・その間MBPのファンはぶんまわししていたので結構負荷かけてしまっていたのでお気をつけて.

f:id:ST_ha1cyon:20161027110130p:plain

一応ちゃんと学習はできているようです.精度も98%を超えているので一安心?.下準備の段階をへてこの結果にたどり着くとこみ上げてくるものがありますよね.

ソースコードを見てみればわかると思うのですが,モデル式の構築やコンパイル,学習の実行,ウエイトの保存がソースコードをみるだけでわかるのは非常にKerasの良いところですね(2回目)

前回学習編で最終章と謳ったのですが,一旦学習編もここで区切って,次回に【分類編】と題することでこのシリーズを終えましょうかね←

というわけでまだこのシリーズは終わらないです.1記事が長いと読む方も書く方もだるいし

では【分類編】にて〜.