卷积神经网络是基于人工神经网络的深度机器学习方法,成功应用于图像识别领域。CNN 采用了局部连接和权值共享,保持了网络的深层结构,同时又减少了网络参数,使模型具有良好的泛化能力又较容易训练,CNN 的训练算法是梯度下降的错误反向传播(Back Propagate,BP)算法的一种变形。
卷积神经网络通常采用若干个卷积和子采样层的叠加结构作为特征抽取器。卷积层与子采样层不断将特征图缩小,但是特征图的数量往往增多。特征抽取器后面接一个分类器,分类器通常由一个多层感知机构成。在特征抽取器的末尾,我们将所有的特征图展开并排列成为一个向量,称为特征向量,该特征向量作为后层分类器的输入,如下图所示:
(点击放大图像)
卷积过程有三个二维矩阵参与,它们分别是两个特征图和一个卷积核:原图 inputX、输出图 outputY、卷积核 kernelW。卷积过程可以理解为卷积核卷积核 kernalW 覆盖在原图 inputX 的一个局部的面上,kernalW 对应位置的权重乘于 inputX 对应神经元的输出,对各项乘积求和并赋值到 outputY 矩阵的对应位置。卷积核在 inputX 图中从左向右,从上至下每次移动一个位置,完成整张 inputX 的卷积过程,如下图所示:
(点击放大图像)
子采样有两种方式,一种是均值子采样,一种是最大值子采样,如下图所示:
(点击放大图像)
在最大值子采样中的卷积核中,只有一个值为 1,其他值为 0,保留最强输入值,卷积核在原图上的滑动步长为 2,相当于把原图缩减到原来的 1/4。均值子采样卷积核中的每个权重为 0.25,保留的是输入图的均值数据。
卷积核的本质是神经元之间相互连接的权重,而且该权重被属于同一特征图的神经元所共享。在实际的网络训练过程中,输入神经元组成的特征图被切割成卷积核大小的小图。每个小图通过卷积核与后层特征图的一个神经元连接。一个特征图上的所有小图和后层特征图中某个神经元的连接使用的是相同的卷积核,也就是同特征图的神经元共享了连接权重。
在 TensorFlow 中,卷积神经网络(Convolutional neural networks,CNNs)主要包含三种类型的组件,主要 API 如下:
- conv = tf.layers.conv2d(inputs = pool, filters = 64, kernel_size = [5, 5], padding = "same", activation = tf.nn.relu)
inputs 表示输入要的 Tensor,filters 表示卷积核的数量,kernel_size 表示卷积核的大小,padding 表示卷积的边界处理方式,有 valid 和 same 两种方式,valid 方式不会在原有输入的基础上添加新的像素,same 表示需要对 input 的边界数据进行填存,具体计算公式参见 https://www.tensorflow.org/api_docs/python/tf/nn/convolution。
activation 表示要采用的激活函数。
- tf.layers.max_pooling2d(inputs = conv, pool_size = [2, 2], strides = 2)
inputs 表示要被池化的输入 Tensor,pool_size 表示池化窗口大小,strides 表示进行池化操作的步长。
- tf.layers.dense(inputs = pool2_flat, units = 1024, activation = tf.nn.relu)
inputs 表示输入层,units 表示输出层的 tensor 的形状为 [batchsize, units],activation 表示要采用的激化函数。
使用 TensorFlow API 构建卷积神经网络的示例代码,如下所示:
- #输入层#改变输入数据维度为4 - D tensor: [batch_size, width, height, channels]#图像数据为28x28像素大小,
- 并且为单通道input_layer = tf.reshape(features, [ - 1, 28, 28, 1])
- #卷积层1#卷积核大小为5x5,卷积核数量为32,激活方法使用RELU#输入Tensor维度: [batch_size, 28, 28, 1]#输出Tensor维度: [batch_size, 28, 28, 32] conv1 = tf.layers.conv2d(inputs = input_layer, filters = 32, kernel_size = [5, 5], padding = "same", activation = tf.nn.relu)
- #池化层1#采用2x2维度的最大化池化操作,步长为2#输入Tensor维度: [batch_size, 28, 28, 32]#输出Tenso维度: [batch_size, 14, 14, 32] pool1 = tf.layers.max_pooling2d(inputs = conv1, pool_size = [2, 2], strides = 2)
- #卷积层2#卷积核大小为5x5,卷积核数量为64,激活方法使用RELU.#输入Tensor维度: [batch_size, 14, 14, 32]#输出Tensor维度: [batch_size, 14, 14, 64] conv2 = tf.layers.conv2d(inputs = pool1, filters = 64, kernel_size = [5, 5], padding = "same", activation = tf.nn.relu)
- #池化层2#采用2x2维度的最大化池化操作,步长为2#输入Tensor维度: [batch_size, 14, 14, 64]#输出Tensor维度: [batch_size, 7, 7, 64] pool2 = tf.layers.max_pooling2d(inputs = conv2, pool_size = [2, 2], strides = 2)
- #展开并列池化层输出Tensor为一个向量#输入Tensor维度: [batch_size, 7, 7, 64]#输出Tensor维度: [batch_size, 7 * 7 * 64] pool2_flat = tf.reshape(pool2, [ - 1, 7 * 7 * 64])
- #全链接层#该全链接层具有1024神经元#输入Tensor维度: [batch_size, 7 * 7 * 64]#输出Tensor维度: [batch_size, 1024] dense = tf.layers.dense(inputs = pool2_flat, units = 1024, activation = tf.nn.relu)
- #对全链接层的数据加入dropout操作,防止过拟合#40 % 的数据会被dropout,
- dropout = tf.layers.dropout(inputs = dense, rate = 0.4, training = mode == learn.ModeKeys.TRAIN)
- #Logits层,对dropout层的输出Tensor,执行分类操作#输入Tensor维度: [batch_size, 1024]#输出Tensor维度: [batch_size, 10] logits = tf.layers.dense(inputs = dropout, units = 10)
CIFAR-10,http://www.cs.toronto.edu/~kriz/cifar.html,是图片识别的 benchmark 问题,主要对 RGB 为 32*32 的图像进行 10 分类,类别包括:airplane, automobile, bird, cat, deer, dog, frog, horse, ship, and truck。其中包括 50000 张训练图片,10000 张测试图片。
(点击放大图像)
TensorFlow Cifar10,https://github.com/tensorflow/models/tree/master/tutorials/image/cifar10,模型包含 1,068,298 个参数,单个图片的推导包含 19.5M 个乘法 / 加法运算。该模型在 GPU 上运行几个小时后,测试精确度会达到 86%。模型特性主要包括:
该模型的代码结构如下:
该模型的 Graph 结构如下:
(点击放大图像)
下图为 Cifar10 的多 GPU 模型架构,每个 GPU 型号最好相同,具备足够的内存能运行整个 Cifar10 模型。
(点击放大图像)
该架构会复制 Cifar10 模型到每个 GPU 上,每个 GPU 上训练完一个 Batch 的数据后,在 CPU 端对梯度执行同步操作(求均值),更新训练参数,然后把模型参数发送给每个 GPU,进行下一个 Batch 数据的训练。
Inception V3,http://arxiv.org/abs/1512.00567,模型包含 25 million 个模型参数,对单个图片的推导包含了 5 billion 的乘法 / 加法运算。top-1 的误差率降到了 21.2%,top-5 的误差率降到了 5.6%。该模型网络结构如下图所示:
(点击放大图像)
由于 ImageNet 的训练数据比较大,下面主要介绍如何使用 Flower 的数据进行训练,该数据集有 5 种类别(daisy, dandelion, roses, sunflowers, tulips)的花,大概有几千张图片。
首先我们需要下载 TensorFlow Inception V3 模型,如下所示:
git pull https://github.com/tensorflow/models
对花数据进行训练的代码结构,如下所示:
- #进入Inception V3程序目录cd models / inception#设定Flower数据的存储路径FLOWERS_DATA_DIR = /tmp/flowers - data /
- #编译程序bazel build //inception:download_and_preprocess_flowers
- #执行下载和转换TFRecord操作bazel - bin / inception / download_and_preprocess_flowers "${FLOWERS_DATA_DIR}"转换好的训练数据包括:train - 00000 - of - 00002,train - 00001 - of - 00002转换好的验证数据包括:validation - 00000 - of - 00002,validation - 00001 - of - 00002
- 基于训练好的Inception V3模型,继续训练花的数据:#设定Inception V3模型下载路径INCEPTION_MODEL_DIR = $HOME / inception - v3 - model mkdir - p $ {
- INCEPTION_MODEL_DIR
- }
- cd $ {
- INCEPTION_MODEL_DIR
- }
- #下载训练好的Inception模型curl - O http: //download.tensorflow.org/models/image/imagenet/inception-v3-2016-03-01.tar.gz
- tar xzf inception - v3 - 2016 - 03 - 01.tar.gz
- #编译程序bazel build //inception:flowers_train
- #设定要加载的模型路径MODEL_PATH = "${INCEPTION_MODEL_DIR}/inception-v3/model.ckpt-157585"
- #设定训练数据路径FLOWERS_DATA_DIR = /tmp/flowers - data /
- #执行模型训练bazel - bin / inception / flowers_train\--train_dir = "${TRAIN_DIR}"\--data_dir = "${FLOWERS_DATA_DIR}"\--pretrained_model_checkpoint_path = "${MODEL_PATH}"\--fine_tune = True\--initial_learning_rate = 0.001\--input_queue_memory_factor = 1
采用单 GPU,训练 1000 次迭代后,模型 loss 值降到 1.04,如下所示:
(点击放大图像)
VGG 网络与 AlexNet 类似,也是一种 CNN,VGG 在 2014 年的 ILSVRC localization and classification 两个问题上分别取得了第一名和第二名。VGG 网络非常深,通常有 16-19 层,卷积核大小为 3 x 3,16 和 19 层的区别主要在于后面三个卷积部分卷积层的数量。可以看到 VGG 的前几层为卷积和 maxpool 的交替,后面紧跟三个全连接层,激活函数采用 Relu,训练采用了 dropout。VGG 中各模型配置如下, 其中 VGG19 的 top-1 的训练精度可达到 71.1%,top-5 的训练精度可达到 89.8%。模型结构示例如下:
(点击放大图像)
(点击放大图像)
TensorFlow Vgg19 的模型示例如下,https://github.com/tensorflow/models/blob/master/slim/nets/vgg.py:
- #卷积操作和池化操作net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope = 'conv1') net = slim.max_pool2d(net, [2, 2], scope = 'pool1') net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope = 'conv2') net = slim.max_pool2d(net, [2, 2], scope = 'pool2') net = slim.repeat(net, 4, slim.conv2d, 256, [3, 3], scope = 'conv3') net = slim.max_pool2d(net, [2, 2], scope = 'pool3') net = slim.repeat(net, 4, slim.conv2d, 512, [3, 3], scope = 'conv4') net = slim.max_pool2d(net, [2, 2], scope = 'pool4') net = slim.repeat(net, 4, slim.conv2d, 512, [3, 3], scope = 'conv5') net = slim.max_pool2d(net, [2, 2], scope = 'pool5') net = slim.conv2d(net, 4096, [7, 7], padding = fc_conv_padding, scope = 'fc6')#dropout操作,防止过拟合net = slim.dropout(net, dropout_keep_prob, is_training = is_training, scope = 'dropout6') net = slim.conv2d(net, 4096, [1, 1], scope = 'fc7') net = slim.dropout(net, dropout_keep_prob, is_training = is_training, scope = 'dropout7') net = slim.conv2d(net, num_classes, [1, 1], activation_fn = None, normalizer_fn = None, scope = 'fc8')
本文首先回顾了深度卷积神经网络的特征图、卷积核,池化操作,全链接层等基本概念。接着介绍了使用 TensorFlow API 构建卷积神经网络,主要包括卷积操作 API,池化操作 API 以及全链接操作 API。针对图片识别,讲解了 TensorFlow Benchmark 模型(Cifar10,Inception V3 及 Vgg19)的架构和代码。如果有用户需要对自己的业务图片进行识别,可再已有模型的基础上持续改进,进行训练及调优,加速研发。
来源: http://www.infoq.com/cn/articles/rust-in-2017-rust-past-present-and-future