【干货】卷积神经网络中的四种基本组件

【导读】当今,卷积神经网络在图像识别等领域取得巨大的成功,那么是什么使其高效而快速呢?本文整理John Olafenwa的一篇博文,主要介绍了卷积神经网络采用的四种基本组件:PoolingDropoutsBatch NormalizationData Augmentation ,分别解释了这些组件在现代CNN中所起的作用。另外,作者使用keras实现这几种组件,构建一个完整的CNN系统,更有助于读者理解这几种组件的作用和实现方法。


Components of convolutional neural networks


最近最先进的网络结构采用了许多附加组件来补充卷积操作。在这篇文章中,我将解释一些能够提高现代卷积神经网络速度和精度的最重要的组件。 我将从解释每个组件的理论开始,并在keras中实现。


Pooling




使CNN非常有效的第一个秘诀就是Pooling。Pooling是一种矢量,对图像的每个局部区域进行标量变换,就像卷积操作一样,然而,与卷积不同的是它们没有过滤器,也不用局部区域计算点积,而是计算区域中像素的平均值(average pooling),或者简单地选取最大像素并丢弃其余部分(Max Pooling)。


以上是2 x 2 pooling,它可以有效地将特征映射的大小减小2倍。


Pooling的想法看起来可能适得其反,因为它会导致信息的丢失,但它在实践中证明是非常有效的,因为它使得covnets(卷积网络)对于图像表示的变化是不变的,并且还减少了背景噪声的影响。 Max Pooling近年来效果最好,其思想是,用某个局部区域的最大像素代表该地区最重要的特征。通常我们想分类的物体的图像可能包含许多其他物体,例如,出现在汽车图像中某处的猫可能会误导分类器。pooling有助于缓解这一现象,使covnets更好地推广。


它也大大降低了covnet的计算成本。通常,网络中每层的图像大小与每层的计算成本(flops)成正比。随着图层变得更深,pooling会减少图像的尺寸,因此,它有助于防止网络需要的flops数量激增。 分段卷积有时用作pooling的替代物。


Dropouts




过度拟合是网络在训练集上运行良好但在测试集上表现不佳的一种现象。这通常是由于过度依赖于训练集中出现的特定的特征。Dropouts是一种抑制过度拟合的技巧。 它可以随机地将一些激活值设置为0,从而避免过度拟合。  通过这样做,网络不得不探索更多分类图像的路径而不是过度依赖于某些特征。 Dropouts是AlexNet中的关键元素之一。

 

Batch Normalization(批量标准化)




神经网络的一个主要问题是梯度消失,造成训练非常糟糕。 来自Google Brain的Ioie和Szegedy发现,这主要是因为内部协变量的变化,这种情况是由于信息通过网络传播而造成的数据分布变化引起的。他们提出批量标准化(Batch Normalization)的技术。它的工作原理是将每一批图像都标准化,从而得到零均值和单位方差。


它通常放在cnns的非线性(relu)之前。 它极大地提高了准确性,同时极大地加快了训练过程。

 

Data Augmentation(数据增强)




现代covnets所需要的最后一种组件是Data Augmentation。人类视觉系统在适应图像平移,旋转和其他形式的扭曲方面非常出色。拍摄图像并且不管如何翻转它,大多数人仍然可以识别它。 然而,covnets不善于处理这种扭曲,它们可能会由于小的改变而失败。 它们解决这个问题的关键是随机扭曲训练图像,使用水平切除,垂直切除,旋转,增白,移位和其他扭曲的手段。这将使covnets学会如何处理这种扭曲,因此,他们将能够在现实世界中很好地工作。


另一种常用技术是从每幅图像中减去平均图像,并除以标准偏差。


对这些基本组件的理论解释让人感到枯燥乏味,现在我将解释如何在keras中实现它们。

 

在这篇文章中,所有的实验都将在CIFAR10上进行,这是一个包含60,000个32×32RGB图像的数据集。 它分为50,000个训练图像和10,000个测试图像。

为了让工程更加模块化,我们为每层创建一个简单的函数


def Unit(x,filters):
out = BatchNormalization()(x)
out = Activation("relu")(out)
out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)

return out


这里是我们代码中最重要的部分,Unit函数定义了一个简单的层,它包含三个层,第一个是我先前解释的Batch Normalization,接下来我们添加RELU activation(激活),最后添加convolution(卷积),注意为何RELU在convolution之前,这是一种最近的做法,称为“预激活”


现在我们将许多unit层合并成一个模型


def MiniModel(input_shape):
images = Input(input_shape)


net = Unit(images,64)
net = Unit(net,64)
net = Unit(net,64)
net = MaxPooling2D(pool_size=(2,2))(net)

net = Unit(net,128)
net = Unit(net,128)
net = Unit(net,128)
net = MaxPooling2D(pool_size=(2, 2))(net)

net = Unit(net,256)
net = Unit(net,256)
net = Unit(net,256)

net = Dropout(0.5)(net)
net = AveragePooling2D(pool_size=(8,8))(net)
net = Flatten()(net)
net = Dense(units=10,activation="softmax")(net)

model = Model(inputs=images,outputs=net)

return model 


在这里,我们使用API来定义我们的模型,我们从三个单元格开始,每个单元格64个过滤器,然后是Max Pooling层,将我们的32 x 32图像减少到16 x 16。接下来是pooling层的3,128个过滤单元,这使我们的图像变成8 x 8,最后,我们有另外3个256通道的单元。请注意,每次我们将图像尺寸缩小2倍时,我们会将通道数加倍。


我们按照0.5比例设置dropout ,这将随机取消50%的参数,正如我之前解释的那样,它会抑制过度拟合。

 

接下来,我们需要加载cifar10数据集并执行data augmentation(数据增强)。


#load the cifar10 dataset
(train_x, train_y) , (test_x, test_y) = cifar10.load_data()

#normalize the data
train_x = train_x.astype('float32') / 255
test_x = test_x.astype('float32') / 255

#Subtract the mean image from both train and test set
train_x = train_x - train_x.mean()
test_x = test_x - test_x.mean()

#Divide by the standard deviation
train_x = train_x / train_x.std(axis=0)
test_x = test_x / test_x.std(axis=0)


在上面的代码中,加载训练数据和测试数据后,我们从每幅图像中减去平均图像并除以标准偏差,这是一种基本的(data augmentation)数据增加技术,有时,我们可能只减去平均值并跳过标准偏差部分,哪一种更合适就使用哪种。


对于更先进的数据增强,我们的图像加载过程会稍微改变,keras有一个非常有用的数据增强实用程序,它简化了整个过程。

 

下面的代码可以做到这一点

datagen = ImageDataGenerator(rotation_range=10,
                            width_shift_range=5. / 32,
                            height_shift_range=5. / 32,
                            horizontal_flip=True)

# Compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied).
datagen.fit(train_x)


在上面,首先我们指定一个10度的旋转角度,高度和宽度都是5/32的偏移以及水平翻移,所有这些变换都会随机应用于训练集中的图像。 请注意,还有更多转换存在,您可以查看可以为该类别指定的所有参数。 请记住,过度使用数据增强可能是有害的。


接下来,我们必须将标签转换为one-hot编码

#Encode the labels to vectors
train_y = keras.utils.to_categorical(train_y,10)
test_y = keras.utils.to_categorical(test_y,10)


我在之前的教程中已经解释过了,所以我不会在这里再次解释它们。 事实上,构建训练过程的几乎所有其他内容都与我之前的教程完全相同,因此,这里是完整的代码。

#import needed classes
import keras
from keras.datasets import cifar10
from keras.layers import Dense,Conv2D,MaxPooling2D,Flatten,AveragePooling2D,Dropout,
BatchNormalization,Activation
from keras.models import Model,Input
from keras.optimizers import Adam
from keras.callbacks import LearningRateScheduler
from keras.callbacks import ModelCheckpoint
from math import ceil
import os
from keras.preprocessing.image import ImageDataGenerator


def Unit(x,filters):
out = BatchNormalization()(x)
out = Activation("relu")(out)
out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)

return out

#Define the model


def MiniModel(input_shape):
images = Input(input_shape)

net = Unit(images,64)
net = Unit(net,64)
net = Unit(net,64)
net = MaxPooling2D(pool_size=(2,2))(net)

net = Unit(net,128)
net = Unit(net,128)
net = Unit(net,128)
net = MaxPooling2D(pool_size=(2, 2))(net)

net = Unit(net,256)
net = Unit(net,256)
net = Unit(net,256)

net = Dropout(0.25)(net)
net = AveragePooling2D(pool_size=(8,8))(net)
net = Flatten()(net)
net = Dense(units=10,activation="softmax")(net)

model = Model(inputs=images,outputs=net)

return model

#load the cifar10 dataset
(train_x, train_y) , (test_x, test_y) = cifar10.load_data()

#normalize the data
train_x = train_x.astype('float32') / 255
test_x = test_x.astype('float32') / 255

#Subtract the mean image from both train and test set
train_x = train_x - train_x.mean()
test_x = test_x - test_x.mean()

#Divide by the standard deviation
train_x = train_x / train_x.std(axis=0)
test_x = test_x / test_x.std(axis=0)


datagen = ImageDataGenerator(rotation_range=10,
                            width_shift_range=5. / 32,
                            height_shift_range=5. / 32,
                            horizontal_flip=True)

# Compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied).
datagen.fit(train_x)



#Encode the labels to vectors
train_y = keras.utils.to_categorical(train_y,10)
test_y = keras.utils.to_categorical(test_y,10)

#define a common unit


input_shape = (32,32,3)
model = MiniModel(input_shape)

#Print a Summary of the model

model.summary()
#Specify the training components
model.compile(optimizer=Adam(0.001),loss="categorical_crossentropy",metrics=["accuracy"])



epochs = 20
steps_per_epoch = ceil(50000/128)

# Fit the model on the batches generated by datagen.flow().
model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                   validation_data=[test_x,test_y],
                   epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1,
workers=4)


#Evaluate the accuracy of the test dataset
accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128)
model.save("cifar10model.h5"


首先,这里有一些不同之处

input_shape = (32,32,3)
model = MiniModel(input_shape)

#Print a Summary of the model

model.summary()


正如我先前所解释的,cifar 10由32 x 32的RGB图像组成,因此输入形状有3个通道。 这是不言而喻的。


下一行创建一个我们已经删除的模型的实例,并传入输入形状。


最后,最后一行将打印出我们网络的完整摘要,包括参数的数量。

需要解释的最后一部分是

epochs = 20
steps_per_epoch = ceil(50000/128)

# Fit the model on the batches generated by datagen.flow().
model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                   validation_data=[test_x,test_y],
                   epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1,
workers=4)


#Evaluate the accuracy of the test dataset
accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128)
model.save("cifar10model.h5")


首先我们定义要运行的epochs的数量,注意不要与steps_per_epoch混淆。

steps_per_epoch = ceil(50000/128)


50000是总共训练图像的数量,这里我们使用128的批处理大小,这意味着,总共20次epochs,对于个epoch,网络将处理50000/128批次的图像。


接下来是fit函数,这与我在前面的教程中解释的fit函数明显不同。

再看看下面的代码可能会有所帮助。

Fit the model on the batches generated by datagen.flow().
model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                   validation_data=[test_x,test_y],
                   epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1,
workers=4)

由于我们使用数据生成器类来实现数据增强,我们必须使用fit_generator函数,不要直接传入train_x和train_y,而是通过数据生成器中的流函数传递它们,同时我们也指定batch大小,接下来我们规定在这种情况下的验证数据(validation data)是测试数据(test data)。 所有其他事情保持不变。


这种设置在20个epochs后产生82%的准确率。


您可以尝试调整参数和网络,来观察您可以将准确度提高多少。在接下来的教程中,我将解释一些能真正高效地构建cnn体系结构所需的其他技巧。 本教程的目的是向您介绍基本组件。


如果你想深入了解计算机视觉。 从https://john.specpal.science下载我的免费电子书“Introduction to Deep Computer Vision”。


如果您有任何问题,请在下面评论或通过@ johnolafenwa在twitter上与我联系。


参考文献:

https://towardsdatascience.com/components-of-convolutional-neural-networks-6ff66296b456

-END-

专 · 知

人工智能领域主题知识资料查看获取【专知荟萃】人工智能领域26个主题知识资料全集(入门/进阶/论文/综述/视频/专家等)

请PC登录www.zhuanzhi.ai或者点击阅读原文,注册登录专知,获取更多AI知识资料

请扫一扫如下二维码关注我们的公众号,获取人工智能的专业知识!

请加专知小助手微信(Rancho_Fang),加入专知主题人工智能群交流!

点击“阅读原文”,使用专知

展开全文
Top
微信扫码咨询专知VIP会员