卷积神经网络

卷积神经网络是基于人工神经网络的深度机器学习方法,成功应用于图像识别领域。CNN采用了局部连接和权值共享,保持了网络的深层结构,同时又减少了网络参数,使模型具有良好的泛化能力又较容易训练,CNN的训练算法是梯度下降的错误反向传播(Back Propagate,BP)算法的一种变形

卷积神经网络通常采用若干个卷积和子采样层的叠加结构作为特征抽取器。卷积层与子采样层不断将特征图缩小,但是特征图的数量往往增多。特征抽取器后面接一个分类器,分类器通常由一个多层感知机构成。在特征抽取器的末尾,我们将所有的特征图展开并排列成为一个向量,称为特征向量,该特征向量作为后层分类器的输入,如下图所示:

(点击放大图像)


卷积过程有三个二维矩阵参与,它们分别是两个特征图和一个卷积核:原图inputX、输出图outputY、卷积核kernelW。卷积过程可以理解为卷积核卷积核kernalW覆盖在原图inputX的一个局部的面上,kernalW对应位置的权重乘于inputX对应神经元的输出,对各项乘积求和并赋值到outputY矩阵的对应位置。卷积核在inputX图中从左向右,从上至下每次移动一个位置,完成整张inputX的卷积过程,如下图所示:

(点击放大图像)

子采样有两种方式,一种是均值子采样,一种是最大值子采样,如下图所示:

(点击放大图像)

在最大值子采样中的卷积核中,只有一个值为1,其他值为0,保留最强输入值,卷积核在原图上的滑动步长为2,相当于把原图缩减到原来的1/4。均值子采样卷积核中的每个权重为0.25,保留的是输入图的均值数据。

卷积核的本质是神经元之间相互连接的权重,而且该权重被属于同一特征图的神经元所共享。在实际的网络训练过程中,输入神经元组成的特征图被切割成卷积核大小的小图。每个小图通过卷积核与后层特征图的一个神经元连接。一个特征图上的所有小图和后层特征图中某个神经元的连接使用的是相同的卷积核,也就是同特征图的神经元共享了连接权重。

TensorFlow API构建卷积神经网络

在TensorFlow中,卷积神经网络(Convolutional neural networks,CNNs)主要包含三种类型的组件,主要API如下:

卷积层(Convolutional Layer),构建一个2维卷积层,常用的参数有:

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表示要采用的激活函数。

池化层(Pooling Layer,max_pooling2d或average_pooling2d),用于构建2维池化,常用的参数有:

tf.layers.max_pooling2d(

inputs=conv,

pool_size=[2, 2],

strides=2)

inputs表示要被池化的输入Tensor,pool_size表示池化窗口大小,strides表示进行池化操作的步长。

全链接层(Dense Layer,dense),主要对特性向量执行分类操作。执行全链接操作前,需要对池化后的特性向量,执行展开操作,转换成[batch_size, features]的形式,如下所示:

tf.reshape(pool, [-1, 7 * 7 * 64]),-1表示BatchSize,

全链接层主要参数如下所示:

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)

TensorFlow Cifar10模型

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%。模型特性主要包括:

核心数学组件:卷积操作,RELU激活算子,池化操作,局部响应归一化操作。

可视化展示:展示训练过程的loss值,梯度,以及参数分布情况等。

滑动平均:使用参数的滑动平均值执行评估操作。

预处理队列:通过队列对训练数据进行预处理,用于减少读取数据的延迟,加快数据的预处理。

该模型的代码结构如下:

cifar10_input.py:负责加载训练数据。

cifar10.py:负责构建cifar10模型。

cifar10_train.py:负责在单设备(CPU/GPU)上进行训练。

cifar10_multi_gpu_train.py:负责在多GPU上进行训练。

cifar10_eval.py:负责对模型进行评估。

该模型的Graph结构如下:

(点击放大图像)


下图为Cifar10的多GPU模型架构,每个GPU型号最好相同,具备足够的内存能运行整个Cifar10模型。


(点击放大图像)

该架构会复制Cifar10模型到每个GPU上,每个GPU上训练完一个Batch的数据后,在CPU端对梯度执行同步操作(求均值),更新训练参数,然后把模型参数发送给每个GPU,进行下一个Batch数据的训练。

TensorFlow Inception V3模型

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 pullhttps://github.com/tensorflow/models

对花数据进行训练的代码结构,如下所示:

data/download_and_preprocess_flowers.sh: 下载花的数据,并转换为TFRecord格式。

slim/inception_model.py:inception V3模型。

flowers_train.py:执行单机多GPU模型的训练。

flowers_eval.py:对训练的准确度进行评估。

#进入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,如下所示:

(点击放大图像)


TensorFlow Vgg19模型

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.tensorflow.org

深度学习利器:分布式TensorFlow及实例分析

深度学习利器:TensorFlow使用实战

深度学习利器:TensorFlow系统架构与高性能程序设计

作者简介

武维:博士,现为IBM Spectrum Computing 研发工程师。主要从事大数据,深度学习,云计算等领域的研发工作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容