web-dev-qa-db-fra.com

Keras remplaçant la couche d'entrée

Le code que j'ai (que je ne peux pas changer) utilise le Resnet avec my_input_tensor comme capteur d'entrée.

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

En étudiant le code source , la fonction ResNet50 crée une nouvelle couche d'entrée de keras avec my_input_tensor, puis créez le reste du modèle. C'est le comportement que je veux copier avec mon propre modèle. Je charge mon modèle à partir du fichier h5.

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

Étant donné que ce modèle possède déjà un calque d'entrée, je souhaite le remplacer par un nouveau calque d'entrée défini par my_input_tensor.

Comment remplacer une couche d'entrée?

10
zcadqe

Lorsque vous avez enregistré votre modèle à l'aide de:

old_model.save('my_model.h5')

il sauvera ce qui suit:

  1. L'architecture du modèle, permettant de créer le modèle.
  2. Les poids du modèle.
  3. La configuration de formation du modèle (perte, optimiseur).
  4. L'état de l'optimiseur, permettant à l'entraînement de reprendre là où vous vous étiez arrêté auparavant.

Alors, lorsque vous chargez le modèle:

res50_model = load_model('my_model.h5')

vous devriez récupérer le même modèle, vous pouvez le vérifier en utilisant:

res50_model.summary()
res50_model.get_weights()

Maintenant, vous pouvez ouvrir le calque d'entrée et ajouter le vôtre en utilisant:

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

ajouter une nouvelle couche d'entrée:

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()
20
Milind Deore

Malheureusement, la solution de @MilindDeore n'a pas fonctionné pour moi. Bien que je puisse imprimer le résumé du nouveau modèle, je reçois une erreur "Matrix size incompatible" lors de la prédiction. Je suppose que cela a du sens, car la nouvelle forme d'entrée de la couche dense ne correspond pas à la forme des anciens poids de couche dense.

Voici donc une autre solution. La clé pour moi était d'utiliser "_layers" au lieu de "couches". Ce dernier ne semble renvoyer qu'une copie.

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
gebbissimo