CIFAR-10 的准确率不能超过 60%,带有 Tensorflow 后端的 Keras

机器算法验证 神经网络 卷积神经网络 喀拉斯
2022-03-13 04:37:07

在 CIFAR-10 数据集上训练 15 个 epoch 后似乎使验证损失不再减少,保持在 1.4 左右(验证准确率为 60%)。我已经对训练集进行了洗牌,将其除以 255,然后导入为 float32。我已经尝试了许多架构,无论是在 Conv2D 层中是否有 dropout,但似乎没有任何效果。相同的架构在 MNIST 的测试集上实现了 99.7% 的准确率。请看下面的架构:

(注意:我已经尝试增加 dropout 和增加/减少 Adam 优化器的学习率以防止过度拟合,所有这些都是为了防止过度拟合,但现在训练和测试集的准确率都在 60% 左右)。

with tf.device('/gpu:0'):
    tf.placeholder(tf.float32, shape=(None, 20, 64))
    #placeholder initialized (pick /cpu:0 or /gpu:0)
    seed = 6
    np.random.seed(seed)
    modelnn = Sequential()
    neurons = x_train_reduced.shape[1:]

    modelnn.add(Convolution2D(32, 3, 3, input_shape=neurons, activation='relu', border_mode='same'))
    modelnn.add(Convolution2D(32, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(Dropout(0.2))
    modelnn.add(Convolution2D(64, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(Convolution2D(64, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(Dropout(0.2))
    modelnn.add(Convolution2D(128, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(Convolution2D(128, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(Dropout(0.2))
    #modelnn.add(Convolution2D(256, 3, 3, activation='relu', border_mode='same'))
    #modelnn.add(Convolution2D(256, 3, 3, activation='relu', border_mode='same'))
    #modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(Flatten())
    #modelnn.add(Dropout(0.5))
    modelnn.add(Dense(1024, activation='relu', W_constraint=maxnorm(3)))
    modelnn.add(Dropout(0.5))
    modelnn.add(Dense(512, activation='relu', W_constraint=maxnorm(3)))
    modelnn.add(Dropout(0.5))
    modelnn.add(Dense(10, activation='softmax'))
    modelnn.compile(loss='categorical_crossentropy', optimizer=optimizer_input, metrics=['accuracy'])
    y_train = to_categorical(y_train)
    modelnn.fit(x_train_reduced, y_train, nb_epoch=nb_epoch_count, shuffle=True, batch_size=bsize,
                           validation_split=0.1)

结果:

    44100/44100 [==============================] - 22s - loss: 2.1453 - acc: 0.2010 - val_loss: 1.9812 - val_acc: 0.2959
    Epoch 2/50
    44100/44100 [==============================] - 24s - loss: 1.9486 - acc: 0.3089 - val_loss: 1.8685 - val_acc: 0.3567
    Epoch 3/50
    44100/44100 [==============================] - 18s - loss: 1.8599 - acc: 0.3575 - val_loss: 1.7822 - val_acc: 0.3982
    Epoch 4/50
    44100/44100 [==============================] - 18s - loss: 1.7925 - acc: 0.3933 - val_loss: 1.7272 - val_acc: 0.4229
    Epoch 5/50
    44100/44100 [==============================] - 18s - loss: 1.7425 - acc: 0.4195 - val_loss: 1.6806 - val_acc: 0.4459
    Epoch 6/50
    44100/44100 [==============================] - 18s - loss: 1.6998 - acc: 0.4440 - val_loss: 1.6436 - val_acc: 0.4682
    Epoch 7/50
    44100/44100 [==============================] - 18s - loss: 1.6636 - acc: 0.4603 - val_loss: 1.6156 - val_acc: 0.4837
    Epoch 8/50
    44100/44100 [==============================] - 18s - loss: 1.6333 - acc: 0.4781 - val_loss: 1.6351 - val_acc: 0.4776
    Epoch 9/50
    44100/44100 [==============================] - 18s - loss: 1.6086 - acc: 0.4898 - val_loss: 1.5732 - val_acc: 0.5063
    Epoch 10/50
    44100/44100 [==============================] - 18s - loss: 1.5776 - acc: 0.5065 - val_loss: 1.5411 - val_acc: 0.5227
    Epoch 11/50
    44100/44100 [==============================] - 18s - loss: 1.5585 - acc: 0.5145 - val_loss: 1.5485 - val_acc: 0.5212
    Epoch 12/50
    44100/44100 [==============================] - 18s - loss: 1.5321 - acc: 0.5288 - val_loss: 1.5354 - val_acc: 0.5316
    Epoch 13/50
    44100/44100 [==============================] - 18s - loss: 1.5082 - acc: 0.5402 - val_loss: 1.5022 - val_acc: 0.5427
    Epoch 14/50
    44100/44100 [==============================] - 18s - loss: 1.4945 - acc: 0.5438 - val_loss: 1.4916 - val_acc: 0.5490
    Epoch 15/50
    44100/44100 [==============================] - 192s - loss: 1.4762 - acc: 0.5535 - val_loss: 1.5159 - val_acc: 0.5394
    Epoch 16/50
    44100/44100 [==============================] - 18s - loss: 1.4577 - acc: 0.5620 - val_loss: 1.5389 - val_acc: 0.5257
    Epoch 17/50
    44100/44100 [==============================] - 18s - loss: 1.4425 - acc: 0.5671 - val_loss: 1.4590 - val_acc: 0.5667
    Epoch 18/50
    44100/44100 [==============================] - 18s - loss: 1.4258 - acc: 0.5766 - val_loss: 1.4552 - val_acc: 0.5763
    Epoch 19/50
    44100/44100 [==============================] - 18s - loss: 1.4113 - acc: 0.5805 - val_loss: 1.4439 - val_acc: 0.5767
    Epoch 20/50
    44100/44100 [==============================] - 18s - loss: 1.3971 - acc: 0.5879 - val_loss: 1.4473 - val_acc: 0.5769
    Epoch 21/50
    44100/44100 [==============================] - 18s - loss: 1.3850 - acc: 0.5919 - val_loss: 1.4251 - val_acc: 0.5871
    Epoch 22/50
    44100/44100 [==============================] - 18s - loss: 1.3668 - acc: 0.6006 - val_loss: 1.4203 - val_acc: 0.5910
    Epoch 23/50
    44100/44100 [==============================] - 18s - loss: 1.3549 - acc: 0.6051 - val_loss: 1.4207 - val_acc: 0.5939
    Epoch 24/50
    44100/44100 [==============================] - 18s - loss: 1.3373 - acc: 0.6111 - val_loss: 1.4516 - val_acc: 0.5784
    Epoch 25/50
    44100/44100 [==============================] - 18s - loss: 1.3285 - acc: 0.6149 - val_loss: 1.4146 - val_acc: 0.5922
    Epoch 26/50
    44100/44100 [==============================] - 18s - loss: 1.3134 - acc: 0.6205 - val_loss: 1.4090 - val_acc: 0.6024
    Epoch 27/50
    44100/44100 [==============================] - 18s - loss: 1.3043 - acc: 0.6239 - val_loss: 1.4307 - val_acc: 0.5959
    Epoch 28/50
    44100/44100 [==============================] - 18s - loss: 1.2862 - acc: 0.6297 - val_loss: 1.4241 - val_acc: 0.5978
    Epoch 29/50
    44100/44100 [==============================] - 18s - loss: 1.2706 - acc: 0.6340 - val_loss: 1.4046 - val_acc: 0.6067
    Epoch 30/50
    44100/44100 [==============================] - 18s - loss: 1.2634 - acc: 0.6405 - val_loss: 1.4120 - val_acc: 0.6037
    Epoch 31/50
    44100/44100 [==============================] - 18s - loss: 1.2473 - acc: 0.6446 - val_loss: 1.4067 - val_acc: 0.6045
    Epoch 32/50
    44100/44100 [==============================] - 18s - loss: 1.2411 - acc: 0.6471 - val_loss: 1.4083 - val_acc: 0.6098
    Epoch 33/50
    44100/44100 [==============================] - 18s - loss: 1.2241 - acc: 0.6498 - val_loss: 1.4091 - val_acc: 0.6076
    Epoch 34/50
    44100/44100 [==============================] - 18s - loss: 1.2121 - acc: 0.6541 - val_loss: 1.4209 - val_acc: 0.6127
    Epoch 35/50
    44100/44100 [==============================] - 18s - loss: 1.1995 - acc: 0.6582 - val_loss: 1.4230 - val_acc: 0.6131
    Epoch 36/50
    44100/44100 [==============================] - 18s - loss: 1.1884 - acc: 0.6622 - val_loss: 1.4024 - val_acc: 0.6124
    Epoch 37/50
    44100/44100 [==============================] - 18s - loss: 1.1778 - acc: 0.6657 - val_loss: 1.4328 - val_acc: 0.6080
    Epoch 38/50
    44100/44100 [==============================] - 18s - loss: 1.1612 - acc: 0.6683 - val_loss: 1.4246 - val_acc: 0.6159
    Epoch 39/50
    44100/44100 [==============================] - 18s - loss: 1.1466 - acc: 0.6735 - val_loss: 1.4282 - val_acc: 0.6122
    Epoch 40/50
    44100/44100 [==============================] - 18s - loss: 1.1325 - acc: 0.6783 - val_loss: 1.4311 - val_acc: 0.6157
    Epoch 41/50
    44100/44100 [==============================] - 18s - loss: 1.1213 - acc: 0.6806 - val_loss: 1.4647 - val_acc: 0.6047
    Epoch 42/50
    44100/44100 [==============================] - 18s - loss: 1.1064 - acc: 0.6842 - val_loss: 1.4631 - val_acc: 0.6047
    Epoch 43/50
    44100/44100 [==============================] - 18s - loss: 1.0967 - acc: 0.6870 - val_loss: 1.4535 - val_acc: 0.6106
    Epoch 44/50
    44100/44100 [==============================] - 18s - loss: 1.0822 - acc: 0.6893 - val_loss: 1.4532 - val_acc: 0.6149
    Epoch 45/50
    44100/44100 [==============================] - 18s - loss: 1.0659 - acc: 0.6941 - val_loss: 1.4691 - val_acc: 0.6108
    Epoch 46/50
    44100/44100 [==============================] - 18s - loss: 1.0610 - acc: 0.6956 - val_loss: 1.4751 - val_acc: 0.6106
    Epoch 47/50
    44100/44100 [==============================] - 18s - loss: 1.0397 - acc: 0.6981 - val_loss: 1.4857 - val_acc: 0.6041
    Epoch 48/50
    44100/44100 [==============================] - 18s - loss: 1.0208 - acc: 0.7039 - val_loss: 1.4901 - val_acc: 0.6106
    Epoch 49/50
    44100/44100 [==============================] - 18s - loss: 1.0187 - acc: 0.7036 - val_loss: 1.4994 - val_acc: 0.6106
    Epoch 50/50
    44100/44100 [==============================] - 18s - loss: 1.0024 - acc: 0.7070 - val_loss: 1.5078 - val_acc: 0.6039
    Time: 1109.7512991428375
    Neural Network now trained from dimensions (49000, 3, 32, 32)

更新:进一步的测试,包括有和没有 MaxNorm 的 BatchNormalization -

图像

新架构:

    modelnn.add(Convolution2D(32, 3, 3, input_shape=neurons, activation='relu', border_mode='same'))
    modelnn.add(Convolution2D(32, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(BatchNormalization())
    modelnn.add(Dropout(0.2))
    modelnn.add(Convolution2D(64, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(Convolution2D(64, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(BatchNormalization())
    modelnn.add(Dropout(0.2))
    modelnn.add(Convolution2D(128, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(Convolution2D(128, 3, 3, activation='relu', border_mode='same'))
    modelnn.add(BatchNormalization())
    modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(Dropout(0.2))
    # modelnn.add(Convolution2D(256, 3, 3, activation='relu', border_mode='same'))
    # modelnn.add(Convolution2D(256, 3, 3, activation='relu', border_mode='same'))
    # modelnn.add(MaxPooling2D(pool_size=(2, 2)))
    modelnn.add(Flatten())
    modelnn.add(Dense(1024, activation='relu', W_constraint=maxnorm(3)))
    modelnn.add(BatchNormalization())
    modelnn.add(Dropout(0.5))
    modelnn.add(Dense(512, activation='relu', W_constraint=maxnorm(3)))
    modelnn.add(BatchNormalization())
    modelnn.add(Dropout(0.5))
    modelnn.add(Dense(10, activation='softmax'))
3个回答

请注意,MNIST 是一个比 CIFAR-10 简单得多的问题集,您可以从全连接(非卷积)NNet 中轻松获得 98% 的数据。一个只有一两个卷积层的非常简单的 CNN 也可以达到同样的准确度。

我不确定你的 NNet 架构,但我可以让你在 CIFAR-10 上使用以下架构(相对简单且权重更少)达到 78% 的测试准确率。使用 vanilla 默认值和 Adam 优化器,不需要特殊的初始化或手动操作:

model = Sequential()
model.add(Conv2D(input_shape=trainX[0,:,:,:].shape, filters=96, kernel_size=(3,3)))
model.add(Activation('relu'))
model.add(Conv2D(filters=96, kernel_size=(3,3), strides=2))
model.add(Activation('relu'))
model.add(Dropout(0.2))
model.add(Conv2D(filters=192, kernel_size=(3,3)))
model.add(Activation('relu'))
model.add(Conv2D(filters=192, kernel_size=(3,3), strides=2))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(BatchNormalization())
model.add(Dense(256))
model.add(Activation('relu'))
model.add(Dense(n_classes, activation="softmax"))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

这个架构非常简单,大致基于https://arxiv.org/pdf/1412.6806.pdf

因此训练这个模型:

n_epochs = 25
batch_size = 256
callbacks_list = None
H = model.fit(trainX, trainY, validation_data=(testX, testY), 
              epochs=n_epochs, batch_size=batch_size, callbacks=callbacks_list)
print('Done!!!')

给出以下内容,您可以看到到第 25 个时期几乎达到 77%,并且或多或少从那里变平(但有足够的正则化从 dropout 中防止它由于过度拟合而退化,至少在测试的迭代次数上) .

训练 50000 个样本,验证 10000 个样本
Epoch 1/50
50000/50000 [==============================] - 19s 390us/step - loss: 1.6058 - acc: 0.4150 - val_loss: 1.5285 - val_acc: 0.4669
Epoch 2/50
50000/50000 [======================= =======] - 19s 371us/步 - 损失:1.2563 - acc: 0.5477 - val_loss: 1.1447 - val_acc: 0.5901
Epoch 3/50
50000/50000 [============= =================] - 19s 373us/步 - 损失:1.0784 - acc: 0.6163 - val_loss: 1.1577 - val_acc: 0.6002
...
Epoch 25/50
50000/50000 [ =============================] - 19s 374us/步 - 损失:0.3188 - acc: 0.8857 - val_loss: 0.7493 - val_acc : 0.7680
...
纪元 50/50
50000/50000 [===============================] - 19 秒 373us/步 - 损失:0.1928 - acc: 0.9329 - val_loss : 0.8718 - val_acc: 0.7751
完成!!!

这是一个更简单、更小的架构,使用相同的训练方案(没有 BatchNormalization 或池化层)可以很快达到 70%:

# CNN architecture with Keras
model = Sequential()
model.add(Conv2D(input_shape=trainX[0,:,:,:].shape, filters=32, 
                 use_bias=True, kernel_size=(3,3)))
model.add(Activation('relu'))
model.add(Dropout(0.1))
model.add(Conv2D(filters=64, use_bias=False, kernel_size=(5,5), strides=2))
model.add(Activation('relu'))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dropout(0.3))
model.add(Dense(n_classes, activation="softmax"))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=    ['accuracy'])

值得注意的是,在 CIFAR-10 上达到最佳发布精度(目前在 90-96% 范围内)的架构通常更复杂,并且需要花费大量时间在 GPU 硬件上进行训练。但是我已经能够通过在几分钟内训练的相当简单的架构达到 70-80% 的范围,这是我在获得通常需要更复杂的架构、更长的训练周期的最佳发布结果之前推荐的方法,有时是特殊的手持/培训方案或数据增强,以及培训时间。

更新:

根据问题中的更新图,最明显的问题是过度拟合。大约在第 15 个 epoch 之后,训练测试数据的分歧证明了这一点,这表明该架构对该数据集的正则化不足。除非过度拟合得到解决,否则您不太可能通过调整任何其他超参数(归一化策略、学习率等)获得改进。

在使用 NNets 时,我建议如下:

  1. 从模仿或复制已知产生良好结果的架构开始
  2. 验证数据集的性能,特别注意网络中的过度拟合(训练测试错误的显着差异证明了这一点)
  3. 当观察到过度拟合时添加额外的正则化(增加丢失率)(您正在寻找“刚好足够”以防止过度拟合 - 太多会导致拟合不足)
  4. 尝试结构、训练方法和超参数以找到改进的途径

关于后者的规定实际上很难获得,因为结构、训练或超参数如何相互作用以在任何给定数据集上产生性能几乎没有理论基础。在基准数据集上实现类似高水平性能的已发布架构所采用的方法差异如此之大就是证明。

已经发现批量标准化可以显着改进某些架构,但其他架构可以在没有它的情况下做得很好(或者对它的存在漠不关心)。在这里提供的唯一真正的指导是尝试一下,看看它是否有帮助。

通常应该避免微调学习率,除非您是一名高级从业者,对 ConvNets 有深入的了解,并且在训练期间具有阅读茶叶关于跨时代增量性能的相应能力。在某些情况下,定制的学习率和其他专门的训练方案可以帮助网络绕过局部最小值并找到更好的整体解决方案,但除非你有大量时间和专业知识来诊断网络的收敛行为,否则这不是一个很好的起点。我们中的大多数人应该使用像 Adam 这样的优化器,它在绝大多数情况下会优于新手尝试手动调整学习率。

通过图像预处理进行数据增强有时可以显着提高性能(通常,输入数据越多,模型的泛化效果就越好 - 数据预处理会增加输入空间的变化,这可以提高样本外的准确性,并且可能减少正则化要求——假设有无限的训练数据,我们根本不需要任何正则化,但在图像处理空间中,我们不太可能接近那个渐近线)。不过,这会显着增加训练时间并降低收敛速度,并引入与输入图像置换技术(旋转、裁剪、缩放、噪声等)相关的另一组超参数。因为这条路径可能会增加训练时间并需要额外的实验来调整结果,所以一些一般建议是先在不进行增强的情况下在网络中获得最佳准确性,然后看看一些适度的增强是否会产生改进。如果是这样,它可能需要进一步的实验。

对于任何和所有调优实验,您都需要密切关注过度拟合和欠拟合行为的变化。更改网络架构、训练方案或超参数可能需要额外调整 dropout 正则化。从训练/测试性能中轻松确定过拟合和欠拟合行为的能力可以说是使用 NNets 的最重要的基线技能,并且随着经验的增长,这变得更加直观。

这是指导你所有努力的蜡烛。蜡烛只能模糊地照亮道路,但没有它,您将在黑暗中跌跌撞撞。如果您的网络严重过拟合或欠拟合,则应在尝试随机排列网络结构或超参数之前解决该问题。

此答案中包含的具有普通训练方案的相对简单的架构展示了使用 NNET 架构处理图像分类等难题的现实:基于已知效果良好的方法获得“相当不错”的结果并不困难,但渐进式改进成本越来越高。通过实验获得最佳发表结果将超出许多人的能力或时间可用性(尽管有可能,只要有足够的时间和精力,可以按照已发表方法的食谱来复制他们的结果——但即使这样也不是意思是微不足道的)。从“相当不错”的起点获得增量改进可能是一个非常耗时的试错过程,而且许多实验不会产生任何显着的改进。

这并不是要劝阻任何人尝试学习,而只是为了清楚地表明,要掌握 NNet 技巧包中的(不断扩展的)工具集需要大量投资,并且通过反复试验来推动改进可能需要在数天或数周的专用 GPU 训练中进行数十次(或数百次)实验。

将网络训练到非常高的性能水平所需的时间、技能和资源(专用 GPU)部分解释了预训练网络的流行。

查看您的样本内和样本外损失和准确度值,您的模型当前拟合不足,但它正在单调改进。换句话说,似乎运行更多 epoch 会导致更高的预测性能/更少的熵损失。

您正在使用高度正则化(辍学层)架构,这还不错。然而,训练花费的时间比没有任何正则化的时间长得多也就不足为奇了。由于存在辍学层,您不太可能(基本上)过拟合。

您可以尝试加速学习的事情:

一世。调整学习率:例如,从一个小的开始,在中间提高,最后再降低。

ii. 添加批量标准化:在上面的架构中,您可以在卷积块和密集层中包含批量规范。通常,batch-norm 层是在非线性激活之后但在 dropout 之前添加的。我不确定 batch-norm 与 maxnorm 的效果如何。对于您的密集层,我会尝试使用/不使用 maxnorm 的 batch-norm+dropuout。如果您应用批量标准化,我觉得您不需要 maxnorm 。

iii. 增加批量大小:我不确定您的批量大小是多少以及您是否拥有 GPU。如果你有一个 GPU,你可能应该尝试以 32 的乘法来最大化你的批量大小。

最后,为了确保您的数据是“可学习的”/不损坏的(例如,您没有不情愿地应用转换来扭曲它),我会从您的架构中丢弃所有正则化,运行训练并查看您可以过度拟合训练集. 如果你能成功学习训练数据,剩下的就是泛化练习了。如果即使没有正则化也无法过度拟合训练数据,则很可能您的模型需要更多容量(更深和更广泛的架构)。

我今天试了一下,测试准确率始终保持在 75-80% 附近。

培训历史

  • 使用的参数总数为:183,242

  • 您可以通过添加更多层来做得更好,但您不需要过度。更复杂的网络并不总能产生更好的结果。

建议

我对你的建议是,你要保持你的架构简单。遵循奥卡姆剃刀原则,越简单越好。

  • 扩展您的数据

  • 不要使用随机种子

  • 使用适当的优化器;我使用来自 Keras 的 Adadelta。

  • CNN 不需要复杂;把事情简单化

  • 更深更窄的网络有时比更宽的网络效果更好

  • 使用正则化(例如 Dropout)

下面是我的代码(使用 Keras)

# Define the model
model = Sequential()
model.add(Convolution2D(64, (4, 4), padding='same', input_shape=(3, 32, 32)))
model.add(MaxPooling2D(pool_size=(2, 2), strides=2))
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(Convolution2D(64, (2, 2), padding='same'))
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(Convolution2D(32, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(Convolution2D(32, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2), strides=2))
model.add(Dropout(0.15))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.25))
model.add(Dense(64))
model.add(Activation('tanh'))
model.add(Dropout(0.25))
model.add(Dense(num_classes, activation='softmax'))
# Compile the model