R&Dセンター 技術開発部 古林 隆宏

TensorFlow+Keras入門 ~ ウチでもできた⁉ ディープラーニング ~

第2回 Kerasで実践! Cats vs. Dogs

第2回はKerasを用いて正面からディープニューラルネットワーク(以下DNNと表記)を実装していきます。

記事内で必要に応じてPythonのコードを示すことがありますが、Python自体の機能や構文に関する解説は割愛させていただきますのでご了承ください。
なお、この記事にて掲載しているコードはすべてMITライセンスのもと利用を許諾するものとします。

1) 今回のお題:ディープラーニングで犬と猫を認識する

出来合いの学習済みモデルを用いた画像認識は第1回でも行いましたが、今回は自分でゴールを設定してディープラーニングを行います。
お題は「入力した画像が犬か猫かを判定する」ということにいたします。

処理の流れを整理

このプログラムに必要な機能を列挙すると、以下のようになります:

  1. 画像の学習ができる
    • 学習用の画像を読み込める
    • 読み込んだ画像でDNNの学習ができる
    • 学習済みモデルを保存できる
  2. 画像の判定ができる
    • 判定したい画像を読み込める
    • 読み込んだ画像をDNNで判定できる
    • 判定結果を表示できる

今回に限らず、ディープラーニングを行うプログラムは、「データの読み込み」「DNN処理」「結果の出力」という3つの処理を、学習向けと判定向けにそれぞれ行う、という構造をとるのが典型的です。

大枠としては以下のようなプログラムになりそうです:

import argparse
 
parser = argparse.ArgumentParser()
parser.add_argument("--infer", action="store", nargs="?", type=str,
                    help="学習は行わず、このオプションで指定した画像ファイルの判定処理を行う")
args = parser.parse_args()
 
if not args.infer:
    print("学習モード")
    # 学習データの読み込み
  
    # 学習の実行
  
    # 学習済みモデルの保存
else:
    print("判定モード")
    # 判定する画像の読み込み
  
    # 判定処理の実行
  
    # 判定結果の出力

コマンドライン引数なしで実行すると、特定のディレクトリから学習用画像を読み込んでディープラーニングを行います。コマンドライン引数として --inferオプションと共に判定したい画像のファイルパスを指定すると、学習済みモデルをもとに、その画像に写っているのが犬か猫かを判定するという想定です。

また今回は、DNNの構造の定義を上記のcat_dog_dnn_keras.pyとは別ファイルに切り出し、モジュールとして呼び出す形で進めます。

2) DNNの構造を定義

それでは最も美味しい部分、すなわちDNNの構造を定義するところから見てみることにしましょう。
新たなファイルmydnn_keras.pyを作成し、そこにDNN部分のみを記述していきます。

コードの大枠を確認

まずKerasから必要な部品をimportしておきます。
そして、MyDNNという関数を呼べば、Kerasで定義したDNNが得られるモジュールとして実装していきます:

from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.pooling import MaxPooling2D
from keras.layers.core import Dense, Dropout, Flatten
from keras.layers.normalization import BatchNormalization
from keras.optimizers import Adam
 
def MyDNN(input_shape=(32, 32, 1), output_size=10, learning_rate=0.001, keep_prob=0.5):
    model = Sequential()
    
    ...
    
    return model

MyDNN関数の引数について

引数のinput_shapeは、入力データ1件の形を表します。
デフォルト値として書かれている(32, 32, 1)は、32ピクセル×32ピクセルの白黒画像を表します。
3つ目の数字は、チャンネル数を表し、RGBのカラー画像を入力とする場合は、赤と緑と青の3チャンネルが必要なので(32, 32, 3)のように指定します。

引数output_sizeは出力のサイズ、つまり入力画像を何通りに分類するかを示します。今回は犬と猫の2通りなので、後ほどメソッド呼び出し時に2を指定します。

引数learning_rateは、学習係数です。これは、どれだけ大胆に学習を行うかを示します。学習係数が大き過ぎると細やかな調整ができなくなるため、高い精度を得にくくなり、小さ過ぎると少しずつしか学習しないため、学習に時間がかかってしまいます。

引数keep_probは、後述するDropoutという処理のパラメータです。値は0.5が一般的です。

Sequentialモデルによる実装

それでは具体的な実装に入っていきます。今回はKerasのSequentialモデルという実装方法を利用します。
Sequentialモデルでは、まずこの処理をして、次にこの処理をして・・・といったようにDNNの構造を順番に追加していく書き方をとります。
冒頭部分で読み込んでいるConv2DMaxPooling2DDense, Dropout, Flattenなどといったものが、この追加する処理にあたるクラスです。

さて、処理を追加していく部分は以下のようになります:

model = Sequential()
    
    model.add(Conv2D(20, kernel_size=5, strides=2, activation='relu',
                     input_shape=input_shape))
    model.add(MaxPooling2D(3, strides=2))
    model.add(BatchNormalization())
 
    model.add(Conv2D(50, kernel_size=5, strides=2, activation='relu'))
    model.add(MaxPooling2D(3, strides=2))
    model.add(BatchNormalization())
 
    model.add(Flatten())
    model.add(Dense(100, activation='relu'))
    model.add(Dropout(keep_prob))
    model.add(Dense(output_size, activation='softmax'))

KerasのSequentialモデルでは、一番先頭の層には入力データの形を教えてあげる必要があるため、初回のConv2D追加時にのみinput_shape引数を指定しています。
DNNの構造とコードの対応は以下のようになっています:

このように、DNNの構造をそのまま書き下すようにシンプルに記述できるのがKerasのSequentialモデルの特徴です。
上図にもあるように、特にDNNの定義においてはそれぞれの処理を「層」ということばで表現します。
複数の層が深く積み重なっているイメージが、DNNの「ディープ」という言葉の由来となっています。

このDNN構造の特徴

各層の詳細な説明はこの記事では割愛しますが、この構造に含まれる特徴として以下の3点が挙げられます:

  • 畳み込み→プーリングというパターンの繰り返し
    • 画像のディープラーニングにおいて現在広く普及しているテクニックで、この繰り返し構造を導入することで画像の特徴を柔軟に認識できることが知られています。「畳み込みニューラルネットワーク(Convolutional Neural Network, CNN)」と呼ばれます。
  • Batch Normalizationによる正規化
    • データのばらつきによる学習への悪影響をおさえる効果があります。こう書くと地味ですが、実は上記のDNNから正規化層を除くと、少なくとも犬と猫の分類処理に対しては全く精度が出なくなるほどの絶大な効果を持っています。
  • ドロップアウトの採用
    • 一部の入力値を学習時に意図的に計算から外し、穴あきのような状態にして学習するテクニックです。これにより、穴あきの入力値でも正解が得られるようなタフで柔軟なDNNに仕上がることが期待されます。
    • パラメータkeep_probは穴あきにする程度を示し、1.0(全くドロップアウトしない)から0.5(半分穴あきにする)程度の値を指定します。

学習方法の指定

Sequentialモデルへの処理の追加が終わったら、最後にcompileメソッドで学習の方法や評価の方法を指定します。
今回はAdamと呼ばれる手法にクロスエントロピーという値を入力することにより学習を行っていきます。

model.compile(optimizer=Adam(lr=learning_rate, epsilon=1e-1),
                  loss='categorical_crossentropy', metrics=['accuracy'])
                  
    return model

DNN定義モジュール完成

まとめると、mydnn_keras.pyの全体は以下のようになります。

from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.pooling import MaxPooling2D
from keras.layers.core import Dense, Dropout, Flatten
from keras.layers.normalization import BatchNormalization
from keras.optimizers import Adam
 
def MyDNN(input_shape=(32, 32, 1), output_size=10, learning_rate=0.001, keep_prob=0.5):
    model = Sequential()
    
    model.add(Conv2D(20, kernel_size=5, strides=2, activation='relu',
                     input_shape=input_shape))
    model.add(MaxPooling2D(3, strides=2))
    model.add(BatchNormalization())
 
    model.add(Conv2D(50, kernel_size=5, strides=2, activation='relu'))
    model.add(MaxPooling2D(3, strides=2))
    model.add(BatchNormalization())
 
    model.add(Flatten())
    model.add(Dense(100, activation='relu'))
    model.add(Dropout(keep_prob))
    model.add(Dense(output_size, activation='softmax'))
 
    model.compile(optimizer=Adam(lr=learning_rate, epsilon=1e-1),
                  loss='categorical_crossentropy', metrics=['accuracy'])
    return model

また、cat_dog_dnn_keras.pyからの呼び出し方は以下のようなイメージです。

from mydnn_keras import MyDNN
 
...
 
model = MyDNN(input_shape=(input_size_x, input_size_y, input_size_c),
              output_size=output_size)

3) 学習部分の実装

cat_dog_dnn_keras.pyに戻って、学習部分を実装していきます。

学習用画像ファイルを読み込む

一般的に機械学習では、学習のために学習データと検証データの2つのデータセットを用意します。
学習データはそのままDNNに学習させるデータで、用意したデータの大部分をこちらに使用します。
検証データはDNNに学習はさせず、鍛えたDNNが実戦でどれだけの精度を発揮するかを検証するために使われます。

今回は、データが以下のようなディレクトリ構造で準備されていることを想定します:

./
   cat_dog_dnn_keras.py
   mydnn_keras.py
   data/
       train/ →学習データを格納
           cat/ →学習データのうち猫の画像を格納
           dog/ →学習データのうち犬の画像を格納
       valid/ →検証データを格納
           cat/ →検証データのうち猫の画像を格納
           dog/ →検証データのうち犬の画像を格納

さて実装部分ですが、ここでもKerasの機能が活躍します。

from keras.preprocessing.image import ImageDataGenerator
 
input_size_x = 224
input_size_y = 224
batch_size = 20
 
keras_idg = ImageDataGenerator(rescale=1.0 / 255)
train_generator = keras_idg.flow_from_directory('data/train',
                      target_size=(input_size_x, input_size_y),
                      batch_size=batch_size,
                      class_mode='categorical')
valid_generator = keras_idg.flow_from_directory('data/valid',
                      target_size=(input_size_x, input_size_y),
                      batch_size=batch_size,
                      class_mode='categorical')

DNNに画像データを学習させるにあたっては下記のような操作が必要なのですが、ImageDataGeneratorを使用すると上記のように書くだけでそれが実現可能です。

  • 指定ディレクトリ以下の画像を読み込んでDNNの入力に相応しい形式に変換
  • 画像サイズを一定に調整
  • 入力画像のクラス(ここでは犬か猫かという情報)を画像データに添える
    • サブディレクトリの構造から判断
  • 画像の量が膨大だと一度に処理できないので、小分け(batch_size個ずつ)にしてDNNに与える
  • 入力画像が一巡したら先頭に戻って無限にデータを与え続ける

また上記では使用していませんが、画像の色補正や拡大縮小によって学習データ件数を増幅するなど、他にも豊富な機能を備えています。

DNNを学習

つづいて、学習させる部分を書いていきます。

from mydnn_keras import MyDNN
import os
 
input_size_c = 3
output_size = 2
epochs = 10
 
num_data_train_dog = len(os.listdir('data/train/dog'))
num_data_train_cat = len(os.listdir('data/train/cat'))
num_data_train = num_data_train_dog + num_data_train_cat
 
num_data_valid_dog = len(os.listdir('data/valid/dog'))
num_data_valid_cat = len(os.listdir('data/valid/cat'))
num_data_valid = num_data_valid_dog + num_data_valid_cat
 
model = MyDNN(input_shape=(input_size_x, input_size_y, input_size_c),
              output_size=output_size)
model.fit_generator(train_generator,
                    validation_data=valid_generator,
                    epochs=epochs,
                    steps_per_epoch=num_data_train/batch_size,
                    validation_steps= num_data_valid/batch_size)

Sequentialモデルにはfitfit_generatorという2つの学習用メソッドがあります。
fitはあらかじめ読み込んだデータ全体を受け取って一度に処理するメソッドです。
それに対しfit_generatorは、データのジェネレータを受け取り、使用する分のみを都度読み込むように動くメソッドです。
今回のように膨大な量の画像を相手にする場合に、fit_generatorを使うことでメモリの消費量を抑制することができます。

fit_generatorメソッドの引数について

epochs引数は、学習データ全体を何巡ぶん学習するかを示します。
steps_per_epoch引数は、DNNの計算を何回行うと学習データを一巡したことになるかを指定します。
上述の通りDNNには学習データをbatch_size個ずつ小分けにして与えますので、学習データ全体の数をbatch_sizeで割った値を指定します。
validation_steps引数は、DNNの計算を何回行うと検証データを一巡したことになるかを指定します。検証データ全体の数をbatch_sizeで割った値を指定します。

学習したDNNを保存

学習処理が終了した時点では結果はメモリ上にあるのみですので、それを後から利用できるようファイルに書き出す必要があります。
以下のように記述すると、ckpt/mydnn_weights.hdf5に学習結果が保存されます。

checkpoint_dir = 'ckpt'
 
os.makedirs(checkpoint_dir, exist_ok=True)
model.save_weights(os.path.join(checkpoint_dir, 'mydnn_weights.hdf5'))

学習部分完成

ここまでをcat_dog_dnn_keras.pyに適用すると、以下のようになります:

import argparse
from keras.preprocessing.image import ImageDataGenerator
import os
from mydnn_keras import MyDNN
 
input_size_x = 224
input_size_y = 224
batch_size = 20
input_size_c = 3
output_size = 2
epochs = 10
checkpoint_dir = 'ckpt'
 
parser = argparse.ArgumentParser()
parser.add_argument("--infer", action="store", nargs="?", type=str,
                    help="学習は行わず、このオプションで指定した画像ファイルの判定処理を行う")
args = parser.parse_args()
 
model = MyDNN(input_shape=(input_size_x, input_size_y, input_size_c),
              output_size=output_size)
 
if not args.infer:
    print("学習モード")
    # 学習データの読み込み
    keras_idg = ImageDataGenerator(rescale=1.0 / 255)
    train_generator = keras_idg.flow_from_directory('data/train',
                          target_size=(input_size_x, input_size_y),
                          batch_size=batch_size,
                          class_mode='categorical')
    valid_generator = keras_idg.flow_from_directory('data/valid',
                          target_size=(input_size_x, input_size_y),
                          batch_size=batch_size,
                          class_mode='categorical')
 
  
    # 学習の実行
    num_data_train_dog = len(os.listdir('data/train/dog'))
    num_data_train_cat = len(os.listdir('data/train/cat'))
    num_data_train = num_data_train_dog + num_data_train_cat
 
    num_data_valid_dog = len(os.listdir('data/valid/dog'))
    num_data_valid_cat = len(os.listdir('data/valid/cat'))
    num_data_valid = num_data_valid_dog + num_data_valid_cat
 
    model.fit_generator(train_generator,
                        validation_data=valid_generator,
                        epochs=epochs,
                        steps_per_epoch=num_data_train/batch_size,
                        validation_steps= num_data_valid/batch_size)
 
    # 学習済みモデルの保存
    os.makedirs(checkpoint_dir, exist_ok=True)
    model.save_weights(os.path.join(checkpoint_dir, 'mydnn_weights.hdf5'))
else:
    print("判定モード")
    # 判定する画像の読み込み
  
    # 判定処理の実行
  
    # 判定結果の出力

これで学習モード側の実装は終わりです!

4) 学習をやってみる

学習した結果を利用する部分の実装はさておき、とりあえず学習を実行してみましょう。

データの取得と準備

犬と猫の判別をディープラーニングするには、大量の犬の画像と、大量の猫の画像が必要になります。
今回は、Kaggleというサイトで奇遇にも目的にぴったりのデータセットが配布されていますので、利用させてもらいましょう。

データは下記URLから取得可能です。
https://www.kaggle.com/c/dogs-vs-cats/data
ダウンロードするにはKaggleのアカウントを作成する必要があります。
Kaggleにサインインしたあとに「train.zip」をクリックし、「Download」ボタンを押します。

train.zipを解凍すると、「dog.123.jpg」「cat.123.jpg」のような画像ファイルが合計25000枚得られます。
上述の通り、今回は下記のようなディレクトリ構造になるようにデータを準備していきます。

./
  cat_dog_dnn_keras.py
  mydnn_keras.py
  data/
      train/ →学習データを格納
          cat/ →学習データのうち猫の画像を格納
          dog/ →学習データのうち犬の画像を格納
      valid/ →検証データを格納
          cat/ →検証データのうち猫の画像を格納
          dog/ →検証データのうち犬の画像を格納

手作業で準備してもよいのですが、自動で画像を振り分けるスクリプトを作成しましたのでご利用ください:

import os
import re
import random
 
random.seed(873675312)
 
source_dir = "./train"
train_dir = "./data/train"
valid_dir = "./data/valid"
 
os.makedirs("%s/dog" % train_dir)
os.makedirs("%s/cat" % train_dir)
os.makedirs("%s/dog" % valid_dir)
os.makedirs("%s/cat" % valid_dir)
 
source_files = os.listdir(source_dir)
source_dog = [f for f in source_files if re.match('dog', f)]
source_cat = [f for f in source_files if re.match('cat', f)]
 
random.shuffle(source_dog)
random.shuffle(source_cat)
 
# 検証データとして1000枚使用
for d, c in zip(source_dog[:1000], source_cat[:1000]):
    os.rename("%s/%s" % (source_dir, d),
              "%s/dog/%s" % (valid_dir, d))
    os.rename("%s/%s" % (source_dir, c),
              "%s/cat/%s" % (valid_dir, c))
 
# 残りを学習データとして利用
for d, c in zip(source_dog[1000:], source_cat[1000:]):
    os.rename("%s/%s" % (source_dir, d),
              "%s/dog/%s" % (train_dir, d))
    os.rename("%s/%s" % (source_dir, c),
              "%s/cat/%s" % (train_dir, c))

cat_dog_dnn_keras.pyと同じ階層に

  • train.zipを解答したtrainディレクトリ
  • move_data.py

を配置し、move_data.pyを実行すると、全データから犬と猫の画像それぞれ1000枚を検証データとして抜き出した上で今回必要なディレクトリ構造を構成します。

さあ、これで準備は整いました!

学習の実行

操作としてはcat_dog_dnn_keras.pyを実行するのみです。
実行すると以下のようなログが出るでしょう(環境により他のメッセージがまじることもあります)。

$ python cat_dog_dnn_keras.py
Using TensorFlow backend.
学習モード
Found 23000 images belonging to 2 classes.
Found 2000 images belonging to 2 classes.
Epoch 1/10
1150/1150 [==============================] - 44s - loss: 0.7404 - acc: 0.6617 - val_loss: 0.5359 - val_acc: 0.7435
Epoch 2/10
1150/1150 [==============================] - 43s - loss: 0.4934 - acc: 0.7605 - val_loss: 0.4790 - val_acc: 0.7675
Epoch 3/10
1150/1150 [==============================] - 43s - loss: 0.4271 - acc: 0.8034 - val_loss: 0.4617 - val_acc: 0.7910
Epoch 4/10
1150/1150 [==============================] - 43s - loss: 0.3885 - acc: 0.8261 - val_loss: 0.4276 - val_acc: 0.8000
Epoch 5/10
1150/1150 [==============================] - 43s - loss: 0.3521 - acc: 0.8467 - val_loss: 0.4939 - val_acc: 0.7855
Epoch 6/10
1150/1150 [==============================] - 43s - loss: 0.3255 - acc: 0.8578 - val_loss: 0.4387 - val_acc: 0.8180
Epoch 7/10
1150/1150 [==============================] - 43s - loss: 0.3003 - acc: 0.8742 - val_loss: 0.3833 - val_acc: 0.8310
Epoch 8/10
1150/1150 [==============================] - 43s - loss: 0.2760 - acc: 0.8843 - val_loss: 0.3847 - val_acc: 0.8315
Epoch 9/10
1150/1150 [==============================] - 43s - loss: 0.2501 - acc: 0.8965 - val_loss: 0.4182 - val_acc: 0.8050
Epoch 10/10
1150/1150 [==============================] - 43s - loss: 0.2172 - acc: 0.9098 - val_loss: 0.3793 - val_acc: 0.8355


先にも触れましたが、Epochというのは学習データ全体を一巡する量の処理を示します。
何Epoch分処理を回せばよいかというのはデータ量や処理の複雑さなどさまざまな条件で変化するため、
基本的には各種の評価値の推移を観察し、これ以上学習しても精度が上がらないだろうというところで処理を打ち切るというのがセオリーです。

出力を読み解く

上記のログで注目したいのは「acc」と「val_acc」という値です。
これはどちらも正解率、つまり今回の場合は犬と猫の分類が正しく出来た割合を示します。
「acc」は学習精度、つまり学習データにおける正解率であり、この値が1に近づいていくことで、学習が進捗していることが確認できます。
「val_acc」は検証精度、つまり検証データにおける正解率を示します。学習済みモデルを実戦に投入した際にはこの程度の正解率が期待できるという目安です。

基本的な傾向として、学習精度はどんどん1に近づいていきますが、
検証精度はある一定のところで打ち止めになります。またやりすぎると逆に悪くなっていくこともあります。
上記のログを見ると、学習精度はどんどん上がっていますが、Epoch 7あたりから検証精度の改善が鈍っていることが見て取れます。
もう少し続けても良いがそろそろ打ち切ってもいいかな、という微妙な状況です。

今回は「とりあえずKerasでディープラーニングができること」が目的ですので、8割の精度でよしとすることにします。
さらに精度を上げたい場合には、以下のような方策をとることになります:

  • 学習データを増やす(新規追加する、加工によって増幅する)
  • Epoch数を増やしてみる
  • DNNの構造を変更する(層を増やす、層を広げる、正則化などのテクニックを加える etc.)

5) 判定部分の実装

学習ができたところで、学習済みモデルを利用して未知の画像を判定する部分を実装していきます。

判定対象画像の読み込み

まず画像の読み込みですが、学習時とは違って特定の1枚の画像を読み込むという操作なので、ImageDataGeneratorとは違う方法を使っていきます。
具体的にはkeras.preprocessing.imageパッケージで提供されているload_imgimg_to_arrayを用います。

from keras.preprocessing.image import load_img, img_to_array
import numpy as np
 
...
 
    image_infer = load_img(args.infer, target_size=(input_size_x, input_size_y))
    data_infer = img_to_array(image_infer)
    data_infer = np.expand_dims(data_infer, axis=0)
    data_infer = data_infer / 255.0

load_imgで画像ファイルを読みこみ、img_to_arrayでKerasで扱える形式に変換します。
np.expand_dimsという部分は、「1枚の画像」を「1枚の画像を含む配列」に変換する操作です。
Kerasの判定処理を行う機能が「復数の画像を含む配列」という形式を要求するため、このような操作を行っています。
最後に画像データの各ピクセルの値を255で割っていますが、これは各ピクセルの値を0以上1以下に正規化する操作で、学習用のブロックでImageDataGenerator(rescale=1.0 / 255)とした部分に対応しています。

判定処理

判定を行う前にload_weightsメソッドで学習済みモデルを読み込む必要があります。
その後predictメソッドで判定処理を実行します。

# 判定処理の実行
    model.load_weights(os.path.join(checkpoint_dir, 'mydnn_weights.hdf5'))
    result = model.predict(data_infer)[0] * 100
 
    # 判定結果の出力
    if result[0] > result[1]:
      print('Cat (%.1f%%)' % result[0])
    else:
      print('Dog (%.1f%%)' % result[1])

predictの結果としては「復数の画像に対する判定結果の配列」が返ってきます。今回は画像が1枚だけですので[0]で直接取り出しています。
個々の結果は[猫である確率, 犬である確率]という形式になっており、今回はこれに100をかけてパーセンテージの値を得ています。
猫が先に来ているのは、ImageDataGeneratorによる学習データ読み込み時にディレクトリ名を用いて順序が決められるためです。

コード完成

これらのコードを含めて、いよいよcat_dog_dnn_keras.pyの完成となります:

import argparse
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array
import numpy as np
import os
from mydnn_keras import MyDNN
 
input_size_x = 224
input_size_y = 224
batch_size = 20
input_size_c = 3
output_size = 2
epochs = 10
checkpoint_dir = 'ckpt'
 
parser = argparse.ArgumentParser()
parser.add_argument("--infer", action="store", nargs="?", type=str,
                    help="学習は行わず、このオプションで指定した画像ファイルの判定処理を行う")
args = parser.parse_args()
 
model = MyDNN(input_shape=(input_size_x, input_size_y, input_size_c),
              output_size=output_size)
 
if not args.infer:
    print("学習モード")
    # 学習データの読み込み
    keras_idg = ImageDataGenerator(rescale=1.0 / 255)
    train_generator = keras_idg.flow_from_directory('data/train',
                          target_size=(input_size_x, input_size_y),
                          batch_size=batch_size,
                          class_mode='categorical')
    valid_generator = keras_idg.flow_from_directory('data/valid',
                          target_size=(input_size_x, input_size_y),
                          batch_size=batch_size,
                          class_mode='categorical')
 
  
    # 学習の実行
    num_data_train_dog = len(os.listdir('data/train/dog'))
    num_data_train_cat = len(os.listdir('data/train/cat'))
    num_data_train = num_data_train_dog + num_data_train_cat
 
    num_data_valid_dog = len(os.listdir('data/valid/dog'))
    num_data_valid_cat = len(os.listdir('data/valid/cat'))
    num_data_valid = num_data_valid_dog + num_data_valid_cat
 
    model.fit_generator(train_generator,
                        validation_data=valid_generator,
                        epochs=epochs,
                        steps_per_epoch=num_data_train/batch_size,
                        validation_steps= num_data_valid/batch_size)
 
    # 学習済みモデルの保存
    os.makedirs(checkpoint_dir, exist_ok=True)
    model.save_weights(os.path.join(checkpoint_dir, 'mydnn_weights.hdf5'))
else:
    print("判定モード")
    # 判定する画像の読み込み
    image_infer = load_img(args.infer, target_size=(input_size_x, input_size_y))
    data_infer = img_to_array(image_infer)
    data_infer = np.expand_dims(data_infer, axis=0)
    data_infer = data_infer / 255.0
  
    # 判定処理の実行
    model.load_weights(os.path.join(checkpoint_dir, 'mydnn_weights.hdf5'))
    result = model.predict(data_infer)[0] * 100
 
    # 判定結果の出力
    if result[0] > result[1]:
      print('Cat (%.1f%%)' % result[0])
    else:
      print('Dog (%.1f%%)' % result[1])

判定モードのブロックが実装された他に、import部分が少し追加されていることに注意してください。

6) 判定の実行

では締めくくりとして、実際に画像の判定を行っていきましょう。
--inferオプションで対象画像を指定しつつcat_dog_dnn_keras.pyを実行します。

$ cat_dog_dnn_keras.py --infer dog_test.jpg
Using TensorFlow backend.
判定モード
Dog (100.0%)
 
$ cat_dog_dnn_keras.py --infer cat_test.jpg
Using TensorFlow backend.
判定モード
Cat (100.0%)

愛犬・愛猫の画像お持ちの方はその画像を使ってみると面白いと思います。
またvalidディレクトリ以下にある画像を用いても良いでしょう。
8割程の精度ではけっこう外すこともありますが、そこはご愛敬ということで……

ご参考までに、CC0ライセンス(≒著作権フリー)の画像を1点ずつご紹介いたします。こちらはご自由にお使いください。

おわりに

この記事をもって、Kerasを用いたディープラーニングの初歩が達成できたことになります。あなたにはすでに「ディープラーニング?ああ、こないだやったよ」と言ってのける資格があるわけです。
記事にするとなかなかのボリュームですが、最終的にできたコードを見ると全部で100行程度ですから「意外に簡単に書けたな」と感じるのではないでしょうか。
次回は、Kerasを使わず直接TensorFlowで実装していくとどうなるのか、というところをご紹介できればと思います。

1

2

3 4 5
PAGE TOP