基于Tensorflow2.0搭建入门级卷积神经网络
前置理论
【引言】:在全连接神经网络中,每个神经元与前后相邻层的每一个神经元都有连接关系。但是,在现实的工业生产中我们所采集的图像数据都是彩色图像,即以三通道的形式存储,在设计模型的时候,模型参数体量也伴随增大。而模型复杂度的提高也会出现过拟合的现象。因此在实际应用时候会先对原始图像进行特征提取再把提取到的特征送给全连接网络,针对这个解决方案引入了卷积的思想。
CNN基本构成 - 卷积层
-
卷积计算:一般会采用一个正方形的卷积核,按指定步长,在输入特征图上滑动,遍历输入特征图中的每个像素点。每一个步长,卷积核会与输入特征图出现重合区域,重合区域对应元素相乘,求和再加上偏置项得到输出特征的一个像素点
-
卷积核:输入特征图的通道数(channel数),决定了当前层卷积核的深度;当前层卷积核的个数,决定了当前层输出特征图的通道数;卷积核每一个元素数值代表了神经网络的权重,同样可以使用梯度下降法更新模型参数
-
全零填充:有两种选择,其一
'SAME'
采用零填充的方式,其目的是保持图像输入输出尺寸一致;其二'VALID'
,其计算公式如下- 若:
padding='same'
则 输出尺寸 = 输入尺寸 步长 输出尺寸 =\frac{输入尺寸}{步长} 输出尺寸=步长输入尺寸 - 若:
padding='valid'
则 输出尺寸 = 输入尺寸 − 核长 + 1 步长 输出尺寸 =\frac{输入尺寸-核长+1}{步长} 输出尺寸=步长输入尺寸−核长+1
- 若:
-
用Tensorflow2.0描述卷积层,代码如下
tf.keras.layers.Conv2D( filters=${卷积核个数}, kernel_size=${卷积核尺寸}, #正方形写核长证书,反之写(核高h,核宽w) strides=${滑动步长},#默认为1 padding=${'SAME'/'VALID'}, activation=${"relu/sigmoid/tanh/softmax"} #激活函数,若有BN此处不写, input_shape=${(高,宽,通道数)} )
CNN基本构成 - 批标准化
-
在学习批标准化之前,我们需要了解标准化的概念,在我们现有的知识中(正态分布标准化)也有类似的概念,其目的是将数据转化符合0均值,1为标准差的分布;批标准化,对一小批数据(batch),做标准化处理
- 批标准化后,第
k
k
k 个卷积核的输出特征图中第
i
i
i 个像素点
H i ′ k = H i k − μ b a t c h k σ b a t c h k \quad H_{i}^{\prime k}=\frac{H_{i}^{k}-\mu_{batch}^{k}}{\sigma_{batch}^{k}} Hi′k=σbatchkHik−μbatchk - 其中 :
- H i k H_{i}^{k} Hik:表示批标准前,第 k k k 个卷积核,输出特征图中第 i i i 个像素点
- μ b a t c h k \mu_{batch}^{k} μbatchk:表示批标准前,第 k k k 个卷积核,batch张输出特征图中所有像素点平均值
- σ b a t c h k \sigma_{batch}^{k} σbatchk:表示批标准前,第 k k k 个卷积核,batch张输出特征图中所有像素点标准差
- 批标准化后,第
k
k
k 个卷积核的输出特征图中第
i
i
i 个像素点
-
批标准化的目的:使得标准化的数据在激活函数的近似线性区间内
批标准化位于卷积层之后,激活层之前
-
用Tensorflow2.0描述,批标准化:
tf.keras.layers.BatchNormalization()
CNN基本构成 - 池化
- 池化目的:用于减少数据特征量(通常情况下最大池化可提取图片纹理,均值池化可保留背景特征)
- 用Tensorflow2.0描述池化层:
# 最大池化 tf.keras.layers.MaxPool2D( pool_size=${池化核尺寸},#正方形写核长整数 或 (核高h,核宽w) strides=${池化步长},#步长整数 或 (核高h,核宽w),默认为pool_size padding=${'valid'/'same'}# 使用全零填充是"same",不使用"valid"(默认) ) # 均值池化 tf.keras.layers.AveragePooling2D( pool_size=${池化核尺寸},#正方形写核长整数 或 (核高h,核宽w) strides=${池化步长},#步长整数 或 (核高h,核宽w),默认为pool_size padding=${'valid'/'same'}# 使用全零填充是"same",不使用"valid"(默认) )
CNN基本构成 - 舍弃
- 在神经网络训练时,将一部分神经网络按照一定概率从神经网络暂时舍弃。神经网络使用时,被舍弃的神经元回复链接,其目的是在训练的过程中降低模型的复杂度,减小模型过拟合的可能性
- 用Tensorflow2.0描述dropout模块
tf.keras.layers.Dropout(${舍弃的概率})
实战搭建CNN
借助卷积核提取特征后 (卷积本质上就是特征提取器),送入全连接网络
【例】:基于卷积神经网络的基本理论作为理论依据,以及Tensorflow2.0为实现框架,复现一个卷积神经网络,要求:
(1)以CIFAR10为数据集设计一个卷积神经网络;
(2)训练该模型,并将模型保存;
(3)记录模型训练过程中精度/损失的变化,并将其可视化
-
根据需求导入第三方库
## 导入第三方库 import os import tensorflow as tf from tensorflow.keras import Model from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten,Dense import numpy as np import matplotlib.pyplot as plt np.set_printoptions(threshold=np.inf)
-
加载CIFAR10数据集,加载数据集之后需要分别把训练集和测试集的输入特征进行归一化
## 加载Cifar10数据集 cifar10 = tf.keras.datasets.cifar10 (x_train,y_train),(x_test,y_test) = cifar10.load_data() x_train, x_test = x_train/255., x_test/255.
-
设计基于CIFAR10数据集的模型,同时将模型以类的形式封装起来 (八股:C-卷积,B-批归一化,A-激活函数,P-池化,D-全连接层)
## 将神经网络模型封装成类 class CNNCifar10(Model): def __init__(self): super(CNNCifar10,self).__init__() ## 卷积层 self.c1 = Conv2D(filters=6,kernel_size=(5,5),padding='same') self.b1 = BatchNormalization() self.a1 = Activation('relu') self.p1 = MaxPool2D(pool_size=(2,2),strides=2,padding='same') self.d1 = Dropout(0.2) ## 全连接层 self.flatten = Flatten() self.f1 = Dense(128,activation='relu') self.d2 = Dropout(0.2) self.f2 = Dense(10,activation='softmax') ## 前向传播 def call(self, x): x = self.c1(x) x = self.b1(x) x = self.a1(x) x = self.p1(x) x = self.d1(x) x = self.flatten(x) x = self.f1(x) x = self.d2(x) y = self.f2(x) return y
-
初始化模型,并加载训练模型的基本配置(如:优化器/损失函数)
## 初始化模型 model = CNNCifar10() ## 加载模型训练的相关配置 model.compile( optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics=['sparse_categorical_accuracy'] )
-
设置训练模型的过程中,保存模型的路径
## 保存模型 checkpoint_save_path = './checkpoint/cifar10CNN.ckpt' if os.path.exists(checkpoint_save_path + '.index'): print('--------------load the model ----------------') model.load_weights(checkpoint_save_path) ## 追溯需要保存的模型 cp_callback = tf.keras.callbacks.ModelCheckpoint( filepath=checkpoint_save_path, save_weights_only=True, save_best_only=True )
-
训练模型,并将训练好的模型参数记录在
.txt
文件中## 训练模型,并记录模型精度/损失 history = model.fit(x_train,y_train, batch_size=32,epochs=5, validation_data=(x_test,y_test), validation_freq=1, callbacks=[cp_callback]) model.summary() ## 将模型参数写入.txt文件 file = open('./model/weights.txt','w') for v in model.trainable_variables: file.write(str(v.name)+'\n') file.write(str(v.shape)+'\n') file.write(str(v.numpy())+'\n') file.close()
-
追溯模型在训练过程中精度/损失的变化情况,并用绘图工具将其可视化
acc = history.history['sparse_categorical_accuracy'] val_acc = history.history['val_sparse_categorical_accuracy'] loss = history.history['loss'] val_loss = history.history['val_loss'] ## 绘图 plt.subplot(1,2,1) plt.plot(acc,label='Training Accurancy') plt.plot(val_acc,label='Validation Accurancy') plt.title('Training and Validation Accuracy') plt.legend() plt.subplot(1,2,2) plt.plot(loss,label='Training Loss') plt.plot(val_loss,label='Validation Loss') plt.title('Training and Validation Loss') plt.legend() plt.show()