突然ですが, 一昨日のことです.
@tuntuku_sy 初めまして, こんにちは. Kerasをメインで使っているものです. ブログ拝見させていただきました. 一つ気になったのですが, そのtrainable=Falseの使い方で, dis modelをfreezeできてますでしょうか?
— Kento Watanabe (@K3nt0W) 2017年2月7日
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さんからこんなリプライが.
@K3nt0W なるほど、ご指摘ありがとうございます。こちらでももう少し調べてみますが、これとか見るとtrainable Falseの問題というよりはsummary()側の問題で実はちゃんとフリーズできているのかもしれません。 https://t.co/tANtlnhHk7
— Shinya Yuki (@tuntuku_sy) 2017年2月7日
なんと!
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やろうかなー!
以上です.