Kerasで大規模な画像分類 - vgg16 転移学習 -

この記事は1年以上前に公開されたものです。情報が古くなっている可能性があります。
1 イントロ
最近機械学習の勉強を始め、Kaggleにも参加するようになったので、自身の備忘録を兼ねて、ブログに記載する。
今回は画像分類のコンペに参加したので、 その時にvgg16の転移学習について調べた内容をまとめる。
2 データについて
データセットは下記のとおり。 今までMNISTとかしかやったことなかったので、非常に大きなデータセットに感じたが、 現実にはそうでもないんでしょうね。
- カテゴリ:約15000
- 学習データの規模:数は約120万。300GB強
- 学習データのサイズ:ばらばら。1600*1200とか。
- テストデータ:約12万
3 実装
3.1 画像データの学習
まず、コード全文。
1from keras.preprocessing.image import ImageDataGenerator2from keras import optimizers3from keras.applications.vgg16 import VGG164from keras.layers import Dense, Dropout, Flatten, Input, BatchNormalization5from keras.models import Model, Sequential6from keras.callbacks import ModelCheckpoint7import numpy as np8 9train_data_dir = "/train/"10validation_data_dir = "/validation/"11 12train_datagen = ImageDataGenerator(rescale=1. / 255)13validation_datagen = ImageDataGenerator(rescale=1. / 255)14 15img_width, img_height = 200, 15016nb_train_samples = 91564917nb_validation_samples = 30209118epochs = 5019batch_size = 6420nb_category = 1495121 22train_generator = train_datagen.flow_from_directory(23 train_data_dir,24 target_size=(img_width, img_height),25 batch_size=batch_size,26 class_mode="categorical")27 28validation_generator = validation_datagen.flow_from_directory(29 validation_data_dir,30 target_size=(img_width, img_height),31 batch_size=batch_size,32 class_mode="categorical")33 34#input_tensorの定義35input_tensor = Input(shape=(img_width, img_height, 3))36 37vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)38 39top_model = Sequential()40top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))41top_model.add(Dense(256, activation='relu', kernel_initializer='he_normal'))42top_model.add(BatchNormalization())43top_model.add(Dropout(0.5))44top_model.add(Dense(nb_category, activation='softmax'))45 46# vgg16とtop_modelを連結47model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))48 49# 15層目までの重みを固定50for layer in model.layers[:15]:51 layer.trainable = False52 53optimizer = optimizers.rmsprop(lr=5e-7, decay=5e-5)54model.compile(loss='categorical_crossentropy',55 optimizer=optimizer,56 metrics=['accuracy'])57 58checkpoint_cb = ModelCheckpoint("snapshot/{epoch:03d}-{val_acc:.5f}.hdf5", save_best_only=True)59 60model.fit_generator(61 train_generator,62 steps_per_epoch=nb_train_samples // batch_size,63 epochs=epochs,64 validation_data=validation_generator,65 validation_steps=nb_validation_samples // batch_size,66 callbacks=[checkpoint_cb])67 68# モデルを保存69model.save("model.h5")70 71model.summary()3.1.1 大規模データでの学習
よくチュートリアル等で見かけるのが、下記のように、学習データをnumpy.arrayとしてロードするパターン。
1(X_train, y_train), (X_test, y_test) = mnist.load_data()しかしながら、今回のように大規模なデータの場合、全データをメモリ上に展開するのは不可能なので、flow_from_directory関数を使うのがよさそうである。この関数はリアルタイムにデータを拡張しながら、画像データを処理してくれる。
使い方として、まず、ImageDataGeneratorを作成する。
1train_datagen = ImageDataGenerator(rescale=1. / 255)2validation_datagen = ImageDataGenerator(rescale=1. / 255)このクラスは、前処理を行いつつ、画像データのバッチ生成を行ってくれる。今回はシンプルにrescaleだけを行う。1/255としているのは、画像のRGBの値域が0-255であるのを0-1に正規化するためである。
次に、データを読み込む。
1train_generator = train_datagen.flow_from_directory(2 train_data_dir,3 target_size=(img_width, img_height),4 batch_size=batch_size,5 class_mode="categorical")6 7validation_generator = validation_datagen.flow_from_directory(8 validation_data_dir,9 target_size=(img_width, img_height),10 batch_size=batch_size,11 class_mode="categorical")今回はカテゴリーの分類なので、class_modeにcategoricalを指定する。
この時に気を付けないといけないのが、フォルダの構成。下記のように、分類するクラスごとにサブフォルダを作成しておかないといけない。
1data/2 train/3 classA/4 aaa.jpg5 bbb.jpg6 ...7 classB/8 ccc.jpg9 ddd.jpg10 ...11 12 validation/13 classA/14 eee.jpg15 fff.jpg16 ...17 classB/18 ggg.jpg19 hhh.jpg20 ...3.1.2 vgg16モデルの利用
Kerasで使えるVGG16モデルを使って学習を行う。
1#input_tensorの定義2input_tensor = Input(shape=(img_width, img_height, 3))3 4vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)5 6top_model = Sequential()7top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))8top_model.add(Dense(256, activation='relu', kernel_initializer='he_normal'))9top_model.add(BatchNormalization())10top_model.add(Dropout(0.5))11top_model.add(Dense(nb_category, activation='softmax'))12 13# vgg16とtop_modelを連結14model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))15 16# 15層目までの重みを固定17for layer in model.layers[:15]:18 layer.trainable = False19 20optimizer = optimizers.rmsprop(lr=5e-7, decay=5e-5)21model.compile(loss='categorical_crossentropy',22 optimizer=optimizer,23 metrics=['accuracy'])24 25checkpoint_cb = ModelCheckpoint("snapshot/{epoch:03d}-{val_acc:.5f}.hdf5", save_best_only=True)26 27model.fit_generator(28 train_generator,29 steps_per_epoch=nb_train_samples // batch_size,30 epochs=epochs,31 validation_data=validation_generator,32 validation_steps=nb_validation_samples // batch_size,33 callbacks=[checkpoint_cb])まず、デフォルトで用意されているvgg16モデルを使う。 この時に、入力のデータセットのサイズを指定する。
1input_tensor = Input(shape=(img_width, img_height, 3))2 3vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)次に、自分で作ったモデルをくっつける。 その前提で、上記の引数のinclude_topはFalseにしている。
1top_model = Sequential()2top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))3top_model.add(Dense(256, activation='relu', kernel_initializer='he_normal'))4top_model.add(BatchNormalization())5top_model.add(Dropout(0.5))6top_model.add(Dense(nb_category, activation='softmax'))7 8# vgg16とtop_modelを連結9model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))正直に言って、ここで指定しているrelu関数とかは、ちゃんと理解して使っているわけではない。 なので、ここのモデルの中身はあまり参考にならないと思う。
次に重みを固定する。これを指定しない場合は、一から重みを学習しなおすのだろうけど、それだと(今回の場合は)意味ないので、今回は15層まで固定する。
1for layer in model.layers[:15]:2 layer.trainable = Falseモデルのコンパイル。この時に、optimizerを指定する。 これはパラメータを更新するときに、どういうアルゴリズムを使うかという指定。
1optimizer = optimizers.rmsprop(lr=5e-7, decay=5e-5)2model.compile(loss='categorical_crossentropy',3 optimizer=optimizer,4 metrics=['accuracy'])最後に学習。callbacksにmodelcheckpointを設定しておくと、途中の結果を保存できる。なくてもよい。
1checkpoint_cb = ModelCheckpoint("snapshot/{epoch:03d}-{val_acc:.5f}.hdf5", save_best_only=True)2 3model.fit_generator(4 train_generator,5 steps_per_epoch=nb_train_samples // batch_size,6 epochs=epochs,7 validation_data=validation_generator,8 validation_steps=nb_validation_samples // batch_size,9 callbacks=[checkpoint_cb])3.2 予測
1from keras.preprocessing.image import ImageDataGenerator2from keras import optimizers3from keras.applications.vgg16 import VGG164from keras.layers import Dense, Dropout, Flatten, Input, BatchNormalization5from keras.models import Model, Sequential, load_model6import pandas as pd7import numpy as np8import os9from pandas import DataFrame10 11test_data_dir = "/test/"12 13test_datagen = ImageDataGenerator(rescale=1. / 255)14 15img_width, img_height = 200, 15016nb_test_samples = 11547417batch_size = 118nb_category = 1495119 20test_generator = test_datagen.flow_from_directory(21 test_data_dir,22 target_size=(img_width, img_height),23 batch_size=batch_size,24 class_mode=None,25 shuffle=False)26 27model = load_model("model.h5")28 29pred = model.predict_generator(30 test_generator,31 steps=nb_test_samples,32 verbose=1)さっきとそんなに変わりません。同じようにflow_from_directoryでデータを読み込むが、注意点はテストデータ(=正解クラス不明)の場合でもサブフォルダが必要だということ。例えば下記のような構成で、データをサブフォルダにまとめておかないと、うまく動きません。地味にはまった。。
1data/2 test/3 sub/4 aaa.jpg5 bbb.jpg6 ...ただ、クラスは不明なので、class_modeはNoneにしておくこと。
4 まとめ
- vgg16使う場合の一通りの流れを理解。
- 巨大なデータは
flow_from_directoryで読み込む。 - その場合はフォルダ構成に注意。



