【问题标题】:High training accuracy, low validation accuracy CNN binary classification keras训练精度高,验证精度低 CNN 二元分类 keras
【发布时间】:2020-08-15 11:43:11
【问题描述】:

我正在尝试创建一个二元分类器,可以区分阿尔茨海默病患者和健康个体的 MRI。
以下是目前的统计数据:

  • 1032 张训练图像
  • 400 张验证图片
  • 运行如下所示的简单模型
  • 我既有 160x160 的原始图像,也有边缘检测后的图像

型号:

model = Sequential([
    Conv2D(filters=16, kernel_size=(5, 5), activation='relu', padding = 'same', input_shape=(160,160,3)),
    MaxPool2D(pool_size=(2, 2), strides=2),
    Flatten(),
    Dense(units=2, activation='softmax')
])

如您所见 - 它非常简单,我有目的地尝试解决过拟合问题。

输出:

11/11 [==============================] - 2s 194ms/step - loss: 0.7604 - accuracy: 0.5155 - val_loss: 0.7081 - val_accuracy: 0.5000
Epoch 2/20
11/11 [==============================] - 2s 185ms/step - loss: 0.6885 - accuracy: 0.5223 - val_loss: 0.6942 - val_accuracy: 0.4839
Epoch 3/20
11/11 [==============================] - 2s 185ms/step - loss: 0.6802 - accuracy: 0.5854 - val_loss: 0.6985 - val_accuracy: 0.4931
Epoch 4/20
11/11 [==============================] - 2s 185ms/step - loss: 0.6717 - accuracy: 0.5932 - val_loss: 0.6996 - val_accuracy: 0.4677
Epoch 5/20
11/11 [==============================] - 2s 195ms/step - loss: 0.6512 - accuracy: 0.6175 - val_loss: 0.7124 - val_accuracy: 0.5115
Epoch 6/20
11/11 [==============================] - 2s 185ms/step - loss: 0.6345 - accuracy: 0.6476 - val_loss: 0.7073 - val_accuracy: 0.5253
Epoch 7/20
11/11 [==============================] - 2s 185ms/step - loss: 0.6118 - accuracy: 0.6680 - val_loss: 0.6920 - val_accuracy: 0.5207
Epoch 8/20
11/11 [==============================] - 2s 185ms/step - loss: 0.5817 - accuracy: 0.7068 - val_loss: 0.6964 - val_accuracy: 0.5207
Epoch 9/20
11/11 [==============================] - 2s 184ms/step - loss: 0.5528 - accuracy: 0.7272 - val_loss: 0.7123 - val_accuracy: 0.5161
Epoch 10/20
11/11 [==============================] - 2s 193ms/step - loss: 0.5239 - accuracy: 0.7417 - val_loss: 0.7397 - val_accuracy: 0.5392
Epoch 11/20
11/11 [==============================] - 2s 186ms/step - loss: 0.5106 - accuracy: 0.7427 - val_loss: 0.7551 - val_accuracy: 0.5461
Epoch 12/20
11/11 [==============================] - 2s 197ms/step - loss: 0.4920 - accuracy: 0.7650 - val_loss: 0.7402 - val_accuracy: 0.5438
Epoch 13/20
11/11 [==============================] - 2s 190ms/step - loss: 0.4741 - accuracy: 0.7835 - val_loss: 0.7564 - val_accuracy: 0.5507
Epoch 14/20
11/11 [==============================] - 2s 188ms/step - loss: 0.4591 - accuracy: 0.7767 - val_loss: 0.7445 - val_accuracy: 0.5300
Epoch 15/20
11/11 [==============================] - 2s 185ms/step - loss: 0.4486 - accuracy: 0.7767 - val_loss: 0.7712 - val_accuracy: 0.5415
Epoch 16/20
11/11 [==============================] - 2s 185ms/step - loss: 0.4503 - accuracy: 0.7806 - val_loss: 0.7446 - val_accuracy: 0.5346
Epoch 17/20
11/11 [==============================] - 2s 188ms/step - loss: 0.4404 - accuracy: 0.7670 - val_loss: 0.7669 - val_accuracy: 0.5553
Epoch 18/20
11/11 [==============================] - 2s 184ms/step - loss: 0.4169 - accuracy: 0.8078 - val_loss: 0.7804 - val_accuracy: 0.5576
Epoch 19/20
11/11 [==============================] - 2s 184ms/step - loss: 0.3987 - accuracy: 0.7971 - val_loss: 0.7846 - val_accuracy: 0.5507
Epoch 20/20
11/11 [==============================] - 2s 192ms/step - loss: 0.3977 - accuracy: 0.7981 - val_loss: 0.8060 - val_accuracy: 0.5461

到目前为止我尝试过的事情:

  • 将图像大小调整为较小的输入
  • 添加 dropout 层
  • 使用仅显示边缘的预处理图像
  • 确保训练和验证数据集中的两个类均匀分布
  • 改变学习率
  • 将参数数量减少到与我拥有的训练图像数量相同的数量级

我确实没有想法,我不确定如何继续前进,因此我将不胜感激任何提示或建议。

我的所有代码:

# Use ImageDataGenerator to create 3 lots of batches
train_batches = ImageDataGenerator(
    rescale=1/255).flow_from_directory(directory=train_path,
        target_size=(80,80), classes=['cn', 'ad'], batch_size=100,
            color_mode="rgb")
valid_batches = ImageDataGenerator(
    rescale=1/255).flow_from_directory(directory=valid_path,
        target_size=(80,80), classes=['cn', 'ad'], batch_size=100,
            color_mode="rgb")
# test_batches = ImageDataGenerator(
#     rescale=1/255).flow_from_directory(directory=test_path,
#         target_size=(224,224), classes=['cn', 'ad'], batch_size=10,
#             color_mode="rgb")

imgs, labels = next(train_batches)

# Test to see normalisation has occurred properly
print(imgs[1][8])

# Define method to plot MRIs
def plotImages(images_arr):
    fig, axes = plt.subplots(1, 10, figsize=(20,20))
    axes = axes.flatten()
    for img, ax in zip( images_arr, axes):
        ax.imshow(img)
        ax.axis('off')
    plt.tight_layout()
    plt.show()

# Plot a sample of MRIs
plotImages(imgs)

# # Define the model
# # VGG16
# model = Sequential()
# model.add(Conv2D(input_shape=(160,160,3),filters=64,kernel_size=(3,3),padding="same", activation="relu"))
# model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same", activation="relu"))
# model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
# model.add(Conv2D(filters=128, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(Conv2D(filters=128, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
# model.add(Conv2D(filters=256, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(Conv2D(filters=256, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(Conv2D(filters=256, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
# model.add(Conv2D(filters=512, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(Conv2D(filters=512, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(Conv2D(filters=512, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
# model.add(Conv2D(filters=512, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(Conv2D(filters=512, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(Conv2D(filters=512, kernel_size=(3,3), padding="same", activation="relu"))
# model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
# model.add(Flatten())
# model.add(Dense(units=1024,activation="relu"))
# model.add(Dense(units=128,activation="relu"))
# model.add(Dense(units=2, activation="softmax"))

# # Model from the paper
# model = Sequential([
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(160,160,3)),
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding='same'),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Flatten(),
#     Dense(units=2, activation='softmax')
# ])

## Model from Dr Paul
# static_conv_layer=Conv2D(filters=16, kernel_size=(5, 5), activation='relu', padding = 'same')
#
# model = Sequential([
#     Conv2D(filters=16, kernel_size=(5, 5), activation='relu', padding = 'same', input_shape=(32,32,3)),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.1),
#     static_conv_layer,
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.1),
#     Flatten(),
#     Dense(units=2, activation='softmax')
# ])

# This model hits around 75% train acc, 54% val acc
model = Sequential([
    Conv2D(filters=16, kernel_size=(5, 5), activation='relu', padding = 'same', input_shape=(80,80,3)),
    MaxPool2D(pool_size=(2, 2), strides=2),
    # Dropout(0.1),
    # Conv2D(filters=16, kernel_size=(3, 3), activation='relu', padding='same'),
    # MaxPool2D(pool_size=(2, 2), strides=2),
    # Conv2D(filters=16, kernel_size=(3, 3), activation='relu', padding='same'),
    # MaxPool2D(pool_size=(2, 2), strides=2),
    Flatten(),
    Dense(units=2, activation='softmax')
])

# model = Sequential([
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(160,160,3)),
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding='same'),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding='same'),
#     Flatten(),
#     Dense(units=2, activation='softmax')
# ])

## Basic model with dropouts
# model = Sequential([
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(224,224,3)),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.1),
#     Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding='same'),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.2),
#     Conv2D(filters=128, kernel_size=(3, 3), activation='relu', padding='same'),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.3),
#     Flatten(),
#     Dense(units=1, activation='sigmoid')
# ])

# Summarise each layer of the model
print(model.summary())

# Compile and train the model
model.compile(optimizer=Adam(), loss='binary_crossentropy', metrics=['accuracy'])
model.fit(x=train_batches,
    steps_per_epoch=len(train_batches),
    validation_data=valid_batches,
    validation_steps=len(valid_batches),
    epochs=20,
    verbose=1
)

编辑:

This paper 似乎做得比我好得多,并且完成了一项非常相似的任务,查看以下方法可能很有用:

【问题讨论】:

  • 你能提供数据集的 URL 吗?
  • github.com/DanielCutter/MRI-CNN 有我正在使用的图像
  • 你能解释一下数据集吗?文件夹“ad”、“ad_proc”、“cn”和“cn_proc”子文件夹在“train”中是什么意思? “README”不包含任何描述
  • @Arun 很抱歉,“ad”和“cn”文件夹包含原始图像,而“ad_proc”和“cn_proc”包含经过边缘检测预处理的图像
  • “ad”和“cn”是什么意思?

标签: python tensorflow machine-learning keras computer-vision


【解决方案1】:

您可以尝试的事情。

  • 这是开始迁移学习的好方法。使用图像净重可帮助您仅训练最后一层并提供更好的准确度。
  • 添加提前停止和降低学习率,并以验证准确度为约束条件。
  • 利用 ImageDataGenerator 并添加更多数据增强技术。
  • 让您的模型更深入,并尝试不同的优化器 (RMSprop),运行更多 epoch 并提前停止。
  • 添加回调并根据学习率绘制训练验证准确度图,以查看哪个 lr 证明最适合数据。

【讨论】:

  • 考虑到单层 16 个过滤器已经过拟合,这些都不是好的建议
【解决方案2】:

您的模型似乎由于缺乏数据而过度拟合。你可以做一些数据增强来增加你拥有的图像数量。如果你不关心你的纵横比,你可以扭曲图像,如果你并不总是需要完整的图像,你可以裁剪它,如果方向不重要,你可以旋转它。这些东西可以显着增加您的数据集大小并有助于缓解过度拟合。

这是来自tensorflow documentation 的示例:

batch_size = 32
AUTOTUNE = tf.data.experimental.AUTOTUNE

def prepare(ds, shuffle=False, augment=False):
  # Resize and rescale all datasets
  ds = ds.map(lambda x, y: (resize_and_rescale(x), y), 
              num_parallel_calls=AUTOTUNE)

  if shuffle:
    ds = ds.shuffle(1000)

  # Batch all datasets
  ds = ds.batch(batch_size)

  # Use data augmentation only on the training set
  if augment:
    ds = ds.map(lambda x, y: (data_augmentation(x, training=True), y), 
                num_parallel_calls=AUTOTUNE)

  # Use buffered prefecting on all datasets
  return ds.prefetch(buffer_size=AUTOTUNE)

另外,here 是 TensorFlow 开发者 youtube 频道的精彩视频,它解释了图像增强的概念并展示了如何实现它的示例。

【讨论】:

  • 您好,谢谢您的回复!我在想,除了增强之外,还有另一种方法可以使用更多图像。我下载了 .nii 格式的医学图像,并将它们转换为我的模型的 png 图像。但是,单个 .nii 文件在转换时会生成大约 200 个 png 文件。在我目前的模型中,我只删除了每位患者,但只有一个。有没有一种方法可以为每位患者使用多张脑部扫描图像?
  • 当然,只要图像有些不同就可以了。例如,如果图像是从不同的角度拍摄的,或者拍摄得更向右一点,那么它应该没问题。可以使用来自同一患者的图像,但相同的图像会导致问题。
猜你喜欢
  • 2020-07-08
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-29
相关资源
最近更新 更多