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

技術特集

1_tensorflow_keras1_tit

tit_tensorflow_keras

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の構造とコードの対応は以下のようになっています:

mydnn-structure

このように、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)