第2回 Kerasで実践! Cats vs. Dogs (3/6)

技術特集

1_tensorflow_keras1_tit

tit_tensorflow_keras

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("判定モード")
    # 判定する画像の読み込み
  
    # 判定処理の実行
  
    # 判定結果の出力

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