我正在尝试使用 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。
想法?
答案 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% 的解决方案,而且问题经常出现。
编辑:
只要它不起作用,就简化模型。 更改批量大小并停止学习?简化模型。 进一步增强图像并停止学习?简化模型。