こしあん
2018-10-24

Kerasで転移学習用にレイヤー名とそのインデックスを調べる方法


7.3k{icon} {views}


Kerasで転移学習をするときに、学習済みモデルのレイヤーの名前と、そのインデックス(何番目にあるかということ)の対応を知りたいことがあります。その方法を解説します。

転移学習とは

転移学習とは、ImageNetなど何百万もの大量の画像で事前学習させたモデルを使い、それを「特徴量検出器」として訓練させたい画像で再訓練させる方法です。fine-tuningともいいます。

もう少し直感的な理解をするなら、例えば犬小屋を作るプロフェッショナルを作りたいとしましょう。これは自分が作りたいモデルに相当します。「日曜大工すらほとんどやらない素人を一から訓練させるよりも、その筋のエキスパートである大工さんを連れてきて犬小屋の作り方を教えたほうが習得が早い」……というのが転移学習の理屈です。大工さんを連れてくるというのは事前学習させたモデルに相当します。

畳み込みニューラルネットワークの場合、入力に近い層はエッジ検出など低レベルの処理を行う一方で、出力に近い層は何が写っているかどこに写っているかといった意味の検出、つまり高レベルの処理を行うようになります。転移学習のポイントとは、低レベルの処理は共通のものとして訓練済みモデルの係数で固定させ、高レベルの部分だけ再訓練させるということです。どこまで訓練させるかは自分が持っている画像の量によって変わります。

どこまで訓練するかを決めるときに必要

さて、Kerasで実際に実装するときは、レイヤーのTrainableをTrueかFalseかで切り替えることで、係数の固定有無を変えることができます。実際にはforループなどで、ある深さまでのレイヤーのTraiableをFalseとします。でも「その深さの値ってどう取得するの?」というのが今回の主題です。

いきなり答えを行きましょう。以下のコードで調べられます。これはResNet50を転移学習させるケースです。

from keras.applications.resnet50 import ResNet50

def enumerate_layers():
    # 確認用。サマリーとレイヤー名とindexの対応を調べる
    resnet = ResNet50(include_top=False, weights="imagenet", input_shape=(224, 224, 3))
    resnet.summary()
    for i, layer in enumerate(resnet.layers):
        print(i, layer.name)

summaryから抜き出しました。

add_13 (Add)                    (None, 14, 14, 1024) 0           bn4f_branch2c[0][0]
                                                                 activation_37[0][0]
__________________________________________________________________________________________________
activation_40 (Activation)      (None, 14, 14, 1024) 0           add_13[0][0]
__________________________________________________________________________________________________
res5a_branch2a (Conv2D)         (None, 7, 7, 512)    524800      activation_40[0][0]
__________________________________________________________________________________________________
bn5a_branch2a (BatchNormalizati (None, 7, 7, 512)    2048        res5a_branch2a[0][0]

ピクセル数が14→7に落ちている「res5a_brance2a」というところから訓練させ、それ以前の係数は固定させたいとします。もちろんこの固定は一例なのでこれが正解ではありません。では「res5a_branch2a」とは何層目なのかというとforループの部分のコードで見れます。

141 add_13
142 activation_40
143 res5a_branch2a
144 bn5a_branch2a

インデックス=143がres5a_brance2aということがわかりました。ではレイヤ0~142までの係数を固定しましょう。

for i in range(143):
    resnet.layers[i].trainable=False

これでOKです。range(N)というのは0~N-1までの数を列挙することに注意しましょう。訓練させる深さを変える場合も同様にして調べればよいです。

全結合層も忘れずに

「include_top=False」でモデルを読み込んだ場合、全結合層(Softmaxなど)が入っていません。ここは手動でレイヤーを追加する必要があります。

from keras.layers import GlobalAveragePooling2D, Dense
from keras.models import Model

x = GlobalAveragePooling2D()(resnet.output)
x = Dense(10, activation="softmax")(x)
model = Model(resnet.inputs, x)

model.summary()

10クラスの分類の例です。GlobalAveragePoolingを使うかFlattenを使うかは好みで。ResNet50の場合出力チャンネルが2048と結構多いので、自分はGlobalAveragePooling派かなと思います(もちろんケースバイケースです)。

以上です。これで転移学習も完璧ですね。



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です