MATHGRAM

主に数学とプログラミング、時々趣味について。

[Keras] パラメータの更新をさせないfreezeに関する実験

突然ですが, 一昨日のことです.

GANのKerasによる実装の中で使われていたfreezeについて質問をしてみました.

なぜこのような質問をしたかというと, 以前Sequentialで組んだモデルに対しmodel.trainable = Falseを使い層をfreezeさせようとしたのですが, summary()が出すnon-trainable params の値が変わらない, という結果になっていたからです.

問題点を具体的に見てみましょう.

modelA = Sequential([
    Dense(32, input_dim=784),
    Activation('relu'),
    Dense(10),
    Activation('softmax'),
])

modelB = Sequential([
    Dense(100, input_dim=10),
    Activation('relu'),
    Dense(10),
    Activation('softmax'),
])

# freeze
modelB.trainable = False

# コンパイルしないと反映されないので, 適当にコンパイルする.
modelB.compile(optimizer="adam", loss="categorical_crossentropy")

modelC = Sequential([modelA, modelB])

# modelCの内部情報を見る
modelC.summary()

出力

Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
sequential_5 (Sequential)        (None, 10)            25450                                        
____________________________________________________________________________________________________
sequential_6 (Sequential)        (None, 10)            2110        activation_8[0][0]               
====================================================================================================
Total params: 27,560
Trainable params: 27,560
Non-trainable params: 0
____________________________________________________________________________________________________

このようにNon-trainable params: 0となっていて一見 freezeは反映されていないように見えます.

まとめると, 僕は,

  • そもそもfreezeはlayerに使うもの
  • summary()を過信していた

の2点から, 上記のようなモデルの組み方では, 層のFreezeは行われないと思っていました.

そしたら, @tuntuku_syさんからこんなリプライが.

なんと!

issueの内容も実際にはFreezeできてそうってことでした. これはちゃんと理解しておかないと, 将来的に厄介なことになりそう!

ってことで実験しました.

とりあえず結果からいうとsummary()側のバグでした. 完全に自分の調査不足です. 少しでも疑問点があったらすぐに検証していく心構えが必要だなと感じました.

実験方法

とりあえず議論に上がっているmodel.trainableと, summary()に関して, どっちがバグってるのか確認したいと思います.
また層はDenseでのみ行うので, 他のlayerの場合は試していません. 注意してください.

summary()とは

model.summary()でfreezeされたパラメータ数を見ることができます.

from keras.models import Sequential
from keras.layers import Dense, Activation, Reshape, Input
from keras.layers.convolutional import UpSampling2D, Convolution2D
from keras.utils import np_utils
import numpy as np

model = Sequential([
    Dense(32, input_dim=784),
    Activation('relu'),
    Dense(10),
    Activation('softmax'),
])

model.compile(optimizer="adam", loss="categorical_crossentropy")
model.summary()

出力

____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
dense_22 (Dense)                 (None, 32)            25120       dense_input_5[0][0]              
____________________________________________________________________________________________________
activation_9 (Activation)        (None, 32)            0           dense_22[0][0]                   
____________________________________________________________________________________________________
dense_23 (Dense)                 (None, 10)            330         activation_9[0][0]               
____________________________________________________________________________________________________
activation_10 (Activation)       (None, 10)            0           dense_23[0][0]                   
====================================================================================================
Total params: 25,450
Trainable params: 25,450
Non-trainable params: 0
____________________________________________________________________________________________________

注目するべきは, 下の3行です. ここの数字と実際の重みに注目していきましょう.

get_weight()

今回重みを確認するために, 以下のような手段を取ろうと思います.

compare = np.equal(model.layers[2].get_weights()[0], model.layers[2].get_weights()[0])
np.all(compare)

出力

True

同じ引数を与えているので, Trueになります. fit()をすると…

w1 = model.layers[0].get_weights()[0]
model.fit(X, y)
w2 = model.layers[0].get_weights()[0]
compare = np.equal(w1, w2)
np.all(compare)

出力

False

もちろんFalseですね. 重みのarrayを受けて, 比較する. 至って単純な作業.

この二つの出力に注目しつつ実験します.

実験

概要

modelA に trainable=False としたmodelBをaddする.
結合したモデルにfitを使い, 学習前後でmodelBの重みを比較する.

コードと結果

# 学習前の重みをセットする
wa1 = modelA.layers[0].get_weights()[0]
wb1 = modelB.layers[0].get_weights()[0]
# 一応Deepcopy版も
wa1dc = deepcopy(modelB.layers[0].get_weights()[0])
wb1dc = deepcopy(modelB.layers[0].get_weights()[0])
#学習開始
modelC.compile(optimizer="adam", loss="categorical_crossentropy")
modelC.fit(X, y, nb_epoch=100)

# 学習後の重みを受け取る
wa2 = modelA.layers[0].get_weights()[0]
wb2 = modelB.layers[0].get_weights()[0]
# 比較する
compareA = np.equal(wa1, wa2)
compareB = np.equal(wb1, wb2)
print(np.all(compareA)) # False
print(np.all(compareB)) # True

出力

False
True

これはしっかりFreezeされてますね!ちゃんと理解できてよかったです. 僕もGANやろうかなー!

以上です.