跳转至

18.6 Cifar-10分类

18.6 Cifar-10分类⚓︎

Cifar 是加拿大政府牵头投资的一个先进科学项目研究所。Hinton、Bengio和他的学生在2004年拿到了 Cifar 投资的少量资金,建立了神经计算和自适应感知项目。这个项目结集了不少计算机科学家、生物学家、电气工程师、神经科学家、物理学家、心理学家,加速推动了 Deep Learning 的进程。从这个阵容来看,DL 已经和 ML 系的数据挖掘分的很远了。Deep Learning 强调的是自适应感知和人工智能,是计算机与神经科学交叉;Data Mining 强调的是高速、大数据、统计数学分析,是计算机和数学的交叉。

Cifar-10 是由 Hinton 的学生 Alex Krizhevsky、Ilya Sutskever 收集的一个用于普适物体识别的数据集。

18.6.1 提出问题⚓︎

我们在前面的学习中,使用了MNIST和Fashion-MNIST两个数据集来练习卷积网络的分类,但是这两个数据集都是单通道的灰度图。虽然我们用彩色的几何图形作为例子讲解了卷积网络的基本功能,但是仍然与现实的彩色世界有差距。所以,本节我们将使用Cifar-10数据集来进一步检验一下卷积神经网络的能力。

图18-41是Cifar-10的样本数据。

图18-41 Cifar-10样本数据

  1. airplane,飞机,6000张
  2. automobile,汽车,6000张
  3. bird,鸟,6000张
  4. cat,猫,6000张
  5. deer,鹿,6000张
  6. dog,狗,6000张
  7. frog,蛙,6000张
  8. horse,马,6000张
  9. ship,船,6000张
  10. truck,卡车,6000张

Cifar-10 由60000张32*32的 RGB 彩色图片构成,共10个分类。50000张训练,10000张测试。分为6个文件,5个训练数据文件,每个文件中包含10000张图片,随机打乱顺序,1个测试数据文件,也是10000张图片。这个数据集最大的特点在于将识别迁移到了普适物体,而且应用于多分类(姊妹数据集Cifar-100达到100类,ILSVRC比赛则是1000类)。

但是,面对彩色数据集,用CPU做训练所花费的时间实在是太长了,所以本节将学习如何使用GPU来训练神经网络。

18.6.2 环境搭建⚓︎

我们将使用Keras\(^{[1]}\)来训练模型,因为Keras是一个在TensorFlow平台上经过抽象的工具,它的抽象思想与我们在前面学习过的各种Layer的概念完全一致,有利于读者在前面的基础上轻松地继续学习。环境搭建有很多细节,我们在这里不详细介绍,只是把基本步骤列出。

  1. 安装Python 3.6(本书中所有案例在Python 3.6上开发测试)
  2. 安装CUDA(没有GPU的读者请跳过)
  3. 安装cuDNN(没有GPU的读者请跳过)
  4. 安装TensorFlow,有GPU硬件的一定要按照GPU版,没有的只能安装CPU版
  5. 安装Keras

安装好后用pip list看一下,关键的几个包是:

Package              Version
-------------------- ---------
Keras                2.2.5
Keras-Applications   1.0.8
Keras-Preprocessing  1.1.0
matplotlib           3.1.1
numpy                1.17.0
tensorboard          1.13.1
tensorflow-estimator 1.13.0
tensorflow-gpu       1.13.1

如果没有GPU,则"tensorflow-gpu"一项会是"tensorflow"。

18.6.3 代码实现⚓︎

batch_size = 32
num_classes = 10
epochs = 25
data_augmentation = True
num_predictions = 20
save_dir = os.path.join(os.getcwd(), 'saved_models')
model_name = 'keras_cifar10_trained_model.h5'

# The data, split between train and test sets:
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# Convert class vectors to binary class matrices.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',
                 input_shape=x_train.shape[1:]))
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

# initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)

# Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

if not data_augmentation:
    print('Not using data augmentation.')
    model.fit(x_train, y_train,
              batch_size=batch_size,
              epochs=epochs,
              validation_data=(x_test, y_test),
              shuffle=True)
else:
    ...

在这个模型中:

  1. 先用卷积->激活->卷积->激活->池化->丢弃层,做为第一梯队,卷积核32个;
  2. 然后再用卷积->激活->卷积->激活->池化->丢弃层做为第二梯队,卷积核64个;
  3. Flatten和Dense相当于把池化的结果转成Nx512的全连接层,N是池化输出的尺寸,被Flatten扁平化了;
  4. 再接丢弃层,避免过拟合;
  5. 最后接10个神经元的全连接层加Softmax输出。

为什么每一个梯队都要接一个DropOut层呢?因为这个网络结果设计已经比较复杂了,对于这个问题来说很可能会过拟合,所以要避免过拟合。如果简化网络结构,又可能会造成训练时间过长而不收敛。

18.6.4 训练结果⚓︎

在GPU上训练⚓︎

在GPU上训练,每一个epoch大约需要1分钟;而在一个8核的CPU上训练,每个epoch大约需要2分钟(据笔者观察是因为并行计算占满了8个核)。所以即使读者没有GPU,用CPU训练还是可以接受的。以下是在GPU上的训练输出:

Epoch 1/25
1563/1563 [==============================] - 33s 21ms/step - loss: 1.8770 - acc: 0.3103 - val_loss: 1.6447 - val_acc: 0.4098
......
Epoch 25/25
1563/1563 [==============================] - 87s 55ms/step - loss: 0.8809 - acc: 0.6960 - val_loss: 0.7724 - val_acc: 0.7372

Test loss: 0.772429921245575
Test accuracy: 0.7372
经过25轮后,模型在测试集上的准确度为73.72%。

在CPU上训练⚓︎

在CPU上训练,只设置了10个epoch,一共半个小时时间,在测试集上达到63.61%的准确率。观察val_loss和val_acc的趋势,随着训练次数的增加,还可以继续优化。

Epoch 1/10
1563/1563 [==============================] - 133s 85ms/step - loss: 1.8563 - acc: 0.3198 - val_loss: 1.5658 - val_acc: 0.4343
......

Epoch 10/10
1563/1563 [==============================] - 131s 84ms/step - loss: 1.0972 - acc: 0.6117 - val_loss: 1.0426 - val_acc: 0.6361

10000/10000 [==============================] - 7s 684us/step
Test loss: 1.042622245979309
Test accuracy: 0.6361

代码位置⚓︎

ch18, Level6

参考资料⚓︎

[1] 参考 https://keras.io/examples/cifar10_cnn/