こしあん
2018-10-24

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

Pocket
LINEで送る


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

BrestCancerデータセットをCNNで分類する 構造化データを畳み込みニューラルネットワーク(CNN)で分析することを考えます。BrestCancerデータセットはScikit-learnに用意されている、乳がんが良性か悪性かの2種類を分類する典型的な構造化データです。サンプル数569、データの次元30の典型的な構造化データです。 なぜ畳み込...
TPUで学習率減衰させる方法 TPUで学習率減衰したいが、TensorFlowのオプティマイザーを使うべきか、tf.kerasのオプティマイザーを使うべきか、あるいはKerasのオプティマイザーを使うべきか非常にややこしいことがあります。TPUで学習率を減衰させる方法を再現しました。 結論から TPU環境でtf.keras...
TensorFlowでコサイン類似度を計算する方法... TensorFlowで損失関数や距離関数に「コサイン類似度」を使うことを考えます。Scikit-learnでは簡単に計算できますが、同様にTensorFlowでの行列演算でも計算できます。それを見ていきます。 コサイン類似度 コサイン類似度は、ユークリッド距離と同様に距離関数として使われる評価...
tf.tensordotで行列積を表現するための設定... TensorFlowのtensordotという関数はとても強力で、テンソルに対する行列積に対する計算をだいたい表現できます。しかし、軸の設定がいまいちよくわからなかったので、確かめてみました。 2x2行列同士の積の場合(Numpy) まず単純に2x2行列同士の(ドット)積を考えます。まずはNu...
Chainerで画像の前処理やDataAugmentationをしたいときはDatasetMixin... Chainerにはデフォルトでランダムクロップや標準化といった、画像の前処理やDataAugmentation用の関数が用意されていません。別途のChainer CVというライブラリを使う方法もありますが、chainer.dataset.DatasetMixinを継承させて独自のデータ・セットを定...
Pocket
Delicious にシェア

Add a Comment

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