以CNN为基础完成一个CIFAR-10图像识别应用。
一、CNN相关理论
CNN(Convolutional Neural Network,卷积神经网络)是DNN(深度神经网络,泛指全连接层)中一个非常重要的并且应用广泛的分支,CNN自从被提出在图像处理领域得到了大量应用。
卷积神经网络按照层级可以分为5层:数据输入层、卷积层、激活层、池化层和全连接层。
1.1 数据输入层
数据输入层主要是对原始图像数据进行预处理。
1.2 卷积层 Conv
卷积层通过卷积计算将样本数据进行降维采样,以获取具有空间关系特征的数据。
1.3 激活层
激活层对数据进行非线性变换处理,目的是对数据维度进行扭曲来获得更多连续的概率密度空间。在CNN中,激活层一般采用的激活函数是ReLU,它具有收敛快、求梯度简单等特点。
1.4 失活层 Dropout
失活层d1 : Dropout极大简化了训练时神经网络的复杂度,加快了神经网络的训练速度; Dropout的主要作用是防止神经网络的过拟合,提高了神经网络的泛化性。
1.5 池化层 Pool
池化层夹在连续的卷积层中间,用于压缩数据的维度以减少过拟合。当采用最大池化策略时,可以采用最大值来代替一个区域的像素特征,这样就相当于忽略了这个区域的其他像素值,大幅度降低了数据采样维度。
1.6 Flatten层
Flatten层将图片矩阵拉平:经过卷积和池化完成特征提取,紧接着是全连接层,需要将图片的三个维度长宽数据压平成一个维度,可以减少参数。
1.7 批量池化层 BatchNormalization
批量池化层 BatchNormalization 进行归一化处理,是一个用于优化训练神经网络的技巧,让数据在训练过程中保持同一分布,在每一个隐藏层进行批量归一化。对于每一个batch,计算该batch的均值与方差,在将线性计算结果送入激活函数之前,先对计算结果进行批量归一化处理,即减均值、除标准差,保证计算结果符合均值为0,方差为1的标准正态分布,然后再将计算结果作为激活函数的输入值进行计算。
1.8 全连接层 DNN
全连接层在所有的神经网络层级之间都有权重连接,最终连接到输出层。在进行模型训练时,神经网络会自动调整层级之间的权重以达到拟合数据的目的。
源码:
# 导入需要的依赖包
import tensorflow as tf
import getConfig
# 初始化一个字典,用于存放配置获取函数返回的配置参数
gConfig = {}
gConfig = getConfig.get_config(config_file='config.ini')
'''
在cnnModel实现上我们采用了tf.keras这个高阶API类,
定义了四层卷积神经网络,输出维度分别是32、64、128和256,
最后在输出层定义了四层全连接神经网络,输出维度分别是256、128、64和10。
在定义卷积神经网络过程中,按照一个卷积神经网络标准的结构进行定义,
使用最大池化(maxpooling)策略进行降维特征提取,使用Dropout防止过拟合。
'''
# 定义cnnModel方法类,在执行器中可以世界实例化一个CNN进行训练
class cnnModel(object):
def __init__(self, rate):
# 初始化Dropout神经元失效的概率
self.rate = rate
def createModel(self):
# 创建Sequential序列对象,通过add添加所需的网络层
model = tf.keras.Sequential()
'''
tf.keras.layers.Conv2D: 使用Conv2D可以创建一个卷积核来对输入数据进行卷积计算,
然后输出结果,其创建的卷积核可以处理二维数据。
'''
# 卷积层conv1: 输出数据维度为64,卷积核维度为3x3,输入数据维度为:[32,32,3]
model.add(tf.keras.layers.Conv2D(64, 3,
kernel_initializer='he_normal',
strides=1,
activation='relu',
padding='same',
input_shape=(32, 32, 3),
name="conv1"))
# 卷积层conv2: 输出数据维度为64,卷积核维度为3x3
model.add(tf.keras.layers.Conv2D(64, 3,
kernel_initializer='he_normal',
strides=1,
activation='relu',
padding='same',
name="conv2"))
# 池化层pool1: 在一个2×2的像素区域内取一个像素最大值作为该区域的像素特征
model.add(tf.keras.layers.MaxPool2D((2, 2),
strides=2,
padding='valid',
name="pool1"))
# 失活层d1 : Dropout极大简化了训练时神经网络的复杂度,加快了神经网络的训练速度;
# Dropout的主要作用是防止神经网络的过拟合,提高了神经网络的泛化性。
model.add(tf.keras.layers.Dropout(rate=self.rate,
name="d1"))
# 批量池化层BatchNormalization
model.add(tf.keras.layers.BatchNormalization())
# 卷积层conv3
model.add(tf.keras.layers.Conv2D(128, 3,
kernel_initializer='he_normal',
strides=1,
activation='relu',
padding='same',
name="conv3"))
# 卷积层conv4
model.add(tf.keras.layers.Conv2D(128, 3,
kernel_initializer='he_normal',
strides=1,
activation='relu',
padding='same',
name="conv4"))
# 池化层pool2: 在一个2×2的像素区域内取一个像素最大值作为该区域的像素特征
model.add(tf.keras.layers.MaxPool2D((2, 2),
strides=2,
padding='valid',
name="pool2"))
# 失活层d2
model.add(tf.keras.layers.Dropout(rate=self.rate,
name="d2"))
# 批量池化层BatchNormalization
model.add(tf.keras.layers.BatchNormalization())
# 卷积层conv5
model.add(tf.keras.layers.Conv2D(256, 3,
kernel_initializer='he_normal',
strides=1,
activation='relu',
padding='same',
name="conv5"))
# 卷积层conv6
model.add(tf.keras.layers.Conv2D(256, 3,
kernel_initializer='he_normal',
strides=1,
activation='relu',
padding='same',
name="conv6"))
# 池化层pool3: 在一个2×2的像素区域内取一个像素最大值作为该区域的像素特征
model.add(tf.keras.layers.MaxPool2D((2, 2),
strides=2,
padding='valid',
name="pool3"))
# 失活层d3
model.add(tf.keras.layers.Dropout(rate=self.rate,
name="d3"))
# 批量池化层BatchNormalization
model.add(tf.keras.layers.BatchNormalization())
# Flatten层将图片矩阵拉平:
# 经过卷积和池化完成特征提取,紧接着是全连接层,需要将图片的三个维度长宽数据压平成一个维度,可以减少参数
model.add(tf.keras.layers.Flatten(name="flatten"))
# Dropout层d4
model.add(tf.keras.layers.Dropout(rate=self.rate,
name="d4"))
# 全连接层
model.add(tf.keras.layers.Dense(128,
activation='relu',
kernel_initializer='he_normal'))
# Dropout层d5
model.add(tf.keras.layers.Dropout(rate=self.rate,
name="d5"))
# 最后的输出层,项目是进行图像10分类,输出的维度是10;
# 使用softmax作为激活函数,softmax是一个在多分类问题上使用的激活函数
model.add(tf.keras.layers.Dense(10,
activation='softmax',
kernel_initializer='he_normal'))
# 设计完神经网络后,需要对网络模型进行编译、生成可以训练的模型;
# 进行编译时,需要定义损失函数loss、优化器optimizer、模型评价标准metrics
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
return model