前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >教程 | 从基本概念到实现,全卷积网络实现更简洁的图像识别

教程 | 从基本概念到实现,全卷积网络实现更简洁的图像识别

作者头像
机器之心
发布2018-05-07 10:29:28
9310
发布2018-05-07 10:29:28
举报
文章被收录于专栏:机器之心机器之心

选自 Medium

机器之心编译

参与:蒋思源、晏奇、黄小天

众所周知,图像就是像素值的集合,而这个观点可以帮助计算机科学家和研究者们构建一个和人类大脑相似并能实现特殊功能的神经网络。有时候,这种神经网络甚至能超过人类的准准度。

上图是一个非常好的案例,其说明了图像是由像素值表征的这一特征。这些小的像素块形成了最基本的卷积神经网络。

卷积神经网络与一般的神经网络有非常高的相似性,它们都是由可学习的权重和偏置项还有神经元组成。每个神经元接受一些输入,然后执行点积(标量),随后可选择性地执行非线性分类。整个网络仍然表示单可微分(single differentiable)的评估函数(score function),整个网络从一端输入原始图像像素,另一端输出类别的概率。该网络仍然具有损失函数,因为损失函数可以在最后(全连接)层计算相对概率(如支持向量机/Softmax),并且学习常规神经网络的各种开发技巧都能应用到损失函数上。

卷积是如何进行的。每一个像素由周围像素的加权和所替代,神经网络会学习这些权重。

最近,随着数据量和计算力的大大提升,ConvNets 在人脸识别、物体识别、交通标志、机器人和自动驾驶等方向表现得十分出色。

下图显展示了在 ConvNet 中四种主要的操作:

1. 卷积(Convolution)

2. 非线性(如 ReLU)

3. 池化或子采样(Pooling or Sub Sampling)

4. 分类(Classification)

一张汽车的图片经过 ConNet,并在全连接层输出类别为汽车

全卷积网络(All Convolution Network)

大多数用于目标识别的现代卷积神经网络(CNN)都是运用同一原理构建:交替卷积和最大池化层,并伴随着少量全连接层。以前就有一篇论文提出,最大池化(max-pooling)可被一个带有增加步长的卷积层轻易替换,而没有在图像识别基准上出现精确度的损失。论文中提到的另一个趣事也是用一个全局平均池化(Global Average pooling)替换全连接层。

如需详细了解全卷积网络,可查阅论文:https://arxiv.org/abs/1412.6806#

去掉全连接层也许不是一件让人很惊讶的事,因为长久以来人们本来就不怎么使用它。不久前 Yann LeCun 甚至在 Facebook 上说,我从一开始就没用过全连接层。

这不无道理,全连接层与卷积层的唯一区别就是后者的神经元只与输入中的局部域相连,并且卷积空间之中的很多神经元共享参数。然而,全连接层和卷积层中的神经元依然计算点积,它们的函数形式是相同的。因此,结果证明全连接层和卷积层之间的转换是可能的,有时甚至可用卷积层替换全连接层。

正如上文提到的,下一步是从网络中去除空间池化运算。现在这也许会引起一些疑惑。让我们详细看一下这个概念。

空间池化(spatial Pooling),也称为子采样(subsampling)或下采样(downsampling),其减少了每一个特征映射的维度,但是保留了最重要的信息。

让我们以最大池化为例。在这种情况下,我们定义了一个空间窗口(spatial window),并从其中的特征映射获取最大元素,现在记住图 2(卷积是如何工作的)。直观来讲带有更大步长的卷积层可作为子采样和下采样层,从而使输入表征更小更可控。同样它也可减少网络中的参数数量和计算,进而控制过拟合的发生。

为了减少表征尺寸,在卷积层中使用更大步长有时成了很多案例中的最佳选择。在训练好的生成模型,如变分自动编码器(VAE)或生成对抗网络(GAN)中,放弃池化层也是十分重要的。此外,未来的神经网络架构可能会具有非常少的或根本没有池化层。

鉴于所有以上提到的小技巧或微调比较重要,我们在 Github 上发布了使用 Keras 模型实现全卷积神经网络:https://github.com/MateLabs/All-Conv-Keras

导入库(library)和依赖项(dependency)

代码语言:javascript
复制
from __future__ import print_function
import tensorflow as tf
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dropout, Activation, Convolution2D, GlobalAveragePooling2D
from keras.utils import np_utils
from keras.optimizers import SGD
from keras import backend as K
from keras.models import Model
from keras.layers.core import Lambda
from keras.callbacks import ModelCheckpoint
import pandas

在多 GPU 上训练

对于模型的多 GPU 实现,我们有一个可将训练数据分配给可用 GPU 的自定义函数。

计算在 GPU 上完成,输出数据传给 CPU 以完成模型。

代码语言:javascript
复制
def make_parallel(model, gpu_count):
    def get_slice(data, idx, parts):
        shape = tf.shape(data)
        size = tf.concat(0, [ shape[:1] // parts, shape[1:] ])
        stride = tf.concat(0, [ shape[:1] // parts, shape[1:]*0 ])
        start = stride * idx
        return tf.slice(data, start, size)
    outputs_all = []
    for i in range(len(model.outputs)):
        outputs_all.append([])
代码语言:javascript
复制
#Place a copy of the model on each GPU, each getting a slice of the batch
代码语言:javascript
复制
    for i in range(gpu_count):
        with tf.device('/gpu:%d' % i):
            with tf.name_scope('tower_%d' % i) as scope:
            inputs = []
代码语言:javascript
复制
#Slice each input into a piece for processing on this GPU
代码语言:javascript
复制
            for x in model.inputs:
                input_shape = tuple(x.get_shape().as_list())[1:]
                slice_n = Lambda(get_slice, output_shape=input_shape, arguments={'idx':i,'parts':gpu_count})(x)
                inputs.append(slice_n)
            outputs = model(inputs)
代码语言:javascript
复制
            if not isinstance(outputs, list):
                outputs = [outputs]
代码语言:javascript
复制
#Save all the outputs for merging back together later
代码语言:javascript
复制
            for l in range(len(outputs)):
                outputs_all[l].append(outputs[l])
代码语言:javascript
复制
# merge outputs on CPU
代码语言:javascript
复制
with tf.device('/cpu:0'):
    merged = []
    for outputs in outputs_all:
        merged.append(merge(outputs, mode='concat', concat_axis=0))
    return Model(input=model.inputs, output=merged)

配置批量大小(batch size)、类(class)数量以及迭代次数

由于我们用的是拥有 10 个类(不同对象的种类)的 CIFAR 10 数据集,所以类的数量是 10,批量大小(batch size)等于 32。迭代次数由你自己的可用时间和设备计算能力决定。在这个例子中我们迭代 1000 次。

图像尺寸是 32*32,颜色通道 channels=3(rgb)

代码语言:javascript
复制
batch_size = 32
nb_classes = 10
nb_epoch = 1000
rows, cols = 32, 32
channels = 3

把数据集切分成「训练集」、「测试集」和「验证集」三部分

代码语言:javascript
复制
(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')
print (X_train.shape[1:])
代码语言:javascript
复制
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)

构建模型

代码语言:javascript
复制
model = Sequential()
代码语言:javascript
复制
model.add(Convolution2D(96, 3, 3, border_mode = 'same', input_shape=(3, 32, 32)))
model.add(Activation('relu'))
model.add(Convolution2D(96, 3, 3,border_mode='same'))
model.add(Activation('relu'))
代码语言:javascript
复制
#The next layer is the substitute of max pooling, we are taking a strided convolution layer to reduce the dimensionality of the image.
代码语言:javascript
复制
model.add(Convolution2D(96, 3, 3, border_mode='same', subsample = (2,2)))
model.add(Dropout(0.5))
model.add(Convolution2D(192, 3, 3, border_mode = 'same'))
model.add(Activation('relu'))
model.add(Convolution2D(192, 3, 3,border_mode='same'))
model.add(Activation('relu'))
代码语言:javascript
复制
# The next layer is the substitute of max pooling, we are taking a strided convolution layer to reduce the dimensionality of the image.
代码语言:javascript
复制
model.add(Convolution2D(192, 3, 3,border_mode='same', subsample = (2,2)))
model.add(Dropout(0.5))
model.add(Convolution2D(192, 3, 3, border_mode = 'same'))
model.add(Activation('relu'))
model.add(Convolution2D(192, 1, 1,border_mode='valid'))
model.add(Activation('relu'))
model.add(Convolution2D(10, 1, 1, border_mode='valid'))
代码语言:javascript
复制
model.add(GlobalAveragePooling2D())
model.add(Activation('softmax'))
model = make_parallel(model, 4)
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])

打印模型。这会给你一个模型的一览,它非常有助于视觉化模型的维度和参数数量

代码语言:javascript
复制
print (model.summary())

数据扩充

代码语言:javascript
复制
datagen = ImageDataGenerator(
featurewise_center=False,  # set input mean to 0 over the dataset
代码语言:javascript
复制
samplewise_center=False,  # set each sample mean to 0
代码语言:javascript
复制
featurewise_std_normalization=False,  # divide inputs by std of the dataset
代码语言:javascript
复制
samplewise_std_normalization=False,  # divide each input by its std
代码语言:javascript
复制
zca_whitening=False,  # apply ZCA whitening
代码语言:javascript
复制
rotation_range=0,  # randomly rotate images in the range (degrees, 0 to 180)
代码语言:javascript
复制
width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)
代码语言:javascript
复制
height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)
代码语言:javascript
复制
horizontal_flip=False,  # randomly flip images
代码语言:javascript
复制
vertical_flip=False)  # randomly flip images
代码语言:javascript
复制
datagen.fit(X_train)

在你的模型中保存最佳权重并添加检查点

代码语言:javascript
复制
filepath="weights.{epoch:02d}-{val_loss:.2f}.hdf5"
代码语言:javascript
复制
checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, save_weights_only=False, mode='max')
代码语言:javascript
复制
callbacks_list = [checkpoint]
代码语言:javascript
复制
# Fit the model on the batches generated by datagen.flow().
代码语言:javascript
复制
history_callback = model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size), samples_per_epoch=X_train.shape[0], nb_epoch=nb_epoch, validation_data=(X_test, Y_test), callbacks=callbacks_list, verbose=0)

最后,拿到训练过程的日志并保存你的模型

代码语言:javascript
复制
pandas.DataFrame(history_callback.history).to_csv("history.csv")
model.save('keras_allconv.h5')

以上的模型在前 350 次迭代后很容易就实现超过 90% 的精确度。如果你想要增加精确度,那你可以用计算时间为代价,并尝试扩充更大的数据。

原文地址:https://medium.com/@matelabs_ai/how-these-researchers-tried-something-unconventional-to-came-out-with-a-smaller-yet-better-image-544327f30e72#.1a1ve638e

©本文为机器之心编译,转载请联系本公众号获得授权。

✄------------------------------------------------

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-03-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器之心 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像识别
腾讯云图像识别基于深度学习等人工智能技术,提供车辆,物体及场景等检测和识别服务, 已上线产品子功能包含车辆识别,商品识别,宠物识别,文件封识别等,更多功能接口敬请期待。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档