为什么Keras中fit_generator的准确率与evaluate_generator的准确率不同?

我做了什么:

  • 我使用Keras的fit_generator()训练一个预训练的CNN。每轮训练后会生成评估指标(loss, acc, val_loss, val_acc)。在模型训练完成后,我使用evaluate_generator()生成评估指标(loss, acc)。

我期待的结果:

  • 如果我训练模型一轮,我希望fit_generator()evaluate_generator()获得的指标是相同的。它们都应该基于整个数据集来计算指标。

我观察到的结果:

  • lossaccfit_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,                                        # 目标目录的路径         target_size=(imagesize,imagesize),                     # 找到的所有图像将被调整到的尺寸         color_mode='rgb',                                      # 图像将被转换为具有1、3或4个通道的颜色模式         classes=None,                                          # 类别子目录的可选列表         class_mode='categorical',                              # 返回的标签数组类型         batch_size=nBatches,                                   # 数据批次的大小         shuffle=True)                                          # 是否打乱数据    return generator

[…]

def train_model(model, nBatches, nEpochs, trainGenerator, valGenerator, resultPath):    history = model.fit_generator(generator=trainGenerator,                                  steps_per_epoch=trainGenerator.samples//nBatches,     # 总步骤数(样本批次)                                  epochs=nEpochs,                   # 训练模型的轮数                                  verbose=2,                        # 详细模式。0 = 静默,1 = 进度条,2 = 每轮一行                                  callbacks=None,                   # 训练过程中应用的keras.callbacks.Callback实例                                  validation_data=valGenerator,     # 在每轮结束时评估损失和模型指标的生成器或元组                                  validation_steps=                                  valGenerator.samples//nBatches,   # 在每轮结束前从验证数据生成器中产出的步骤数(样本批次)                                  class_weight=None,                # 可选字典,将类别索引(整数)映射到权重(浮点数)值,用于加权损失函数                                  max_queue_size=10,                # 生成器队列的最大大小                                  workers=32,                       # 使用基于进程的线程时启动的最大进程数                                  use_multiprocessing=True,         # 是否使用基于进程的线程                                  shuffle=False,                     # 是否在每轮开始时打乱批次的顺序                                  initial_epoch=0)                  # 开始训练的轮数    print("%s: 模型已训练。" % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))    # 保存模型    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: 模型已保存。" % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))    return history, model

[…]

def evaluate_model(model, generator):    score = model.evaluate_generator(generator=generator,           # 生成元组的生成器                                     steps=                                     generator.samples//nBatches)   # 在停止前从生成器中产出的步骤数(样本批次)    print("%s: 模型已评估:"          "\n\t\t\t\t\t\t 损失:%.3f"          "\n\t\t\t\t\t\t 准确率:%.3f" %          (datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),           score[0], score[1]))

[…]

def main():    # 创建模型    modelUntrained = create_model(imagesize, nBands, nClasses)    # 准备训练和验证数据    trainGenerator = generate_data(imagePathTraining, imagesize, nBatches)    valGenerator = generate_data(imagePathValidation, imagesize, nBatches)    # 训练并保存模型    history, modelTrained = train_model(modelUntrained, nBatches, nEpochs, trainGenerator, valGenerator, resultPath)    # 在验证数据上评估    print("%s: 模型评估(valX, valY):" % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))    evaluate_model(modelTrained, valGenerator)    # 在训练数据上评估    print("%s: 模型评估(trainX, trainY):" % datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))    evaluate_model(modelTrained, trainGenerator)

更新

我找到了一些报告此问题的网站:

我尝试了一些建议的解决方案,但到目前为止没有成功。acclossfit_generator()evaluate_generator()中仍然不同,即使使用相同的生成器生成的相同数据进行训练和验证。我尝试了以下方法:

  • 在整个脚本中或在向预训练层添加新层之前静态设置学习阶段
    K.set_learning_phase(0) # 测试      K.set_learning_phase(1) # 训练
  • 解冻预训练模型中的所有批量归一化层
    for i in range(len(model.layers)):        if str.startswith(model.layers[i].name, 'bn'):            model.layers[i].trainable=True
  • 不添加未经训练的dropout或批量归一化层
    # 创建预训练基础模型    basemodel = ResNet50(include_top=False,                     # 排除原始模型中的最终池化和全连接层                         weights='imagenet',                    # 在ImageNet上进行预训练                         input_tensor=None,                     # 作为模型图像输入的可选张量                         input_shape=(imagesize,                # 形状元组                                      imagesize,                                      nBands),                         pooling=None,                          # 模型的输出将是最后一个卷积层的4D张量输出                         classes=nClasses)                      # 用于对图像进行分类的类别数量    # 创建新的未经训练的层    x = basemodel.output    x = GlobalAveragePooling2D()(x)                             # 全局空间平均池化层    x = Dense(1024, activation='relu')(x)                       # 全连接层    y = Dense(nClasses, activation='softmax')(x)                # 逻辑层确保概率总和为1    # 创建结合预训练基础模型和新未经训练层的模型    model = Model(inputs=basemodel.input,                  outputs=y)    # 冻结预训练层的权重    for layer in basemodel.layers:        layer.trainable = False    # 定义学习优化器    learningRate = 0.01    optimizerSGD = optimizers.SGD(lr=learningRate,              # 学习率。                                  momentum=0.9,                 # 参数,加速SGD在相关方向上的速度并抑制振荡                                  decay=learningRate/nEpochs,   # 每轮更新的学习率衰减                                  nesterov=True)                # 是否应用Nesterov动量    # 编译模型    model.compile(optimizer=optimizerSGD,                       # 随机梯度下降优化器                  loss='categorical_crossentropy',              # 目标函数                  metrics=['accuracy'],                         # 模型在训练和测试期间评估的指标                  loss_weights=None,                            # 不同模型输出的损失贡献的标量系数                  sample_weight_mode=None,                      # 样本加权                  weighted_metrics=None,                        # 在训练和测试期间通过sample_weight或class_weight加权评估的指标                  target_tensors=None)                          # 模型的目标张量,将在训练期间提供目标数据
  • 使用不同的预训练CNN作为基础模型(VGG19, InceptionV3, InceptionResNetV2, Xception
    from keras.applications.vgg19 import VGG19    basemodel = VGG19(include_top=False,                        # 排除原始模型中的最终池化和全连接层                         weights='imagenet',                    # 在ImageNet上进行预训练                         input_tensor=None,                     # 作为模型图像输入的可选张量                         input_shape=(imagesize,                # 形状元组                                      imagesize,                                      nBands),                         pooling=None,                          # 模型的输出将是最后一个卷积层的4D张量输出                         classes=nClasses)                      # 用于对图像进行分类的类别数量

请告诉我是否有我遗漏的其他解决方案。


回答:

我现在设法获得了相同的评估指标。我做了以下更改:

  • 如@Anakin所建议,在flow_from_directory()中设置seed
def generate_data(path, imagesize, nBatches):        datagen = ImageDataGenerator(rescale=1./255)        generator = datagen.flow_from_directory(directory=path,     # 目标目录的路径             target_size=(imagesize,imagesize),                     # 找到的所有图像将被调整到的尺寸             color_mode='rgb',                                      # 图像将被转换为具有1、3或4个通道的颜色模式             classes=None,                                          # 类别子目录的可选列表             class_mode='categorical',                              # 返回的标签数组类型             batch_size=nBatches,                                   # 数据批次的大小             shuffle=True,                                          # 是否打乱数据             seed=42)                                               # 打乱和变换的随机种子        return generator

Related Posts

在使用k近邻算法时,有没有办法获取被使用的“邻居”?

我想找到一种方法来确定在我的knn算法中实际使用了哪些…

Theano在Google Colab上无法启用GPU支持

我在尝试使用Theano库训练一个模型。由于我的电脑内…

准确性评分似乎有误

这里是代码: from sklearn.metrics…

Keras Functional API: “错误检查输入时:期望input_1具有4个维度,但得到形状为(X, Y)的数组”

我在尝试使用Keras的fit_generator来训…

如何使用sklearn.datasets.make_classification在指定范围内生成合成数据?

我想为分类问题创建合成数据。我使用了sklearn.d…

如何处理预测时不在训练集中的标签

已关闭。 此问题与编程或软件开发无关。目前不接受回答。…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注