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 激活函数。
我有一个睁眼和闭眼的图片集。
使用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 激活函数。