今回は、転移学習応用編ということで、ILSVRC-2014 (ImageNet Large Scale Visual Recognition Challenge)で優勝した、Inception-v3を用いて、転移学習を行ってみましょう

。Inception-v3の詳しい説明は、次のURLを参照してください。(https://arxiv.org/abs/1512.00567

今回は、特にGithub上のExampleのコードを追ってはいきません。というか良さげなコードがありませんでした。

まずは、モデルをロードします。

Kerasの便利なところは、有名なモデルはすでにライブラリに組み込んであり、呼び出しが簡易になっている点です。今回紹介するInception-v3以外にも、ResNet50やVGG16などがあります。
(https://keras.io/ja/applications/#inceptionv3)

使い方を確認すると、下記となっています。

Class
keras.applications.inception_v3.InceptionV3(include_top=True, weights=’imagenet’, input_tensor=None)

Arguments
・include_top: ネットワークの出力層側にある全結合層を含むかどうか.
・weights: None (ランダム初期化) か “imagenet” (ImageNetで学習した重み) の一方.
・input_tensor: モデルの入力画像として利用するためのオプションのKerasテンソル (すなわち,layers.Input()の出力)
・input_shape: オプショナルなshapeのタプル,include_topがFalseの場合のみ指定可能 (そうでないときは入力のshapeは(299, 299, 3) (tfのとき) か (3, 299, 299) (thのとき) ).正確に3つの入力チャンネルをもつ必要があり,width と height は139以上にする必要があります.例えば(150, 150, 3)は有効値.

今回、転移学習を行うということで、全結合層は自前で用意するため、include_topはFalseとします。

あと、何を題材にするか盛大に困ったわけですが、cifar10をテーマにしてみようと思います。

cifar10は、kerasがすでにサンプルのデータセットとして用意しているデータで、10のクラスにラベル付けされた50000枚の32×32訓練用カラー画像、10000枚のテスト用画像のデータセットから構成されるデータとなります。

ところが、Inception-v3は画像のサイズが139×139以上じゃないと受け付けないという仕様なわけで、リサイズが必要となります。というわけで、リサイズのコードを用意しました。OpenCVが全然動かなくてクビにしました。PILで対応です。さらに、ラベルをone hot vector形式に直します。これはいつもの流れですね。

<Code>
import keras
from PIL import Image
from keras.datasets import cifar10
from keras import applications
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense, GlobalAveragePooling2D, GlobalMaxPooling2D
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

(X_train_raw, y_train_raw), (X_test_raw, y_test_raw) = cifar10.load_data()

X_train = np.zeros((50000, 160, 160, 3))
for i,img in enumerate(X_train_raw):
pil_data = Image.fromarray(np.uint8(img))
resize_img=pil_data.resize((160,160),resample=Image.LANCZOS)
X_train[i] = np.asarray(resize_img)

X_test = np.zeros((10000, 160, 160, 3))
for i,img in enumerate(X_test_raw):
pil_data = Image.fromarray(np.uint8(img))
resize_img=pil_data.resize((160,160),resample=Image.LANCZOS)
X_test[i] = np.asarray(resize_img)

num_classes = 10
y_train = keras.utils.to_categorical(y_train_row, num_classes)
y_test = keras.utils.to_categorical(y_test_row, num_classes)

</Code>

次に、転移学習部分を用意しましょう。

まずは、モデルをロードします。この一行で世界最強のモデルを使えるとは、すごい時代です。最終段に10クラス分類する全結合層を用意します。いつもとレイヤーの重ね方が違うのですが、どうもこういうものみたいです。場合によっては、最終段近くのレイヤーは、フリーズさせないで学習させると面白いかもしれません。今回はなんと311レイヤーあるのですが(鬼CNNだ)、終わりのレイヤーも学習してみることにします(小物感)。これが後々辛くなるのですが…。

<Code>
base_model = keras.applications.inception_v3.InceptionV3(include_top=False, weights=’imagenet’, input_tensor=None, input_shape=(160,160,3))
x = base_model.output
# 単純にフラットにする場合はこちら
# x = Flatten()(x)
# チャネルごとの平均をとってフラットにする
x = GlobalAveragePooling2D()(x)

x = Dense(1024, activation=’relu’)(x)
x = Dropout(0.5)(x)
predictions = Dense(10, activation=’softmax’)(x)

model = Model(input=base_model.input, output=predictions)
model.summary()

for layer in model.layers[:310]:
layer.trainable = False

</Code>

最後に学習部分を用意して実行です。

転移学習では、ゆっくりと学習させるほうが良い結果が出るとされています。最適化関数もAdamではなく素直にSGDが良いみたいです。

<Code>
# compile the model with a SGD/momentum optimizer
# and a very slow learning rate.
model.compile(loss=’categorical_crossentropy’,
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=[‘accuracy’])

model.fit(X_train, y_train,
batch_size=200,
epochs=5,
verbose=1,
validation_data=(X_test, y_test))
</Code>

死ぬほど時間がかかります。終わりの2段の再学習でも死にそうになります。1エポックで2時間以上だと…。我がMac Book Airでは、Fine Tuning ですら1日仕事ですね。こういうところでビデオカードが欲しくなるんだと思います。

あ、精度ですか?
実行時間がかかりすぎるので、最後の全結合層だけ学習させるように変更しちゃいました。それでも1日ぶん回しても10エポック、結果は、62.3%でした。そもそも32×32の小さな画像ですし、GPUもなく学習回数も少ないので、まだ収束している様子はありませんでしたが、このあたりにしておきましょう。(Sho.)

#本連載もいよいよ最終回となりました。最後までお付き合い頂いてありがとうございました。皆様の参考になれば幸いです。(編集長)