Keras - Transfert d'apprentissage - modification de la forme du tenseur d'entrée


15

Ce message semble indiquer que ce que je veux accomplir n'est pas possible. Cependant, je ne suis pas convaincu de cela - compte tenu de ce que j'ai déjà fait, je ne vois pas pourquoi ce que je veux faire ne peut pas être réalisé ...

J'ai deux jeux de données d'images où l'un a des images de forme (480, 720, 3) tandis que l'autre a des images de forme (540, 960, 3).

J'ai initialisé un modèle en utilisant le code suivant:

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'])

Maintenant que j'ai formé ce modèle sur l'ancien jeu de données, j'aimerais supprimer la couche de tenseur d'entrée et ajouter le modèle avec un nouveau tenseur d'entrée avec une forme qui correspond aux dimensions de l'image de ce dernier jeu de données.

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')

ce qui donne cette erreur:

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'

Dans le message que j'ai lié, maz indique qu'il existe une incompatibilité de dimension qui empêche de changer la couche d'entrée d'un modèle - si tel était le cas, comment se fait-il que je mette une couche d'entrée (480, 720, 3) devant du modèle VGG16 qui attend (224, 224, 3) des images?

Je pense qu'un problème plus probable est que la sortie de mon ancien modèle attend quelque chose de différent de ce que je lui donne en fonction de ce que fchollet dit dans ce post . Je suis confus syntaxiquement, mais je crois que le x = Layer()(x)segment entier construit le calque morceau par morceau à partir de l'entrée-> sortie et simplement jeter une entrée différente devant le casse.

Je n'en ai vraiment aucune idée cependant ...

Quelqu'un peut-il m'éclairer sur la façon d'accomplir ce que j'essaie de faire ou, si ce n'est pas possible, m'expliquer pourquoi?


l'avez-vous résolu?
tktktk0711

Réponses:


4

Pour ce faire, créez une nouvelle instance de modèle VGG16 avec la nouvelle forme d'entrée new_shapeet copiez tous les poids de calque. Le code est à peu près

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())

J'ai essayé cela avec inceptionV3, et cela devient de plus en plus lent au fur et à mesure que la boucle continue
BachT

@ r-zip J'obtiens une erreur: 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) et c'est la couche d'entrée alors utilisez [2:]?
mLstudent33

1

La largeur et la hauteur de sortie des dimensions de sortie du VGGnet sont une partie fixe de la largeur et de la hauteur d'entrée car les seules couches qui modifient ces dimensions sont les couches de regroupement. Le nombre de canaux dans la sortie est fixé au nombre de filtres dans la dernière couche convolutionnelle. La couche aplatie la remodèlera pour obtenir une dimension avec la forme:

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

où x est un nombre décimal <1.

Le point principal est que la forme de l'entrée des couches Denses dépend de la largeur et de la hauteur de l'entrée de l'ensemble du modèle. L'entrée de forme dans la couche dense ne peut pas changer car cela signifierait ajouter ou supprimer des nœuds du réseau neuronal.

Une façon d'éviter cela est d'utiliser une couche de mise en commun globale plutôt qu'une couche aplatie (généralement GlobalAveragePooling2D), cela trouvera la moyenne par canal provoquant la forme de l'entrée des couches Denses (channels,)qui ne dépend pas de la forme d'entrée. l'ensemble du modèle.

Une fois cela fait, aucune des couches du réseau ne dépend de la largeur et de la hauteur de l'entrée, de sorte que la couche d'entrée peut être modifiée avec quelque chose comme

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

model.layers[0] = input_layerne fonctionne pas pour moi dans TF 2.1. Il n'y a pas d'erreur, mais le calque n'est pas réellement remplacé. Il semble que la copie des poids peut être plus robuste (voir les autres réponses).
z0r

0

Voici une autre solution, non spécifique au modèle VGG.

Notez que les poids de la couche dense ne peuvent pas être copiés (et seront donc nouvellement initialisés). Cela a du sens, car la forme des poids diffère dans l'ancien et le nouveau modèle.

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

Cela devrait être assez facile avec kerassurgeon. Vous devez d'abord installer la bibliothèque; selon que vous utilisez Keras via TensorFlow (avec tf 2.0 et supérieur) ou Keras en tant que bibliothèque distincte, il doit être installé de différentes manières.

Pour Keras dans TF: pip install tfkerassurgeon( https://github.com/Raukk/tf-keras-surgeon ). Pour Keras autonome: pip install kerassurgeon( https://github.com/BenWhetton/keras-surgeon )

Pour remplacer l'entrée (exemple avec TF 2.0; code actuellement non testé):

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 answer a fonctionné pour moi dans TF2 avec seulement de petites adaptations que je partage ci-dessous dans une seule fonction:

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

C'est ainsi que je modifie la taille d'entrée dans le modèle Keras. J'ai deux modèles CNN, l'un avec une taille d'entrée [Aucun, Aucun, 3] tandis que l'autre a une taille d'entrée [512 512,3]. Les deux modèles ont les mêmes poids. En utilisant set_weights (model.get_weights ()), les poids du modèle 1 peuvent être transférés vers le modèle 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())
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.