Keras fit_generator() 没有正确训练

时间:2021-05-07 16:11:29

标签: python tensorflow tensorflow2.0 tf.keras

我正在尝试使用 Keras 和 TensorFlow 2.0.0 后端创建图像分类器。

我正在本地机器上的自定义数据集上训练这个模型,该数据集包含总共 17~000 张图像。图像大小不同,位于三个不同的文件夹(训练、验证和测试)中,每个文件夹包含两个子文件夹(每个类一个)。 我尝试了一个类似于 VGG16 的架构,它过去在这个数据集上产生了不错的结果。请注意,数据中存在轻微的类不平衡(52:48)

当我调用 fit_generator() 时,模型不能很好地训练;尽管在整个第一个 epoch 中训练损失略有降低,但之后并没有太大变化。使用这种更高调节的架构,我在过去 55~ epochs 后达到了 85% 的准确率。

导入和超参数

import tensorflow as tf
from tensorflow import keras
from keras import backend as k
from keras.layers import Dense, Dropout, Conv2D, MaxPooling2D, Flatten, Input, UpSampling2D
from keras.models import Sequential, Model, load_model
from keras.utils import to_categorical
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import ModelCheckpoint

TRAIN_PATH = 'data/train/'
VALID_PATH = 'data/validation/'
TEST_PATH = 'data/test/'
TARGET_SIZE = (256, 256)
RESCALE = 1.0 / 255
COLOR_MODE = 'grayscale'
EPOCHS = 2
BATCH_SIZE = 16
CLASSES = ['Damselflies', 'Dragonflies']
CLASS_MODE = 'categorical'
CHECKPOINT = "checkpoints/weights.hdf5"

型号

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), activation='relu',
                 input_shape=(256, 256, 1), padding='same'))

model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.1))

model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.1))

model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.1))

model.add(Flatten())
model.add(Dense(516, activation='relu'))
model.add(Dropout(0.1))

model.add(Dense(128, activation='relu'))
model.add(Dropout(0.1))

model.add(Dense(2, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='Adam', metrics=['accuracy'])

过去,我创建了一个自定义管道来重塑、灰度、翻转和标准化图像;然后,我使用 CPU 对批量处理的图像训练模型。

我尝试使用 ImageDataGenerator、flow_from_directory 和 GPU 支持重复该过程。

# randomly flip images, and scale pixel values
trainGenerator = ImageDataGenerator(rescale=RESCALE, 
                                    horizontal_flip=True,  
                                    vertical_flip=True)

# only scale the pixel values validation images
validatioinGenerator = ImageDataGenerator(rescale=RESCALE)

# only scale the pixel values test images
testGenerator = ImageDataGenerator(rescale=RESCALE)

# instanciate train flow
trainFlow = trainGenerator.flow_from_directory(
    TRAIN_PATH,
    target_size = TARGET_SIZE,
    batch_size = BATCH_SIZE,
    classes = CLASSES,
    color_mode = COLOR_MODE,
    class_mode = CLASS_MODE,
    shuffle=True
) 

# instanciate validation flow
validationFlow = validatioinGenerator.flow_from_directory(
    VALID_PATH,
    target_size = TARGET_SIZE,
    batch_size = BATCH_SIZE,
    classes = CLASSES,
    color_mode = COLOR_MODE,
    class_mode= CLASS_MODE,
    shuffle=True
)

然后,使用 fit_generator 拟合模型。

checkpoints = ModelCheckpoint(CHECKPOINT, monitor='val_accuracy', verbose=1, save_best_only=True, mode='max')

with tf.device('/GPU:0'):
    model.fit_generator(
        trainFlow,
        validation_data=validationFlow, 
        callbacks=[checkpoints],
        epochs=EPOCHS
    )

我尝试将其训练 40 个 epoch。 分类器在第一个 epoch 后达到了 52%,并且不会随着时间的推移而提高。

测试分类器

testFlow = testGenerator.flow_from_directory(
    TEST_PATH,
    target_size = TARGET_SIZE,
    batch_size = BATCH_SIZE,
    classes = CLASSES,
    color_mode = COLOR_MODE,
    class_mode= CLASS_MODE,
)

ans = model.predict_generator(testFlow)

当我查看预测时,模型将所有测试图像预测为具有相同置信度 [0.48498476, 0.51501524] 的多数类。

<块引用>

我确定数据正确了吗?

是的。我测试了生成器是否正确生成处理过的图像及其相应的标签。

<块引用>

我是否尝试过更改损失函数、激活函数和优化器?

是的。我尝试将类模式更改为二进制,将损失更改为 binary_crossentropy,并更改最后一层以生成具有 sigmoid 激活的单个输出。不,我没有改变优化器。但是,我确实尝试提高学习率。

<块引用>

我是否尝试过更改模型的架构?

是的。我尝试增加和减少模型复杂性。 正则化较少的层数越多,正则化越多的层数越少,结果相似。

<块引用>

层是否可训练?

是的。

<块引用>

是否正确实现了 GPU 支持?

希望如此。

print("Num GPUs Available: ", len(tf.config.experimental.list_physical_devices('GPU')))

可用 GPU 数量:1

a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a') 
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b') 
c = tf.matmul(a, b)

config = tf.compat.v1.ConfigProto(log_device_placement=True) 
config.gpu_options.allow_growth = True 
sess = tf.compat.v1.Session(config=config)
print(sess) 

设备映射: /job:localhost/replica:0/task:0/device:GPU:0 -> device: 0, name: NVIDIA GeForce GTX 1050 with Max-Q Design, pci bus id: 0000:03:00.0, 计算能力: 6.1

<块引用>

我尝试过迁移学习吗?

还没有。

我在 2017 年发现了一个类似的未回答问题keras-doesnt-train-using-fit-generator

想法?

2 个答案:

答案 0 :(得分:1)

问题出在您的模型上。我复制了你的代码并在我之前使用过的数据集上运行它(它获得了很高的准确性)并得到了与你相似的结果。然后我替换了下面的简单模型

model = tf.keras.Sequential([
    Conv2D(16, 3, padding='same', activation='relu', input_shape=(256 , 256,1)),
    MaxPooling2D(),
    Conv2D(32, 3, padding='same', activation='relu' ),
    MaxPooling2D(),
    Conv2D(64, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Conv2D(128, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Conv2D(256, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Flatten(),
    Dense(128, activation='relu'),
    Dropout(.3),
    Dense(64, activation='relu'),
    Dropout(.3),
    Dense(2, activation='softmax')
])
model.compile(loss='categorical_crossentropy',
              optimizer='Adam', metrics=['accuracy'])

模型训练得当。顺便说一下,model.fit_generator 是折旧的。您现在可以使用 model.fit 来处理生成器。然后我拿了你的模型并删除了除最后一个之外的所有 dropout 层,并且你的模型得到了正确的训练。代码是:

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), activation='relu',
                 input_shape=(256, 256, 1), padding='same'))

model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
#model.add(Dropout(0.1))

model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
#model.add(Dropout(0.1))

model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
#model.add(Dropout(0.1))

model.add(Flatten())
model.add(Dense(516, activation='relu'))
#model.add(Dropout(0.1))

model.add(Dense(128, activation='relu'))
model.add(Dropout(0.1))

model.add(Dense(2, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='Adam', metrics=['accuracy'])

答案 1 :(得分:0)

@Gerry P,

偶然地,我发现了导致错误的原因。 删除 from Keras import backend as k 解决了模型无法学习的问题。

这还不是全部。我还发现您定义的模型、未调用 ModelCheckpoint 和未自定义类名影响了拟合过程。

model = Sequential([
    Conv2D(16, 3, padding='same', activation='relu', input_shape=(256 , 256, 1)),
    MaxPooling2D(),
    Conv2D(32, 3, padding='same', activation='relu' ),
    MaxPooling2D(),
    Conv2D(64, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Conv2D(128, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Conv2D(256, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Flatten(),
    Dense(128, activation='relu'),
    Dropout(.3),
    Dense(64, activation='relu'),
    Dropout(.3),
    Dense(2, activation='softmax')
])

我评论了导入以尝试解决我复制粘贴您的顺序模型时发生的错误。然后,当我测试它漂亮或平均的数据集时,我忘记取消注释了。在第三个 epoch 之后,我达到了 80% 以上的准确率。然后,我恢复了更改并在我的数据集上进行了尝试,但它再次失败。 作为奖励,不导入 Keras 的后端减少了训练模型所需的时间!

最近,我不得不重新安装 Keras 和 TensorFlow,因为它们无法再检测到我的 GPU。我可能犯了一个错误,安装了不兼容的 Keras 版本。

CUDA==10.0
tensorflow-gpu==2.0.0
keras==2.3.1

请注意,这仍然不是 100% 的解决方案,而且问题经常出现。

编辑:

只要它不起作用,就简化模型。 更改批量大小并停止学习?简化模型。 进一步增强图像并停止学习?简化模型。