Keras - Трансферное обучение - изменение формы входного тензора


15

Этот пост, кажется, указывает на то, что то, чего я хочу достичь, невозможно. Однако я не уверен в этом - учитывая то, что я уже сделал, я не понимаю, почему то, что я хочу сделать, не может быть достигнуто ...

У меня есть два набора данных изображений, где у одного есть изображения формы (480, 720, 3), а у другого - изображения формы (540, 960, 3).

Я инициализировал модель, используя следующий код:

input = Input(shape=(480, 720, 3), name='image_input')

initial_model = VGG16(weights='imagenet', include_top=False)

for layer in initial_model.layers:
    layer.trainable = False

x = Flatten()(initial_model(input))
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(14, activation='linear')(x)

model = Model(inputs=input, outputs=x)
model.compile(loss='mse', optimizer='adam', metrics=['mae'])

Теперь, когда я натренировал эту модель на предыдущем наборе данных, я хотел бы отключить слой входного тензора и добавить в модель новый входной тензор с формой, которая соответствует размерам изображения последнего набора данных.

model = load_model('path/to/my/trained/model.h5')
old_input = model.pop(0)
new_input = Input(shape=(540, 960, 3), name='image_input')
x = model(new_input)
m = Model(inputs=new_input, outputs=x)
m.save('transfer_model.h5')

что приводит к этой ошибке:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2506, in save
    save_model(self, filepath, overwrite, include_optimizer)
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/models.py", line 106, in save_model
    'config': model.get_config()
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2322, in get_config
    layer_config = layer.get_config()
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2370, in get_config
    new_node_index = node_conversion_map[node_key]
KeyError: u'image_input_ib-0'

В сообщении, которое я связал, maz утверждает, что существует несоответствие размеров, которое не позволяет изменить входной слой модели - если это так, то как мне поместить (480, 720, 3) входной слой перед модели VGG16, которая ожидает (224, 224, 3) изображения?

Я думаю, что более вероятная проблема заключается в том, что результаты моей бывшей модели ожидают чего-то другого, чем то, что я даю, основываясь на том, что fchollet говорит в этом посте . Я синтаксически запутался, но я полагаю, что весь x = Layer()(x)сегмент строит слой слой за куском из input-> output, и просто выбрасывая другой вход вперед, это ломает его.

Я действительно понятия не имею, хотя ...

Может кто-нибудь объяснить мне, как выполнить то, что я пытаюсь сделать, или, если это невозможно, объяснить, почему нет?


ты решил это?
tktktk0711

Ответы:


4

Вы можете сделать это, создав новый экземпляр модели VGG16 с новой формой ввода new_shapeи скопировав все веса слоев. Код примерно

new_model = VGG16(weights=None, input_shape=new_shape, include_top=False)
for new_layer, layer in zip(new_model.layers[1:], model.layers[1:]):
    new_layer.set_weights(layer.get_weights())

Пробовал это с inceptionV3, и он становится все медленнее и медленнее, поскольку цикл продолжается
BachT

@ r-zip Я получаю сообщение об ошибке: Traceback (most recent call last): File "predict_video11.py", line 67, in <module> new_layer.set_weights(layer.get_weights()) File "/usr/local/lib/python2.7/dist-packages/keras/engine/base_layer.py", line 1057, in set_weights 'provided weight shape ' + str(w.shape)) ValueError: Layer weight shape (3, 3, 33, 64) not compatible with provided weight shape (3, 3, 9, 64) и что такое входной слой, так что используйте [2:]?
mLstudent33

1

Ширина и высота вывода выходных размеров VGGnet являются фиксированной частью ширины и высоты ввода, поскольку единственными слоями, которые изменяют эти размеры, являются слои пула. Количество каналов на выходе фиксируется количеством фильтров в последнем сверточном слое. Сглаженный слой изменит это, чтобы получить одно измерение с формой:

((input_width * x) * (input_height * x) * channels)

где х - десятичное число <1.

Суть в том, что форма входных данных для плотных слоев зависит от ширины и высоты входных данных для всей модели. Ввод формы в плотный слой не может измениться, поскольку это будет означать добавление или удаление узлов из нейронной сети.

Один из способов избежать этого - использовать глобальный слой пула, а не плоский слой (обычно GlobalAveragePooling2D), в котором будет найдено среднее значение для каждого канала, в результате чего форма входа в плотные слои будет просто такой, (channels,)которая не зависит от формы входа для вся модель.

Как только это будет сделано, ни один из слоев в сети не будет зависеть от ширины и высоты входных данных, поэтому входной слой можно изменить с помощью чего-то вроде

input_layer = InputLayer(input_shape=(480, 720, 3), name="input_1")
model.layers[0] = input_layer

model.layers[0] = input_layerне работает для меня в TF 2.1. Там нет ошибки, но слой на самом деле не заменен. Похоже, что копирование весов может быть более надежным (см. Другие ответы).
z0r

0

Вот еще одно решение, не характерное для модели VGG.

Обратите внимание, что веса плотного слоя не могут быть скопированы (и, следовательно, будут заново инициализированы). Это имеет смысл, потому что форма весов отличается в старой и новой модели.

import keras
import numpy as np

def get_model():
    old_input_shape = (20, 20, 3)
    model = keras.models.Sequential()
    model.add(keras.layers.Conv2D(9, (3, 3), padding="same", input_shape=old_input_shape))
    model.add(keras.layers.MaxPooling2D((2, 2)))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(1, activation="sigmoid"))
    model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=0.0001), metrics=['acc'], )
    model.summary()
    return model

def change_model(model, new_input_shape=(None, 40, 40, 3)):
    # replace input shape of first layer
    model._layers[1].batch_input_shape = new_input_shape

    # feel free to modify additional parameters of other layers, for example...
    model._layers[2].pool_size = (8, 8)
    model._layers[2].strides = (8, 8)

    # rebuild model architecture by exporting and importing via json
    new_model = keras.models.model_from_json(model.to_json())
    new_model.summary()

    # copy weights from old model to new one
    for layer in new_model.layers:
        try:
            layer.set_weights(model.get_layer(name=layer.name).get_weights())
        except:
            print("Could not transfer weights for layer {}".format(layer.name))

    # test new model on a random input image
    X = np.random.rand(10, 40, 40, 3)
    y_pred = new_model.predict(X)
    print(y_pred)

    return new_model

if __name__ == '__main__':
    model = get_model()
    new_model = change_model(model)

0

Это должно быть довольно легко kerassurgeon. Сначала вам нужно установить библиотеку; в зависимости от того, используете ли вы Keras через TensorFlow (с tf 2.0 и выше) или Keras как отдельную библиотеку, его необходимо устанавливать по-разному.

Для Keras в TF: pip install tfkerassurgeon( https://github.com/Raukk/tf-keras-surgeon ). Для автономного Keras: pip install kerassurgeon( https://github.com/BenWhetton/keras-surgeon )

Чтобы заменить ввод (например, TF 2.0; текущий непроверенный код):

from tensorflow import keras  # or import keras for standalone version
from tensorflow.keras.layers import Input

model = load_model('path/to/my/trained/model.h5')
new_input = Input(shape=(540, 960, 3), name='image_input')

# or kerassurgeon for standalone Keras
from tfkerassurgeon import delete_layer, insert_layer

model = delete_layer(model.layers[0])
# inserts before layer 0
model = insert_layer(model.layers[0], new_input)

0

Ответ @gebbissimo работал для меня в TF2 с небольшими изменениями, которые я разделяю ниже в одной функции:

def change_input_size(model,h,w,ch=3):
   model._layers[0]._batch_input_shape = (None,h,w,ch)
   new_model = keras.models.model_from_json(model.to_json())
   new_model.summary()
   for layer,new_layer in zip(model.layers,new_model.layers):
      new_layer.set_weights(layer.get_weights())
   return new_model

0

Вот как я изменяю размер ввода в модели Keras. У меня есть две модели CNN, одна с размером входного сигнала [None, None, 3], а другая с размером входного сигнала [512,512,3]. Обе модели имеют одинаковый вес. Используя set_weights (model.get_weights ()), веса модели 1 можно перенести в модель 2

inputs = Input((None, None, 3))
.....
model = Model(inputs=[inputs], outputs=[outputs])
model.compile(optimizer='adam', loss='mean_squared_error')
model.load_weights('my_model_name.h5')

inputs2 = Input((512, 512, 3))
....
model2 = Model(inputs=[inputs2], outputs=[outputs])
model2.compile(optimizer='adam', loss='mean_squared_error')
model2.set_weights(model.get_weights())
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.