Keras заменяет входной слой

Код, который у меня есть (который я не могу изменить), использует Resnet с my_input_tensor в качестве input_tensor.

model1 = keras.applications.resnet50.ResNet50(input_tensor=my_input_tensor, weights='imagenet')

Исследуя исходный код, функция ResNet50 создает новый входной слой keras с my_input_tensor, а затем создает остальную часть модели. Это поведение, которое я хочу скопировать с моей собственной моделью. Я загружаю свою модель из файла h5.

model2 = keras.models.load_model('my_model.h5')

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

Как я могу заменить входной слой?

Ответ 1

Когда вы сохранили свою модель, используя:

old_model.save('my_model.h5')

он сохранит следующее:

  1. Архитектура модели, позволяющая создать модель.
  2. Вес модели.
  3. Конфигурация обучения модели (потеря, оптимизатор).
  4. Состояние оптимизатора, позволяющее возобновить обучение с того места, где вы раньше.

Итак, когда вы загружаете модель:

res50_model = load_model('my_model.h5')

вы должны вернуть ту же модель, вы можете проверить то же самое:

res50_model.summary()
res50_model.get_weights()

Теперь вы можете добавить слой ввода и добавить свой собственный:

res50_model.layers.pop(0)
res50_model.summary()

добавить новый уровень ввода:

newInput = Input(batch_shape=(0,299,299,3))    # let us say this new InputLayer
newOutputs = res50_model(newInput)
newModel = Model(newInput, newOutputs)

newModel.summary()
res50_model.summary()

Ответ 2

Решение от @MilindDeore у меня не сработало, к сожалению. Несмотря на то, что я могу напечатать сводку по новой модели, я получаю сообщение об ошибке "Размер матрицы несовместим" при прогнозировании. Я думаю, это имеет смысл, поскольку новая входная форма плотного слоя не соответствует форме весов старого плотного слоя.

Итак, вот еще одно решение. Ключом для меня было использование "_layers" вместо "layer". Последний, кажется, только возвращает копию.

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)

Ответ 3

Layers.pop(0) или что-то подобное не работает.

У вас есть два варианта, которые вы можете попробовать:

1.

Вы можете создать новую модель с необходимыми слоями.

Относительно простой способ сделать это - i) извлечь конфигурацию json модели, ii) изменить ее соответствующим образом, iii) создать из нее новую модель, а затем iv) скопировать весовые коэффициенты. Я просто покажу основную идею.

я) извлечь конфигурацию

model_config = model.get_config()

ii) изменить конфигурацию

input_layer_name = model_config['layers'][0]['name']
model_config['layers'][0] = {
                      'name': 'new_input',
                      'class_name': 'InputLayer',
                      'config': {
                          'batch_input_shape': (None, 300, 300),
                          'dtype': 'float32',
                          'sparse': False,
                          'name': 'new_input'
                      },
                      'inbound_nodes': []
                  }
model_config['layers'][1]['inbound_nodes'] = [[['new_input', 0, 0, {}]]]
model_config['input_layers'] = [['new_input', 0, 0]]

ii) создать новую модель

new_model = model.__class__.from_config(model_config, custom_objects={})  # change custom objects if necessary

ii) копирование весов

# iterate over all the layers that we want to get weights from
weights = [layer.get_weights() for layer in model.layers[1:]]
for layer, weight in zip(new_model.layers[1:], weights):
    layer.set_weights(weight)

2.

Вы можете попробовать библиотеку вроде kerassurgeon (я ссылаюсь на форк, который работает с версией tenorflow keras). Обратите внимание, что операции вставки и удаления работают только при определенных условиях, таких как совместимые размеры.

from kerassurgeon.operations import delete_layer, insert_layer

model = delete_layer(model, layer_1)
# insert new_layer_1 before layer_2 in a model
model = insert_layer(model, layer_2, new_layer_3)