Por que diferir métricas calculadas por model.evaluate () das métricas rastreadas durante o treinamento em Keras?

Estou usando o Keras 2.0.4 (back-end do TensorFlow) para uma tarefa de classificação de imagens (com base em modelos pré-treinados). Durante o treinamento / ajuste, acompanho todas as métricas usadas (por exemplo,categorical_accuracy, categorical crossentropy) comCSVLogger - incluindo as métricas correspondentes associadas ao conjunto de validação (ou seja,val_categorical_accuracy, val_categorical_crossentropy)

Com o retorno de chamadaModelCheckpoint Estou acompanhando a melhor configuração de pesos (save_best_only=True) Para avaliar o modelo no conjunto de validação, eu usomodel.evaluate().

Minha expectativa é: métricas rastreadas porCSVLogger (da 'melhor' época) igual às métricas calculadas pormodel.evaluate(). Infelizmente, esse não é o caso. As métricas diferem por + -5%. Existe uma razão para esse comportamento?

E D I T:

Após alguns testes, pude obter algumas idéias:

Se eu não usar um gerador para dados de treinamento e validação (e, portanto, nãomodel.fit_generator()), o problema não ocorre. -> Usando oImageDataGenerator para dados de treinamento e validação é a fonte da discrepância. (Observe, para o cálculo deevaluate I não use um gerador, mas euFaz use os mesmos dados de validação (pelo menos seDataImageGenerator funcionaria como esperado ...).
Eu acho que o ImageDataGenerator não funciona como deveria (por favor, veja tambémesta)Se eu não usar nenhum gerador, não haverá esse problema. Id métricas rastreadas porCSVLogger (da 'melhor' época) igual às métricas calculadas pormodel.evaluate().
Curiosamente, há outro problema: se você usar os mesmos dados para treinamento e validação, haverá uma discrepância entre as métricas de treinamento (por exemplo,loss) e métricas de validação (por exemplo,val_loss) no final de cada época.
(Um problema semelhante)

Código usado:

############################ import section ############################
from __future__ import print_function # perform like in python 3.x
from keras.datasets import mnist
from keras.utils import np_utils # numpy utils for to_categorical()
from keras.models import Model, load_model
from keras.layers import Dense, GlobalAveragePooling2D, Dropout, GaussianDropout, Conv2D, MaxPooling2D
from keras.optimizers import SGD, Adam
from keras import backend as K
from keras.preprocessing.image import ImageDataGenerator 
from keras import metrics
import os
import sys
from scipy import misc
import numpy as np
from keras.applications.vgg16 import preprocess_input as vgg16_preprocess_input
from keras.applications import VGG16
from keras.callbacks import CSVLogger, ModelCheckpoint


############################ manual settings ###########################
# general settings
seed = 1337

loss_function = 'categorical_crossentropy'

learning_rate = 0.001

epochs = 10

batch_size = 20

nb_classes = 5 

img_width, img_height = 400, 400 # >= 48 necessary, as VGG16 is used

chosen_optimizer = SGD(lr=learning_rate, momentum=0.0, decay=0.0, nesterov=False)

steps_per_epoch = 40 // batch_size  # 40 train samples in 5 classes
validation_steps = 40 // batch_size # 40 train samples in 5 classes

data_dir = # TODO: set path where data is stored (folders: 'train', 'val', 'test'; within each folder are folders named by classes)

# callbacks: CSVLogger & ModelCheckpoint
filepath = # TODO: set path, where you want to store files generated by the callbacks
file_best_checkpoint= 'best_epoch.hdf5'
file_csvlogger = 'logged_metrics.txt'

modelcheckpoint_best_epoch= ModelCheckpoint(filepath=os.path.join(filepath, file_best_checkpoint), 
                                  monitor = 'val_loss' , verbose = 1, 
                                  save_best_only = True, 
                                  save_weights_only=False, mode='auto', 
                                  period=1) # every epoch executed
csvlogger = CSVLogger(os.path.join(filepath, file_csvlogger) , separator=',', append=False)



############################ prepare data ##############################
# get validation data (for evaluation)
X_val, Y_val = # TODO: load train data (4darray, samples, img_width, img_height, nb_channels) IMPORTANT: 5 classes with 8 images each.

# preprocess data
my_preprocessing_function = mf.my_vgg16_preprocess_input

# 'augmentation' configuration we will use for training
train_datagen = ImageDataGenerator(preprocessing_function = my_preprocessing_function) # only preprocessing; static data set

# 'augmentation' configuration we will use for validation
val_datagen = ImageDataGenerator(preprocessing_function = my_preprocessing_function) # only preprocessing; static data set

train_data_dir = os.path.join(data_dir, 'train')
validation_data_dir = os.path.join(data_dir, 'val')
train_generator = train_datagen.flow_from_directory(
    train_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    shuffle = True,
    seed = seed, # random seed for shuffling and transformations
    class_mode='categorical')  # label type (categorical = one-hot vector)

validation_generator = val_datagen.flow_from_directory(
    validation_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    shuffle = True,
    seed = seed, # random seed for shuffling and transformations
    class_mode='categorical')  # label type (categorical = one-hot vector)



############################## training ###############################
print("\n---------------------------------------------------------------")
print("------------------------ training model -----------------------")
print("---------------------------------------------------------------")
# create the base pre-trained model
base_model = VGG16(include_top=False, weights = None, input_shape=(img_width, img_height, 3), pooling = 'max', classes = nb_classes)
model_name =  "VGG_modified"

# do not freeze any layers --> all layers trainable
for layer in base_model.layers:
    layer.trainable = True

# define topping of base_model
x = base_model.output # get the last layer of our base_model
x = Dense(1024, activation='relu', name='fc1')(x)
x = Dense(1024, activation='relu', name='fc2')(x)
predictions = Dense(nb_classes, activation='softmax', name='predictions')(x)

# finally, stack model together
model = Model(outputs=predictions, name= model_name, inputs=base_model.input) #Keras 1.x.x: model = Model(input=base_model.input, output=predictions) 
print(model.summary())

# compile the model (should be done *after* setting layers to non-trainable)
model.compile(optimizer = chosen_optimizer, loss=loss_function, 
            metrics=['categorical_accuracy','kullback_leibler_divergence'])

# train the model on your data
model.fit_generator(
    train_generator,
    steps_per_epoch=steps_per_epoch,
    epochs=epochs,
    validation_data=validation_generator,
    validation_steps=validation_steps,
    callbacks = [csvlogger, modelcheckpoint_best_epoch])



############################## evaluation ##############################
print("\n\n---------------------------------------------------------------")
print("------------------ Evaluation of Best Epoch -------------------")
print("---------------------------------------------------------------")
# load model (corresponding to best training epoch)
model = load_model(os.path.join(filepath, file_best_checkpoint))

# evaluate model on validation data (in test mode!)
list_of_metrics = model.evaluate(X_val, Y_val, batch_size=batch_size, verbose=1, sample_weight=None)
index = 0
print('\nMetrics:')
for metric in model.metrics_names:
    print(metric+ ':' , str(list_of_metrics[index]))
    index += 1

E D I T 2
Referindo-se a 1. de E D I T: Se eu usar o mesmo gerador para dados de validação durante o treinamento e a avaliação (usandoevaluate_generator()), o problema ainda ocorre. Portanto, é definitivamente um problema causado pelos geradores ...

questionAnswers(1)

yourAnswerToTheQuestion