VGG16のFine-tuningによる犬猫認識 (1)
VGG16はILSVRCのコンペ用に学習されたニューラルネットなのでImageNetの1000クラスを認識できる。しかし、前の記事(2017/1/4)で実験したように「ひまわり」のようなImageNetに存在しないクラスはそのままでは認識できない。
この問題を解決するためVGG16の高い認識能力を継承しつつ、新しい独自のクラス(今回は犬か猫かの2クラス)を認識できるように少量のデータでニューラルネットの重みを再調整することをFine-tuningという*1。「少量のデータで」というところがすごく重要。もし大量データでないとダメだったらAWSの利用料で破産するのでこの記事は書けない(^^;;
今回は、Keras Blogの
- Building powerful image classification models using very little dat
を参考に犬と猫の2クラス認識を例としてVGGのFine-tuningについて実験した。このKeras Blogの記事はKeras 1.2.0の公開より前に書かれており、keras.applications.vgg16
が使われていない。この記事ではKeras 1.2.0に合わせて一部を書き直した。
リポジトリ:dogs_vs_cats
セットアップ
タスクは犬と猫の分類。犬か猫の画像をニューラルネットに入力して犬または猫を出力する2クラス分類のタスク。ILSVRCの1000クラス分類に比べたらずっと簡単だけどVGG16は1000クラス分類のニューラルネットなので犬と猫の2クラス分類はそのままではできない。
Dogs vs. Cats Redux: Kernels Edition | Kaggle
このデータセットはKaggleのDogs vs. Catsで提供されているためダウンロードするにはKaggleに登録する必要がある*2。
訓練データのtrain.zip
を解凍すると犬の画像が12500枚、猫の画像が12500枚含まれている。解凍してしばらく癒されてた(笑)
今回は、小規模データを使ったFine-tuningの実験なのでここから犬1000枚、猫1000枚を訓練データ、犬400枚、猫400枚をバリデーションデータとして用いた。テストデータのtest.zipは正解ラベルが付いていなかったので今回は使わない。正解ラベルがついてたらコンテストにならないからだと思われる。
train.zipを解凍するとtrainディレクトリができる。その後にsetup.pyを実行すると下のようにデータを振り分けられる。
data ├── train │ ├── cats cat0001.jpg - cat1000.jpg │ └── dogs dog0001.jpg - dog1000.jpg └── validation ├── cats cat0001.jpg - cat0400.jpg └── dogs dog0001.jpg - dog0400.jpg
分類クラスごとにサブディレクトリ(ここではcats
とdogs
)を作るのが重要。KerasのImageDataGenerator
が自動的にcats/dogsをクラス名と認識してくれるのだ。
Kerasで画像ファイルを直接ロード
これまでMNIST(2016/11/9)やCIFAR-10(2016/11/27)のようにPythonのNumPy array形式のデータを直接ダウンロードできる例ばかり扱ってきた。今回は、NumPy arrayのデータは提供されておらず、画像ファイルしか提供されていない。このようなケースでは、先にデータ拡張(2016/12/12)で使ったImageDataGenerator
が非常に役立つ。このクラスにはディレクトリを指定するとそこの画像ファイルを読み込む関数が提供されている。
下のような感じで使う。
# 訓練データとバリデーションデータを生成するジェネレータを作成 train_datagen = ImageDataGenerator( rescale=1.0 / 255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1.0 / 255) train_generator = train_datagen.flow_from_directory( 'data/train', target_size=(150, 150), batch_size=32, class_mode='binary') validation_generator = test_datagen.flow_from_directory( 'data/validation', target_size=(150, 150), batch_size=32, class_mode='binary')
ImageDataGenerator
でどのような画像拡張をするかオプションで指定できる。flow_from_directory()
に画像が含まれるディレクトリ名を指定してジェネレータを作成。このディレクトリに含まれる画像が「種画像」になる。- 指定したディレクトリの下にクラスごとにディレクトリ(dogs/cats)を作っておくとクラスも認識する。
- 画像ファイルのサイズが異なっていても
target_size
で指定した大きさにリサイズする。 - 今回はcats/dogsの2クラス分類なので
class_mode
にはbinary
を指定する。あとで別の例を紹介するが多クラス分類の場合はcategorical
を指定する。
あとはこのジェネレータが4Dテンソル形式(samples, rows, cols, channels)に変換した画像データをじゃんじゃん生成してくれるのだ!実際には自分で生成する必要さえなく、下のように訓練用の関数fit_generator()
にジェネレータを渡すだけでよい。これは楽できていいね。画像の読み込みとか地味に面倒だから。
# 訓練 history = model.fit_generator( train_generator, samples_per_epoch=2000, nb_epoch=nb_epoch, validation_data=validation_generator, nb_val_samples=800)
三種類の方法を比較
前置きが長かったけどここからが本題。次の3つのニューラルネットを学習して精度を比較してみたい。
- 小さな畳み込みニューラルネットをスクラッチから学習する
- VGG16が抽出した特徴を使って多層パーセプトロンを学習する
- VGG16をFine-tuningする
ちょっと長くなりそうなので次回へ。