こしあん
2018-10-24

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


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派かなと思います(もちろんケースバイケースです)。

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

Related Posts

KerasのModelCheckpointのsave_best_onlyは何を表すのか?... Kerasには「モデルの精度が良くなったときだけ係数を保存する」のに便利なModelCheckpointというクラスがあります。ただこのsave_best_onlyがいまいち公式の解説だとピンとこないので調べてみました。 ModelCheckpointとは? 公式ドキュメントより ke...
ColabのTPUでNASNet Largeを訓練しようとして失敗した話... ColabのTPUはとてもメモリ容量が大きく、計算が速いのでモデルのパラメーターを多くしてもそこまでメモリオーバーor遅くなりません。ただし、あまりにモデルが深すぎると訓練の初期設定で失敗することがあります。NASNet Largeを訓練しようとして発生しました。これを見ていきます。 CIFAR...
Kerasでメモリ使用量を減らしたかったらmax_queue_sizeを調整しよう... Kerasで大きめの画像を使ったモデルを訓練していると、メモリが足りなくなるということがよくあります。途中処理の変数のデータ型(np.uint8)を変えるのだけではなく、max_queue_sizeの調整をする必要があることがあります。それを見ていきます。 メモリサイズの目安 ニューラルネット...
KerasでSTL-10を扱う方法 スタンフォード大学が公開している画像データセットに「STL-10」というのがあります。これはCIFAR-10に似た形式ながら、高画質かつ教師なし学習にも応用できる便利なデータです。PyTorchではtorchvisionを使うと簡単に読み込めるのですが、Kerasではデフォルトで容易されていないの...
Kerasでランドマーク検出用の損失関数を作る上でのポイント... ランドマーク検出やオブジェクト検出では、yに最初に物体やランドマークが存在する確率をおいて、それ以降に座標を配置するというようなデータ構造を取ります。その場合、カスタム損失関数を定義する必要が出てきますが、どのように定義するれば良いでしょうか。それを見ていきます。 Kerasの損失関数 分類問...

Add a Comment

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