Comportamiento extraño de la función de pérdida en el modelo de keras, con base convolucional pre-entrenada

Estoy tratando de crear un modelo en Keras para hacer predicciones numéricas de las imágenes. Mi modelo tiene una base convolucional densenet121 , con un par de capas adicionales en la parte superior. Todas las capas, excepto las dos últimas, se configuran en layer.trainable = False . Mi pérdida es el error cuadrático medio, ya que es una tarea de regresión. Durante el entrenamiento, obtengo una loss: ~3 , mientras que la evaluación en el mismo lote de datos da una loss: ~30 :

 model.fit(x=dat[0],y=dat[1],batch_size=32) 

Época 1/1 32/32 [===============================] – 0s 11ms / step – pérdida: 2.5571

 model.evaluate(x=dat[0],y=dat[1]) 

32/32 [==============================] – 2s 59ms / step 29.276123046875

Alimento exactamente las mismas 32 imágenes durante el entrenamiento y la evaluación. Y también y_pred=model.predict(dat[0]) pérdida usando los valores pronosticados de y_pred=model.predict(dat[0]) y luego construí el error cuadrático medio usando numpy. El resultado fue el mismo que el que obtuve de la evaluación (es decir, 29.276123 …).

Se sugirió que este comportamiento podría deberse a BatchNormalization capas de BatchNormalization en base convolucional ( discusión en github ). Por supuesto, todas BatchNormalization capas de BatchNormalization en mi modelo se han establecido en layer.trainable=False también. ¿Tal vez alguien ha encontrado este problema y ha descubierto la solución?

    Parece que encontré la solución. Como he sugerido, el problema es con las capas BatchNormalization. Hacen cosas de árbol 1) restan la media y se normalizan por estándar 2) recostackn estadísticas sobre media y estándar utilizando el promedio de carrera 3) entrenan dos parámetros adicionales (dos por nodo). Cuando uno se puede configurar en Falso, estos dos parámetros se congelan y la capa también deja de recostackr estadísticas en media y estándar. Pero parece que la capa aún realiza la normalización durante el tiempo de entrenamiento utilizando el lote de entrenamiento . Lo más probable es que sea un error en keras o tal vez lo hicieron a propósito por alguna razón. Como resultado, los cálculos sobre la propagación hacia adelante durante el tiempo de entrenamiento son diferentes en comparación con el tiempo de predicción , aunque el atributo entrenable se establece en Falso .

    Hay dos soluciones posibles que se me ocurren:

    1. Para configurar todas las capas de BatchNormalization a entrenables. En este caso, estas capas recostackrán estadísticas de su conjunto de datos en lugar de usar una pre-entrenada (¡que puede ser significativamente diferente!). En este caso, ajustará todas las capas de BatchNorm a su conjunto de datos personalizado durante la capacitación.
    2. Dividir el modelo en dos partes model=model_base+model_top . Después de eso, use model_base para extraer las características de model_base.predict() y luego alimente estas características en model_top y entrene solo a model_top .

    Acabo de probar la primera solución y parece que está funcionando:

     model.fit(x=dat[0],y=dat[1],batch_size=32) Epoch 1/1 32/32 [==============================] - 1s 28ms/step - loss: **3.1053** model.evaluate(x=dat[0],y=dat[1]) 32/32 [==============================] - 0s 10ms/step **2.487905502319336** 

    Esto fue después de un poco de entrenamiento: hay que esperar hasta que se recopilen suficientes estadísticas sobre la media y la estándar.

    Segunda solución que aún no he probado, pero estoy bastante seguro de que funcionará ya que la propagación hacia adelante durante el entrenamiento y la predicción serán las mismas.

    Actualizar. Encontré una excelente publicación en el blog donde este tema se ha discutido en todos los detalles. Mira aqui

    Pero las capas de deserción generalmente crean un efecto opuesto que hace que la pérdida en la evaluación sea menor que la pérdida durante el entrenamiento.

    ¡No necesariamente! Aunque en la capa de abandono, algunas de las neuronas se eliminan, pero tenga en cuenta que la salida se reduce de acuerdo con la tasa de abandono. El tiempo de inferencia (es decir, el tiempo de prueba) se elimina por completo y, dado que solo ha entrenado a su modelo en una sola época, el comportamiento que vio puede suceder. No olvide que, dado que está entrenando el modelo para una sola época, solo una parte de las neuronas se han eliminado en la capa de deserción, pero todas están presentes en el momento de la inferencia.

    Si continúa entrenando el modelo durante más épocas, es de esperar que la pérdida de entrenamiento y la pérdida de prueba ( en los mismos datos ) se vuelvan más o menos iguales.

    Experiméntelo usted mismo: simplemente establezca el parámetro trainable de la (s) capa (s) de False en False y vea si esto sucede o no.


    Uno puede estar confundido (como lo estaba) al ver que, después de una época de entrenamiento, la pérdida de entrenamiento no es igual a la pérdida de evaluación en el mismo lote de datos. Y esto no es específico de los modelos con capas Dropout o BatchNormalization . Considera este ejemplo:

     from keras import layers, models import numpy as np model = models.Sequential() model.add(layers.Dense(1000, activation='relu', input_dim=100)) model.add(layers.Dense(1)) model.compile(loss='mse', optimizer='adam') x = np.random.rand(32, 100) y = np.random.rand(32, 1) print("Training:") model.fit(x, y, batch_size=32, epochs=1) print("\nEvaluation:") loss = model.evaluate(x, y) print(loss) 

    La salida:

     Training: Epoch 1/1 32/32 [==============================] - 0s 7ms/step - loss: 0.1520 Evaluation: 32/32 [==============================] - 0s 2ms/step 0.7577340602874756 

    Entonces, ¿por qué las pérdidas son diferentes si se han computado sobre los mismos datos, es decir, 0.1520 != 0.7577 ?

    Si pregunta esto, es porque usted, como yo, no ha prestado suficiente atención: ese 0.1520 es la pérdida antes de actualizar los parámetros del modelo (es decir, antes de hacer una pasada hacia atrás o una propagación hacia atrás). Y 0.7577 es la pérdida después de que se hayan actualizado los pesos del modelo. A pesar de que los datos utilizados son los mismos, el estado del modelo al calcular esos valores de pérdida no es el mismo (otra pregunta: entonces, ¿por qué ha aumentado la pérdida después de la propagación hacia atrás? Es simplemente porque la ha entrenado solo durante una época). y por lo tanto las actualizaciones de pesos no son lo suficientemente estables todavía.

    Para confirmar esto, también puede utilizar el mismo lote de datos que los datos de validación:

     model.fit(x, y, batch_size=32, epochs=1, validation_data=(x,y)) 

    Si ejecuta el código de arriba con la línea modificada de arriba, obtendrá una salida como esta (obviamente, los valores exactos pueden ser diferentes para usted):

     Training: Train on 32 samples, validate on 32 samples Epoch 1/1 32/32 [==============================] - 0s 15ms/step - loss: 0.1273 - val_loss: 0.5344 Evaluation: 32/32 [==============================] - 0s 89us/step 0.5344240665435791 

    Verá que la pérdida de validación y la pérdida de evaluación son exactamente iguales: se debe a que la validación se realiza al final de la época (es decir, cuando el modelo ya se ha actualizado).