首页 > 解决方案 > Keras模型评估精度不变,设计模型

问题描述

我正在尝试在 Keras 中设计一个 CNN 来对其他图像中的表情符号小图像进行分类。下面是 13 个类之一的示例。所有图像的大小相同,所有表情符号的大小也相同。我认为一个人在分类时应该很容易达到非常高的准确度,因为一个类别的表情符号是完全相同的!我的直觉告诉我,如果 emoji 是 50x50,我可以创建一个相同大小的卷积层来匹配一种 emoji。然而,我的主管认为这是不可行的。无论如何,我的问题是,无论我如何设计我的模型,我总是为每个 epoch 获得相同的验证准确度,对应于 1/13(或者简单地猜测每个 emoji 属于同一类)。

我的模型如下所示:

model = Sequential()
model.add(Conv2D(16, kernel_size=3, activation="relu", input_shape=IMG_SIZE))
model.add(Dropout(0.5))
model.add(Conv2D(32, kernel_size=3, activation="relu"))
model.add(Conv2D(64, kernel_size=3, activation="relu"))
model.add(Conv2D(128, kernel_size=3, activation="relu"))
#model.add(Conv2D(256, kernel_size=3, activation="relu"))
model.add(Dropout(0.5))
model.add(Flatten()) 
#model.add(Dense(256, activation="relu"))
model.add(Dense(128, activation="relu"))
model.add(Dense(64, activation="relu"))
model.add(Dense(NUM_CLASSES, activation='softmax', name="Output")) 

我这样训练它:

# ------------------ Compile and train ---------------
sgd = optimizers.SGD(lr=0.001, decay=1e-6, momentum=0.9, nesterov=True)
rms = optimizers.RMSprop(lr=0.004, rho=0.9, epsilon=None, decay=0.0)

model.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=["accuracy"])  # TODO Read more about this
train_hist = model.fit_generator(
    train_generator,
    steps_per_epoch=train_generator.n // BATCH_SIZE,
    validation_steps=validation_generator.n // BATCH_SIZE,  # TODO que?
    epochs=EPOCHS,
    validation_data=validation_generator,
    #callbacks=[EarlyStopping(patience=3, restore_best_weights=True)]
)

即使使用这个具有超过 2 亿个参数的模型,我在每个 epoch 的验证准确度上也正好是 0.0773:

Epoch 1/10
56/56 [==============================] - 21s 379ms/step - loss: 14.9091 - acc: 0.0737 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 2/10
56/56 [==============================] - 6s 108ms/step - loss: 14.9308 - acc: 0.0737 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 3/10
56/56 [==============================] - 6s 108ms/step - loss: 14.7869 - acc: 0.0826 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 4/10
56/56 [==============================] - 6s 108ms/step - loss: 14.8948 - acc: 0.0759 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 5/10
56/56 [==============================] - 6s 109ms/step - loss: 14.8897 - acc: 0.0762 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 6/10
56/56 [==============================] - 6s 109ms/step - loss: 14.8178 - acc: 0.0807 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 7/10
56/56 [==============================] - 6s 108ms/step - loss: 15.0747 - acc: 0.0647 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 8/10
56/56 [==============================] - 6s 108ms/step - loss: 14.7509 - acc: 0.0848 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 9/10
56/56 [==============================] - 6s 108ms/step - loss: 14.8948 - acc: 0.0759 - val_loss: 14.8719 - val_acc: 0.0773
Epoch 10/10
56/56 [==============================] - 6s 108ms/step - loss: 14.8228 - acc: 0.0804 - val_loss: 14.8719 - val_acc: 0.0773

因为它没有学到任何东西,我开始认为这不是我的模型的错,而可能是数据集或我如何训练它。我也尝试过使用“adam”进行训练,但得到了相同的结果。我尝试更改图像的输入大小,但结果仍然相同。下面是我的数据集中的一个示例。你们有什么想法可能是错的吗?

与数据集相同

标签: pythontensorflowkerasdeep-learningmulticlass-classification

解决方案


我认为目前的主要问题是,相对于训练样本的数量而言,您的模型参数太多。对于现在的图像分类,您通常只需要一个卷积层、一个 Global Something Pooling 层,然后是一个用于输出的 Dense 层。您只需要确保您的 conv 部分最终具有足够大的感受野,以便能够找到您需要的所有功能。

首先要考虑的是确保您有足够大的感受野(在此处进一步阅读)。实现这一点的主要方法有三种:池化、步幅 >= 2 和/或膨胀 >= 2。因为您只有 13 个“特征”要识别,而且它们都将始终是像素完美的,我我认为扩张将是一种方法,这样模型就可以轻松地“过度拟合”这 13 个“特征”。如果我们使用 4 个卷积层,分别膨胀 1、2、4 和 8,那么我们最终会得到 31 的感受野。这应该足以轻松识别 50 像素的表情符号。

接下来,每一层应该有多少个过滤器?通常,您从几个过滤器开始,并随着您浏览模型而增加,就像您在这里所做的那样。但是,我们希望在特定特征上“过度拟合”,因此我们可能应该增加早期层的数量。为了简单起见,让我们为所有层设置 64 个过滤器。

最后,我们如何将这一切转换为单个预测?现在的人们使用 GlobalAveragePooling 或 GlobalMaxPooling ,而不是使用会使用大量参数并且不会是平移不变的密集层。GlobalAveragePooling 更常见,因为它有助于找到许多特征的组合。但是,我们只想在这里找到大约 13 个确切的特征,因此 GlobalMaxPooling 可能会更好。然后之后的单个密集层将足以获得预测。因为我们使用的是Global MaxPooling,所以不需要先将其展平——全球池已经为我们做到了。

结果模型:

Conv2D(64, kernel_size=3, activation="relu", input_shape=IMG_SIZE)
Conv2D(64, kernel_size=3, dilation_rate=2, activation="relu")
Conv2D(64, kernel_size=3, dilation_rate=4, activation="relu")
Conv2D(64, kernel_size=3, dilation_rate=8, activation="relu")
GlobalMaxPooling()
Dense(NUM_CLASSES, activation='softmax', name="Output")

试试看。您可能还想在除最后一层之外的每一层之后添加 BatchNormalization 层。一旦你获得了不错的训练准确度,检查它是否过度拟合。如果是(很可能),请尝试以下步骤:

  • 如果您还没有批量规范
  • 所有卷积层和密集层的权重衰减
  • 卷积层后的 SpatialDropout2D 和池化层后的常规 Dropout
  • 使用 ImageDataGenerator 扩充您的数据

设计这样的网络几乎更像是一门艺术而不是一门科学,所以如果你认为你应该改变一些东西。最终,您将直观地了解在任何给定情况下或多或少可能起作用的方法。


推荐阅读