Keras - Transferir aprendizaje - cambiar la forma del tensor de entrada

15

Esta publicación parece indicar que lo que quiero lograr no es posible. Sin embargo, no estoy convencido de esto: dado lo que ya he hecho, no veo por qué no puedo lograr lo que quiero hacer ...

Tengo dos conjuntos de datos de imágenes donde uno tiene imágenes de forma (480, 720, 3) mientras que el otro tiene imágenes de forma (540, 960, 3).

Inicialicé un modelo usando el siguiente código:

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

Ahora que he entrenado este modelo en el conjunto de datos anterior, me gustaría quitar la capa del tensor de entrada y anteponer el modelo con un nuevo tensor de entrada con una forma que coincida con las dimensiones de la imagen del último conjunto de datos.

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

que produce este error:

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'

En la publicación que vinculé, maz afirma que hay un desajuste de dimensión que impide cambiar la capa de entrada de un modelo; si este fuera el caso, entonces, ¿cómo es que puse una capa de entrada (480, 720, 3) al frente del modelo VGG16 que espera imágenes (224, 224, 3)?

Creo que un problema más probable es que el resultado de mi modelo anterior espera algo diferente de lo que le doy en función de lo que dice fchollet en esta publicación . Estoy sintácticamente confundido, pero creo que todo el x = Layer()(x)segmento está construyendo la capa pieza por pieza a partir de input-> output y simplemente arrojando una entrada diferente al frente lo está rompiendo.

Aunque realmente no tengo idea ...

¿Puede alguien aclararme cómo lograr lo que estoy tratando de hacer o, si no es posible, explicarme por qué no?

aweeeezy
fuente
lo has resuelto?
tktktk0711

Respuestas:

4

Puede hacer esto creando una nueva instancia de modelo VGG16 con la nueva forma de entrada new_shapey copiando sobre todos los pesos de capa. El código es aproximadamente

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())
r-zip
fuente
Intenté esto con inceptionV3, y se vuelve más y más lento a medida que continúa el ciclo
BachT
@ r-zip Me sale un error: ¿ 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) y esa es la capa de entrada, así que úsala [2:]?
mLstudent33
1

El ancho y la altura de salida de las dimensiones de salida de VGGnet son una parte fija del ancho y la altura de entrada porque las únicas capas que cambian esas dimensiones son las capas de agrupación. El número de canales en la salida se fija al número de filtros en la última capa convolucional. La capa de aplanar cambiará la forma para obtener una dimensión con la forma:

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

donde x es algún decimal <1.

El punto principal es que la forma de la entrada a las capas densas depende del ancho y la altura de la entrada a todo el modelo. La entrada de forma a la capa densa no puede cambiar ya que esto significaría agregar o eliminar nodos de la red neuronal.

Una forma de evitar esto es usar una capa de agrupación global en lugar de una capa plana (generalmente GlobalAveragePooling2D), esto encontrará que el promedio por canal hace que la forma de la entrada a las capas densas sea la (channels,)que no depende de la forma de entrada a Todo el modelo.

Una vez hecho esto, ninguna de las capas de la red depende del ancho y la altura de la entrada, por lo que la capa de entrada se puede cambiar con algo como

input_layer = InputLayer(input_shape=(480, 720, 3), name="input_1")
model.layers[0] = input_layer
Andrew Mcghie
fuente
model.layers[0] = input_layerno funciona para mí en TF 2.1. No hay error, pero la capa no se reemplaza realmente. Parece que copiar pesos puede ser más robusto (ver otras respuestas).
z0r
0

Aquí hay otra solución, no específica para el modelo VGG.

Tenga en cuenta que los pesos de la capa densa no se pueden copiar (y, por lo tanto, se inicializarán nuevamente). Esto tiene sentido, porque la forma de los pesos difiere en el modelo antiguo y en el nuevo.

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)
gebbissimo
fuente
0

Esto debería ser bastante fácil con kerassurgeon. Primero necesitas instalar la biblioteca; dependiendo de si está utilizando Keras a través de TensorFlow (con tf 2.0 y superior) o Keras como una biblioteca separada, debe instalarse de diferentes maneras.

Para Keras en TF: pip install tfkerassurgeon( https://github.com/Raukk/tf-keras-surgeon ). Para Keras independientes: pip install kerassurgeon( https://github.com/BenWhetton/keras-surgeon )

Para reemplazar la entrada (ejemplo con TF 2.0; código actualmente no probado):

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)
palabras por el contrario
fuente
0

La respuesta de @gebbissimo funcionó para mí en TF2 con solo pequeñas adaptaciones que comparto a continuación en una sola función:

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
Sergio Montazzolli
fuente
0

Así es como cambio el tamaño de entrada en el modelo Keras. Tengo dos modelos CNN, uno con tamaño de entrada [Ninguno, Ninguno, 3] mientras que el otro tiene tamaño de entrada [512,512,3]. Ambos modelos tienen los mismos pesos. Al usar set_weights (model.get_weights ()), los pesos del modelo 1 se pueden transferir al modelo 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())
Yaya
fuente