DeepLearning
Keras
TensorFlow1.4

tf.kerasでのvgg16 fine-tuningで悩んだ話

More than 1 year has passed since last update.

はじめに

[Tensorflow 1.4.0 : Release note]
tf.keras is now part of the core TensorFlow API.

とあるように ver 1.4.0よりtf.keras.XXXkerasに容易にアクセスできるようになりました.私も重い腰を上げ,素のtensorflowからkeras(tf.keras)への乗り換えをはじめています.特にモデルが大きくなる場合には記述の見通しがよく,kerasの威力を実感しています.

ただ,表題の通り1点よくわからない状況に出くわしたのでここにまとめてみます(正確な情報をご存知の方がおられればご教授いただけると嬉しいです).

環境

  • Ubuntu 16.04
  • Python 3.5
  • tensorflow 1.4.0

状況

vgg16のfine-tuningを試したかったので,以下のような暫定コードを書きました.block1-4は固定,block5とfc層だけ再学習するという前提の構成です.入力はダミーにしています.

if __name__ == "__main__":

    # vgg16
    input_tensor = k.layers.Input(shape=(224, 224, 3))
    vgg16_model = k.applications.VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
    # my fc_layer
    my_model = k.models.Sequential([
        k.layers.InputLayer(input_shape=vgg16_model.output_shape[1:]),
        k.layers.Flatten(),
        k.layers.Dense(1, activation='sigmoid')
    ])
    # freeze block1-4
    for layer in vgg16_model.layers[:15]:
        layer.trainable = False
    # build model
    model = k.models.Sequential([vgg16_model, my_model])
    model.summary()
    model.compile(loss='binary_crossentropy',
                  optimizer=k.optimizers.SGD(lr=1e-4),
                  metrics=['acc'])

    # training
    batch_size = 32
    dummy_x = np.zeros(shape=(batch_size,224,224,3))
    dummy_y = np.zeros(shape=(batch_size,1))

    model.fit(x=dummy_x,
              y=dummy_y,
              batch_size=batch_size)

これを走らせるとモデル自体の構築は完了しますが学習が失敗します.tensorflowのソースコードを少し見る限りk.applications.VGG16が提供する情報が足らず,学習データとのバインドに失敗しているようにみえます(具体的にはvgg16_model._feed_input_namesの情報が空).

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
vgg16 (Model)                (None, 7, 7, 512)         14714688  
_________________________________________________________________
sequential_1 (Sequential)    (None, 1)                 25089     
=================================================================
Total params: 14,739,777
Trainable params: 7,104,513
Non-trainable params: 7,635,264
_________________________________________________________________
Traceback (most recent call last):
  File "__main__.py", line 72, in <module>
    batch_size=batch_size)
  File "/usr/local/lib/python3.5/dist-packages/tensorflow/python/keras/_impl/keras/models.py", line 841, in fit
    initial_epoch=initial_epoch)
  File "/usr/local/lib/python3.5/dist-packages/tensorflow/python/keras/_impl/keras/engine/training.py", line 1535, in fit
    batch_size=batch_size)
  File "/usr/local/lib/python3.5/dist-packages/tensorflow/python/keras/_impl/keras/engine/training.py", line 1394, in _standardize_user_data
    exception_prefix='input')
  File "/usr/local/lib/python3.5/dist-packages/tensorflow/python/keras/_impl/keras/engine/training.py", line 69, in _standardize_input_data
    'expected no data, but got:', data)
ValueError: ('Error when checking model input: expected no data, but got:', array([[[[ 0.,  0.,  0.],

暫定対処

以下のようにダミーの入力レイヤーを足してやることでひとまず学習し始めるところまでは確認しました.
こういうものなのでしょうか?(それならそれで構わないのですが..)

if __name__ == "__main__":
    # dummy input layer
    dummy_input_layer = k.models.Sequential([
        k.layers.InputLayer(input_shape=(224, 224, 3))
    ])
    # vgg16
    input_tensor = k.layers.Input(shape=(224, 224, 3))
    vgg16_model = k.applications.VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
    # my fc_layer
    my_model = k.models.Sequential([
        k.layers.InputLayer(input_shape=vgg16_model.output_shape[1:]),
        k.layers.Flatten(),
        k.layers.Dense(1, activation='sigmoid')
    ])

    # freeze block1-4
    for layer in vgg16_model.layers[:15]:
        layer.trainable = False

    # build model
    model = k.models.Sequential([dummy_input_layer, vgg16_model, my_model])
    model.summary()
    model.compile(loss='binary_crossentropy',
                  optimizer=k.optimizers.SGD(lr=1e-4),
                  metrics=['acc'])

    # training
    batch_size = 32
    dummy_x = np.zeros(shape=(batch_size,224,224,3))
    dummy_y = np.zeros(shape=(batch_size,1))

    model.fit(x=dummy_x,
              y=dummy_y,
              batch_size=batch_size)