Почему точность от fit_generator отличается от точности для оценки_генератора в Керасе? - PullRequest
2 голосов
/ 08 апреля 2019

Что я делаю:

  • Я тренирую предварительно обученный CNN с Keras fit_generator().Это создает метрики оценки (loss, acc, val_loss, val_acc) после каждой эпохи.После обучения модели я создаю оценочные метрики (loss, acc) с evaluate_generator().

Что я ожидаю:

  • Если я тренируюсьмодель для одной эпохи, я ожидаю, что метрики, полученные с fit_generator() и evaluate_generator(), одинаковы.Они оба должны получать метрики на основе всего набора данных.

То, что я наблюдаю:

  • Оба loss и acc различныот fit_generator() и evaluate_generator(): enter image description here

Что я не понимаю:

  • Почемуточность от fit_generator() отличается от точности от evaluate_generator()

Мой код:

def generate_data(path, imagesize, nBatches):
    datagen = ImageDataGenerator(rescale=1./255)
    generator = datagen.flow_from_directory\
        (directory=path,                                        # path to the target directory
         target_size=(imagesize,imagesize),                     # dimensions to which all images found will be resize
         color_mode='rgb',                                      # whether the images will be converted to have 1, 3, or 4 channels
         classes=None,                                          # optional list of class subdirectories
         class_mode='categorical',                              # type of label arrays that are returned
         batch_size=nBatches,                                   # size of the batches of data
         shuffle=True)                                          # whether to shuffle the data
    return generator

[...]

def train_model(model, nBatches, nEpochs, trainGenerator, valGenerator, resultPath):
    history = model.fit_generator(generator=trainGenerator,
                                  steps_per_epoch=trainGenerator.samples//nBatches,     # total number of steps (batches of samples)
                                  epochs=nEpochs,                   # number of epochs to train the model
                                  verbose=2,                        # verbosity mode. 0 = silent, 1 = progress bar, 2 = one line per epoch
                                  callbacks=None,                   # keras.callbacks.Callback instances to apply during training
                                  validation_data=valGenerator,     # generator or tuple on which to evaluate the loss and any model metrics at the end of each epoch
                                  validation_steps=
                                  valGenerator.samples//nBatches,   # number of steps (batches of samples) to yield from validation_data generator before stopping at the end of every epoch
                                  class_weight=None,                # optional dictionary mapping class indices (integers) to a weight (float) value, used for weighting the loss function
                                  max_queue_size=10,                # maximum size for the generator queue
                                  workers=32,                       # maximum number of processes to spin up when using process-based threading
                                  use_multiprocessing=True,         # whether to use process-based threading
                                  shuffle=False,                     # whether to shuffle the order of the batches at the beginning of each epoch
                                  initial_epoch=0)                  # epoch at which to start training
    print("%s: Model trained." % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))

    # Save model
    modelPath = os.path.join(resultPath, datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + '_modelArchitecture.h5')
    weightsPath = os.path.join(resultPath, datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + '_modelWeights.h5')
    model.save(modelPath)
    model.save_weights(weightsPath)
    print("%s: Model saved." % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))
    return history, model

[...]

def evaluate_model(model, generator):
    score = model.evaluate_generator(generator=generator,           # Generator yielding tuples
                                     steps=
                                     generator.samples//nBatches)   # number of steps (batches of samples) to yield from generator before stopping

    print("%s: Model evaluated:"
          "\n\t\t\t\t\t\t Loss: %.3f"
          "\n\t\t\t\t\t\t Accuracy: %.3f" %
          (datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
           score[0], score[1]))

[...]

def main():
    # Create model
    modelUntrained = create_model(imagesize, nBands, nClasses)

    # Prepare training and validation data
    trainGenerator = generate_data(imagePathTraining, imagesize, nBatches)
    valGenerator = generate_data(imagePathValidation, imagesize, nBatches)

    # Train and save model
    history, modelTrained = train_model(modelUntrained, nBatches, nEpochs, trainGenerator, valGenerator, resultPath)

    # Evaluate on validation data
    print("%s: Model evaluation (valX, valY):" % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))
    evaluate_model(modelTrained, valGenerator)

    # Evaluate on training data
    print("%s: Model evaluation (trainX, trainY):" % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))
    evaluate_model(modelTrained, trainGenerator)

Обновление

Я нашел несколько сайтов, которые сообщают оэта проблема:

До сих пор я безуспешно пытался следовать некоторым из предложенных ими решений.acc и loss по-прежнему отличаются от fit_generator() и evaluate_generator(), даже если для обучения и проверки используются одни и те же данные, созданные с помощью одного и того же генератора.Вот что я попробовал:

  • статически устанавливая обучающую фазу для всего сценария или перед добавлением новых слоев к предварительно обученным
    K.set_learning_phase(0) # testing  
    K.set_learning_phase(1) # training
  • размораживаниевсе слои нормализации партии из предварительно обученной модели
    for i in range(len(model.layers)):
        if str.startswith(model.layers[i].name, 'bn'):
            model.layers[i].trainable=True
  • без добавления отсева или нормализации партии в качестве неподготовленных слоев
    # Create pre-trained base model
    basemodel = ResNet50(include_top=False,                     # exclude final pooling and fully connected layer in the original model
                         weights='imagenet',                    # pre-training on ImageNet
                         input_tensor=None,                     # optional tensor to use as image input for the model
                         input_shape=(imagesize,                # shape tuple
                                      imagesize,
                                      nBands),
                         pooling=None,                          # output of the model will be the 4D tensor output of the last convolutional layer
                         classes=nClasses)                      # number of classes to classify images into

    # Create new untrained layers
    x = basemodel.output
    x = GlobalAveragePooling2D()(x)                             # global spatial average pooling layer
    x = Dense(1024, activation='relu')(x)                       # fully-connected layer
    y = Dense(nClasses, activation='softmax')(x)                # logistic layer making sure that probabilities sum up to 1

    # Create model combining pre-trained base model and new untrained layers
    model = Model(inputs=basemodel.input,
                  outputs=y)

    # Freeze weights on pre-trained layers
    for layer in basemodel.layers:
        layer.trainable = False

    # Define learning optimizer
    learningRate = 0.01
    optimizerSGD = optimizers.SGD(lr=learningRate,              # learning rate.
                                  momentum=0.9,                 # parameter that accelerates SGD in the relevant direction and dampens oscillations
                                  decay=learningRate/nEpochs,   # learning rate decay over each update
                                  nesterov=True)                # whether to apply Nesterov momentum
    # Compile model
    model.compile(optimizer=optimizerSGD,                       # stochastic gradient descent optimizer
                  loss='categorical_crossentropy',              # objective function
                  metrics=['accuracy'],                         # metrics to be evaluated by the model during training and testing
                  loss_weights=None,                            # scalar coefficients to weight the loss contributions of different model outputs
                  sample_weight_mode=None,                      # sample-wise weights
                  weighted_metrics=None,                        # metrics to be evaluated and weighted by sample_weight or class_weight during training and testing
                  target_tensors=None)                          # tensor model's target, which will be fed with the target data during training
  • с использованиемразличные предварительно обученные CNN в качестве базовой модели ( VGG19, InceptionV3, InceptionResNetV2, Xception )
    from keras.applications.vgg19 import VGG19

    basemodel = VGG19(include_top=False,                        # exclude final pooling and fully connected layer in the original model
                         weights='imagenet',                    # pre-training on ImageNet
                         input_tensor=None,                     # optional tensor to use as image input for the model
                         input_shape=(imagesize,                # shape tuple
                                      imagesize,
                                      nBands),
                         pooling=None,                          # output of the model will be the 4D tensor output of the last convolutional layer
                         classes=nClasses)                      # number of classes to classify images into

Пожалуйста, дайте мне знать, если есть другие решения, которые мне не хватает.

Ответы [ 3 ]

1 голос
/ 08 апреля 2019

Теперь мне удалось получить те же показатели оценки.Я изменил следующее:

  • Я установил seed в flow_from_directory(), как предложено @ Anakin
def generate_data(path, imagesize, nBatches):
        datagen = ImageDataGenerator(rescale=1./255)
        generator = datagen.flow_from_directory(directory=path,     # path to the target directory
             target_size=(imagesize,imagesize),                     # dimensions to which all images found will be resize
             color_mode='rgb',                                      # whether the images will be converted to have 1, 3, or 4 channels
             classes=None,                                          # optional list of class subdirectories
             class_mode='categorical',                              # type of label arrays that are returned
             batch_size=nBatches,                                   # size of the batches of data
             shuffle=True,                                          # whether to shuffle the data
             seed=42)                                               # random seed for shuffling and transformations
        return generator

  • Я установил use_multiprocessing=False в fit_generator() в соответствии с предупреждением: use_multiprocessing=True and multiple workers may duplicate your data
history = model.fit_generator(generator=trainGenerator,
                                  steps_per_epoch=trainGenerator.samples//nBatches,     # total number of steps (batches of samples)
                                  epochs=nEpochs,                   # number of epochs to train the model
                                  verbose=2,                        # verbosity mode. 0 = silent, 1 = progress bar, 2 = one line per epoch
                                  callbacks=callback,               # keras.callbacks.Callback instances to apply during training
                                  validation_data=valGenerator,     # generator or tuple on which to evaluate the loss and any model metrics at the end of each epoch
                                  validation_steps=
                                  valGenerator.samples//nBatches,   # number of steps (batches of samples) to yield from validation_data generator before stopping at the end of every epoch
                                  class_weight=None,                # optional dictionary mapping class indices (integers) to a weight (float) value, used for weighting the loss function
                                  max_queue_size=10,                # maximum size for the generator queue
                                  workers=1,                        # maximum number of processes to spin up when using process-based threading
                                  use_multiprocessing=False,        # whether to use process-based threading
                                  shuffle=False,                    # whether to shuffle the order of the batches at the beginning of each epoch
                                  initial_epoch=0)                  # epoch at which to start training

  • Я унифицировал мою настройку Python, как предложено в керадокументация на как получить воспроизводимые результаты с использованием Keras во время разработки
import tensorflow as tf
import random as rn
from keras import backend as K

np.random.seed(42)
rn.seed(12345)
session_conf = tf.ConfigProto(intra_op_parallelism_threads=1,
                              inter_op_parallelism_threads=1)
tf.set_random_seed(1234)
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
K.set_session(sess)

  • Вместо масштабирования входных изображений с помощью datagen = ImageDataGenerator(rescale=1./255), я теперьсгенерируйте мои данные с помощью:
from keras.applications.resnet50 import preprocess_input
datagen = ImageDataGenerator(preprocessing_function=preprocess_input)

При этом мне удалось получить схожую точность и потери с fit_generator() и evaluate_generator().Кроме того, использование одних и тех же данных для обучения и тестирования теперь приводит к аналогичным показателям. Причины оставшихся отличий приведены в документации keras .

1 голос
/ 23 мая 2019

Установка use_multiprocessing=False на уровне fit_generator устраняет проблему, НО за счет значительного замедления обучения. Лучшим, но все же несовершенным рабочим окружением было бы установить use_multiprocessing=False только для генератора проверки, так как приведенный ниже код был изменен из функции keras 'fit_generator.

...
        try:
            if do_validation:
                if val_gen and workers > 0:
                    # Create an Enqueuer that can be reused
                    val_data = validation_data
                    if isinstance(val_data, Sequence):
                        val_enqueuer = OrderedEnqueuer(val_data,
                                                       **use_multiprocessing=False**)
                        validation_steps = len(val_data)
                    else:
                        val_enqueuer = GeneratorEnqueuer(val_data,
                                                         **use_multiprocessing=False**)
                    val_enqueuer.start(workers=workers,
                                       max_queue_size=max_queue_size)
                    val_enqueuer_gen = val_enqueuer.get()
...
1 голос
/ 08 апреля 2019

Тренировка для одной эпохи может быть недостаточно информативной в этом случае.Также ваши данные о поездах и тестах могут не совпадать, так как вы не устанавливаете случайное начальное число для метода flow_from_directory.Взгляните здесь .

Возможно, вы можете установить начальное число, удалить аугментации (если есть) и сохранить обученные веса моделей, чтобы загрузить их позже для проверки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...