CNN 在图像二值分类上的准确率为 50%

50% accuracy in CNN on image binary classification

我有一个睁眼和闭眼的图片集。
使用keras从当前目录收集数据,方式如下:

batch_size = 64
N_images = 84898 #total number of images
datagen = ImageDataGenerator(
    rescale=1./255)
data_iterator = datagen.flow_from_directory(
    './Eyes',
    shuffle = 'False',
    color_mode='grayscale',
    target_size=(h, w),
    batch_size=batch_size,
    class_mode = 'binary')

我有一个包含每只眼睛状态的 .csv 文件。

我构建了这个顺序模型:

num_filters = 8
filter_size = 3
pool_size = 2

model = Sequential([
  Conv2D(num_filters, filter_size, input_shape=(90, 90, 1)),
  MaxPooling2D(pool_size=pool_size),
  Flatten(),
  Dense(16, activation='relu'),
  Dense(2, activation='sigmoid'), # Two classes. one for "open" and another one for "closed"
])

模型编译。

model.compile(
    'adam',
    loss='categorical_crossentropy',
    metrics=['accuracy']
)

最后,我将所有数据拟合如下:

model.fit(
  train_images,
  to_categorical(train_labels),
  epochs=3,
  validation_data=(test_images, to_categorical(test_labels)),
)

结果波动在50%左右,我不明白为什么。

您当前的模型基本上只有一个卷积层。也就是说,num_filters 卷积滤波器(在本例中为 3 x 3 阵列)被定义和拟合,以便当它们与图像卷积时,它们产生的特征在 class 之间尽可能具有辨别力es。然后,您执行最大池化以在传递到 2 个密集层之前稍微减少输出 CNN 特征的维度。

我首先要说一个卷积层几乎肯定是不够的,尤其是对于 3x3 过滤器。基本上,对于单个卷积层,您可以获得的最有意义的信息是边缘或线条。这些特征对于函数逼近器(即你的全连接层)仅比原始像素强度值稍微有用一点,因为它们在 class 内和 class 之间仍然具有极高的可变性.考虑将眼睛图像向左移动 2 个像素会导致从 1 层 CNN 输出完全不同的值。您希望 CNN 的输出不随比例、旋转、照明等变化。

实际上,这意味着您将需要更多的卷积层。相对简单的 VGG 网络至少有 14 个卷积层,现代基于残差层的网络通常有超过 100 个卷积层。尝试编写一个例程来定义顺序更复杂的网络,直到您开始看到性能提升。

作为次要点,通常您不想在训练期间对最终层输出使用 sigmoid() 激活函数。这会使梯度变平,并使反向传播损失的速度变慢。您实际上并不关心输出值落在 0 和 1 之间,您只关心它们的相对大小。通常的做法是使用交叉熵损失,它结合了对数 softmax 函数(梯度比普通 softmax 更稳定)和负对数似然损失,正如您已经完成的那样。因此,由于对数 softmax 部分将输出值转换到所需范围内,因此无需使用 sigmoid 激活函数。