【问题标题】:50% validation accuracy in binary classification with any CNN model (including VGG16)使用任何 CNN 模型(包括 VGG16)进行二元分类的验证准确率为 50%
【发布时间】:2020-08-12 15:49:42
【问题描述】:

我是 CNN 的新手,我的二元分类产生 50% 的验证准确度时遇到问题。

为了记录,我正在尝试将脑部扫描的 MRI 归类为阿尔茨海默病和健康控制。
默认情况下,图像为 250x250 像素,黑白 .png 格式,我总共有大约 1,000 张图像。
我尝试创建自己的模型并实施 VGG16,所有这些都产生了大约 50% 的验证准确度。

我开始认为这可能是数据处理不正确的问题,但由于我是新手,所以我不确定。也许与黑白图像被解释为RGB有关?但老实说,我不确定。

希望有人能看一下,谢谢!


import tensorflow as tf
from keras.layers.convolutional import Convolution2D
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Activation, Dense, Flatten, BatchNormalization, Conv2D, MaxPool2D, Dropout
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import os
import warnings
import matplotlib.pyplot as plt
from tensorflow.python.keras.applications.vgg16 import VGG16
from tensorflow.python.keras.layers import ZeroPadding2D, MaxPooling2D

warnings.simplefilter(action='ignore', category=FutureWarning)

os.chdir('C:/Users/dancu/PycharmProjects/firstCNN/data/ad-vs-cn')

physical_devices = tf.config.experimental.list_physical_devices('GPU')
print("Num GPUs Available: ", len(physical_devices))
tf.config.experimental.set_memory_growth(physical_devices[0], True)

# Define paths for image data
train_path = "C:/Users/dancu/PycharmProjects/firstCNN\data/ad-vs-cn/train"
test_path = "C:/Users/dancu/PycharmProjects/firstCNN\data/ad-vs-cn/test"
valid_path = "C:/Users/dancu/PycharmProjects/firstCNN\data/ad-vs-cn/valid"

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

imgs, labels = next(train_batches)

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

# 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=(64,64,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=4096,activation="relu"))
model.add(Dense(units=4096,activation="relu"))
model.add(Dense(units=2, activation="softmax"))

# This model hits around 70% train acc, 50% val acc
# model = Sequential([
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(64,64,3)),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.2),
#   #  BatchNormalization(),
#     Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding='same'),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.3),
#    # BatchNormalization(),
#     Conv2D(filters=128, kernel_size=(3, 3), activation='relu', padding='same'),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.4),
#   #  BatchNormalization(),
#     Conv2D(filters=128, kernel_size=(3, 3), activation='relu', padding='same'),
#     MaxPool2D(pool_size=(2, 2), strides=2),
#     Dropout(0.4),
#     Flatten(),
#     Dense(units=2, activation='softmax')
# ])

## This model hits around 68% training accuracy at it's peak
# base_model = Sequential([
#     Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(256,256,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=2, activation='softmax')
# ])

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

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

编辑:到目前为止,感谢大家的回复,他们都非常有见地。 我认为我们得出的结论是,这是一个小样本的情况,而且这些图像很难使用 2D CNN 进行分类。明天我将尝试使用原始 .nii 文件组合一个基本的 3D CNN,看看这是否会提高准确性。

【问题讨论】:

  • 由于1000个数据不是很多,你可能想看看图像数据增强。您还可以在优化器上使用不同的动量或学习率,或者使用 less / more epoch,因为 35 个 epoch 可能会在 1000 个数据上出现过拟合问题跨度>

标签: python tensorflow machine-learning keras conv-neural-network


【解决方案1】:

将所有图像重复 3 次以自己符合 RGB 3 通道。丢失时,使用binary_crossentropy。在测试图像生成器中,您有 (256, 240),使其与您的训练大小相同。

另外,请尝试网络的原始尺寸 (224x224)。

【讨论】:

  • 我打印了每个图像的张量,它们包含三个通道,每个通道都有相同的值,所以 ImageDataGenerator 似乎已经在处理从黑白到 RGB 的转换
  • 然后,做另外两件事。
  • 你为什么要在小尺寸上进行训练而在大尺寸上进行测试。使尺寸统一。
  • 图片的原始尺寸是多少?你把它们做得太小了,这也可能是一个原因。因为原始的 VGG 需要 224x224。缩小尺寸会从图像中剔除信息。
  • 增加层数或者试试ResNet。此外,在最后一层用 1 个神经元制作密集。
【解决方案2】:

您只有 2 个类:是和否。因此,我建议仅通过“sigmoid”激活生成一个通道的输出,即网络的最后一层应该是:

Dense(units=1, activation='sigmoid')

然后用“binary_crossentropy”训练你的网络。开始使用小型简单的 CNN。

【讨论】:

  • 感谢您的回复,我尝试了您提到的最终密集层更改的较小模型,现在训练和验证准确度都锁定在 50%,而不是之前的训练准确度是约为 68%
  • 您的数据集是否公开可用?有时间我可能会尝试检查问题。
  • 有时间等我的网络训练我会去看看。 :)
  • Daniel,你确定你的验证数据是好的吗?我收到了同样奇怪的完美 0.50 的验证准确度。为了检查问题,我忽略了验证数据并使用了 ImageDataGenerator 的 validation_split 参数。 val_accuracy 更改为 0.6057。这仍然有点奇怪,因为测试准确度也是 0.6057,而且这在许多时期都不会改变。我不是 AD 专家,无法直观地对您的训练和验证数据集进行分类
【解决方案3】:

在训练和验证生成器中,您使图像尺寸仅为 64 X 64,而原始尺寸为 250 X 250。减少了网络可以学习的图像细节丢失。我建议您将这些值设置为 224 X 224。 您有 1000 张图像可能不足以获得高水平的准确度。您可能希望使用图像数据生成器中可用的增强功能来增强数据。文档是here. 在您的测试生成器中,您应该使图像大小与用于训练的大小相同。当您使用 VGG16 时,请务必将 weights=imagenet 和图像大小设置为 224 X 224。您正在进行二进制分类,因此我建议顶层为 Dense(units=1,activation='sigmoid')。然后你可以测试输出是高于还是低于 0.5。编译模型时使用 loss="binary_crossentropy"。同时设置top=False和pooling=max,然后添加dense layer。我不使用 Vgg16,因为它有超过 4000 万个可训练参数。我你训练完整的模型,训练时间可能很长。我更喜欢使用只有 400 万个参数且基本一样准确的 Mobilenet。

【讨论】:

  • 增强似乎与这种特定类型的数据无关。也许,只有左右翻转才有意义。肯定没有轮换。我同意其余的建议。
猜你喜欢
  • 1970-01-01
  • 2020-02-12
  • 1970-01-01
  • 2019-03-26
  • 1970-01-01
  • 1970-01-01
  • 2019-11-24
  • 2019-02-12
  • 2019-07-15
相关资源
最近更新 更多