课程地址:【北京大学】Tensorflow2.0_哔哩哔哩_bilibili
Python3.7和TensorFlow2.1
六讲:
神经网络计算:神经网络的计算过程,搭建第一个神经网络模型
神经网络优化:神经网络的优化方法,掌握学习率、激活函数、损失函数和正则化的使用,用Python语言写出SGD、Momentum、Adagrad、RMSProp、Adam五种反向传播优化器
神经网络八股:神经网络搭建八股,六步法写出手写数字识别训练模型
网络八股扩展:神经网络八股扩展,增加自制数据集、数据增强、断点续训、参数提取和acc/loss可视化,实现给图识物的应用程序
卷积神经网络:用基础CNN、LeNet、AlexNet、VGGNet、InceptionNet和ResNet实现图像识别
循环神经网络:用基础RNN、LSTM、GRU实现股票预测
前两讲:使用六步法搭建了全连接网络,训练了MNIST数据集和FASHION数据集,实现了图像识别应用
本讲:讲解卷积神经网络,利用基础CNN、LeNet、AlexNet、VGGNet、InceptionNet和ResNet实现图像识别
全连接网络回顾
全连接网络中的每一个神经元小球与前后相邻层的每一个神经元都有连接关系,输入是特征,输出是预测结果。实践证明,全连接网络对识别和预测都有非常好的效果
上一讲中输入全连接网络的是一幅28行28列的784个像素点的灰度值图片,仅两层全连接网络就有十万多个待训练参数
![](https://i-blog.csdnimg.cn/blog_migrate/ebe1793860491d82cd0cb46698940691.png)
全连接网络的参数量
在实际项目中,输入神经网络的是具有更高分辨率的彩色图片,使得送入全连接网络的输入特征数过多。随着隐藏层层数增加,网络规模过大,待优化参数过多,很容易使模型过拟合
![](https://i-blog.csdnimg.cn/blog_migrate/10e8b7bc243542e46296974c38e1d506.png)
卷积神经网络
卷积计算过程
为了减少待训练参数,在实际应用时,会先对原始图片进行特征提取,把提取出来的特征送给全连接网络,让全连接网络输出识别结果。比如一幅彩色图片进入神经网络时,会先对它进行若干层特征提取,把提取到的特征送入全连接网络进行识别
![](https://i-blog.csdnimg.cn/blog_migrate/fa328a801d053f3b20e4c9a6081106d2.png)
全连接网络的改进
卷积计算是一种有效提取图像特征的方法。一般会用一个正方形的卷积核,按指定步长,在输入特征图上滑动,遍历这张输入特征图里的每个像素点,每滑动一个步长,卷积核会与输入特征图部分像素点重合,重合区域对应元素相乘求和,再加上偏置项,得到输出特征图的一个像素点
如果输入特征是单通道灰度图,使用深度为1的单通道卷积核
如果输入特征是三通道彩色图,使用一个3*3*3或5*5*3的卷积核,总之要使卷积核的通道数与输入特征图的通道数一致。因为要想让卷积核与输入特征图对应点匹配上,必须让卷积核的深度与输入特征图的深度一致,所以输入特征图的深度(channel数)决定了当前层卷积核的深度
由于每个卷积核在卷积计算后,会得到一张输出特征图,所以当前层使用了几个卷积核,就有几张输出特征图,即当前层卷积核的个数决定了当前层输出特征图的深度
如果觉得某层模型的特征提取能力不足,可以在这一层多用几个卷积核,提高这一层的特征提取能力
![](https://i-blog.csdnimg.cn/blog_migrate/42deb5c77d020d0cbcc580af5f0fe72d.png)
里面的每一个小颗粒都存储着一个待训练参数。在执行卷积计算时,卷积核里的这些参数是固定的,每次反向传播时,这些小颗粒中存储的待训练参数会被梯度下降法更新。卷积就是利用立体卷积核,实现了参数的空间共享
对于输入特征图是单通道的,选择单通道卷积核:
![](https://i-blog.csdnimg.cn/blog_migrate/a03ef45db4e9240e2a5a884588bbc94e.png)
对于输入特征图是三通道的,选择三通道卷积核:
![](https://i-blog.csdnimg.cn/blog_migrate/8ce293b9982f6a0e5d94bae2edb25668.png)
每滑动一步,输入特征图与卷积核里的27个元素重合,它们对应元素相乘求和再加上偏置项b,得到输出特征图中的一个像素值
用多个卷积核可实现对同一层输入特征的多次特征提取,卷积核的个数决定输出层的通道数(channels),即输出特征图的深度
![](https://i-blog.csdnimg.cn/blog_migrate/e7fb70c91abcc6904372e0b1c8187b17.gif)
卷积核在输入特征图上按指定步长滑动,每个步长,卷积核会与输入特征图上部分像素点重合,重合区域输入特征图与卷积核对应元素相乘求和,得到输出特征图中的一个像素点。当输入特征图被遍历完成,得到一张输出特征图,完成了一个卷积核的卷积计算过程;当有n个卷积核时,会有n张输出特征图,叠加在一起
Convolutional Neural Networks - Basics
感受野(Receptive Field)
输出特征图中一个像素点映射到原始输入图片的区域大小
![](https://i-blog.csdnimg.cn/blog_migrate/b329e92de76ca451ff9b57afb5644820.png)
同样一个5*5的原始输入图片,经过两层3*3卷积核作用,和经过一层5*5卷积核作用一样,都得到一个感受野是5的输出特征图
两层3*3卷积核和一层5*5卷积核的特征提取能力是一样的,选择哪种好? —— 要考虑它们所承载的待训练参数量和计算量
![](https://i-blog.csdnimg.cn/blog_migrate/9387eead1601bc202cdce146204db771.png)
图片大小是x*x,卷积核为3*3,每计算出一个像素点需要9次乘加计算(卷积核大小为9)
x*x经过第一次卷积计算后得到的图片大小为(x-2)*(x-2),故第一次卷积的乘加计算个数为(x-2)*(x-2)*9
同理,(x-2)*(x-2)经过第二次卷积计算得到的图片大小为(x-4)*(x-4),故第二次卷积的乘加计算个数为(x-4)*(x-4)*9
所以计算量为(x-2)*(x-2)*9+(x-4)*(x-4)*9
图片大小是x*x,卷积核为5*5,每计算出一个像素点需要25次乘加计算(卷积核大小为25)
x*x经过第一次卷积计算后得到的图片大小为(x-4)*(x-4),故第一次卷积的乘加计算个数为(x-4)*(x-4)*25
所以计算量为(x-4)*(x-4)*25
![](https://i-blog.csdnimg.cn/blog_migrate/57c9d0fc691f53d5ed80070af434f533.png)
当采用尺寸不同的卷积核时,最大的区别就是感受野的大小不同,所以经常会 采用多层小卷积核来替换一层大卷积核,在保持感受野相同的情况下减少参数量和计算量,例如十分常见的 用2层3*3卷积核来替换1层5*5卷积核的方法
全零填充(Padding)
希望卷积计算保持输入特征图尺寸不变,可以使用全零填充,在输入特征图周围填充0
5*5*1的输入特征图经过全零填充后,再通过3*3*1的卷积核,进行步长为1的卷积计算,输出特征图仍是5*5*1,如图:
![](https://i-blog.csdnimg.cn/blog_migrate/29f62561b7b4a80b5de0667a5dffda9f.png)
全零填充的输出特征尺寸计算
卷积输出特征图维度的计算公式:
![](https://i-blog.csdnimg.cn/blog_migrate/80ccf797bdec42255d711b737ee8f39a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5cd3e0734af3f3c095da30b5cbe832e9.png)
对于5*5*1的图像来说,当padding='SAME'时,输出图像边长为5;当padding='VALID'时,输出图像边长为3
TensorFlow2描述卷积计算层
在TensorFlow框架下利用Keras来构建CNN中的卷积层,使用的是tf.keras.layers.Conv2D函数
tf.keras.layers.Conv2D (
input_shape = (高, 宽, 通道数) # 输入特征图维度,仅在第一层有,可省略
filters = 卷积核个数, # 整数
kernel_size = 卷积核尺寸, # 正方形写核长整数,或(核高h,核宽w)
strides = 滑动步长, # 横纵向相同写步长整数(通常是相同的),或(纵向步长h,横向步长w),默认1
padding = “same” or “valid”, # 使用全零填充是“same”,不使用是“valid”(默认)
activation = “ relu ” or “ sigmoid ” or “ tanh ” or “ softmax”等 , # 如果这一层卷积后还有批标准化BN操作,不在这里进行激活,不写激活函数
)
在利用TensorFlow框架构建卷积网络时,一般会利用Batch Normalization函数构建BN层进行批标准化,所以在Conv2D函数中经常不写激活函数
# 采用6个5*5的卷积核,不使用全零填充,激活函数用sigmoid
# 写法1
Conv2D(6, 5, padding='valid', activation='sigmoid')
# 写法2
Conv2D(6, (5,5), padding='valid', activation='sigmoid')
# 写法3(推荐关键字传递参数的方法,代码可读性强)
Conv2D(filters=6, kernel_size=(5,5), padding='valid', activation='sigmoid')
卷积神经网络中的操作
卷积神经网络就是借助卷积核对输入特征进行特征提取,再把提取到的特征送入全连接网络进行识别预测
提取特征包括卷积、批标准化、激活、池化四步
![](https://i-blog.csdnimg.cn/blog_migrate/12563163fdd7b3d5060351e47e773537.png)
批标准化(Batch Normalization,BN)
神经网络对0附近的数据更敏感,但是随着网络层数的增加,特征数据会出现偏离0均值的情况。标准化可以使数据符合以0为均值,1为标准差的标准正态分布,把偏移的特征数据重新拉回到0附近
批标准化,是对一个batch的数据在网络各层的输出做标准化处理,使数据回归标准正态分布,常用在卷积操作和激活操作之间(BN层位于卷积层之后,激活层之前)
![](https://i-blog.csdnimg.cn/blog_migrate/a5662970be1e3f34821889854ad9e838.png)
可以通过下面这个式子计算批标准化后的输出特征图:
![](https://i-blog.csdnimg.cn/blog_migrate/e4eec5f3fd37623ed4d400dafa1aa547.png)
批标准化操作,会让每个像素点进行减均值除以标准差的自更新计算。注意,是对第k个卷积核产生的batch张输出特征图集体求均值和标准差
BN操作将原本偏移的特征数据重新拉回到0均值,使进入激活函数的数据分布在激活函数的线性区,使得输入数据的微小变化更明显地体现到激活函数的输出,提升了激活函数对输入数据的区分力
![](https://i-blog.csdnimg.cn/blog_migrate/77fd50b4183dcd3c1b13edfe224e43f0.png)
BN将神经网络每层的输入都调整到均值为0,方差为1的标准正态分布,其目的是解决神经网络中的梯度消失问题
但是这种简单的特征数据标准化使特征数据完全满足标准正态分布,集中在激活函数中心的线性区域,使激活函数丧失了非线性特性。BN操作的另一个重要步骤是缩放和偏移,在BN操作中为每个卷积核引入两个可训练参数:缩放因子γ和偏移因子β,反向传播时,两个因子会与其他待训练参数一同被训练优化,使标准正态分布后的特征数据通过缩放因子和偏移因子优化了特征数据分布的宽窄和偏移量,保证了网络的非线性表达力
![](https://i-blog.csdnimg.cn/blog_migrate/2a396db4b6c745bbeb34663fe60ecb19.png)
TensorFlow提供了BN操作的函数:tf.keras.layers.BatchNormalization()
![](https://i-blog.csdnimg.cn/blog_migrate/46b038068f0e66d3e9299ff06b6707a1.png)
池化(Pooling)
池化操作用于减少卷积神经网络中特征数量(降维),主要方法有最大池化和均值池化
最大池化(MaxPool2D):可以提取图片纹理
均值池化(AveragePooling2D):可以保留背景特征
![](https://i-blog.csdnimg.cn/blog_migrate/c1e3aa0e09005c7d9bfa8288493ac675.png)
TensorFlow给出了池化函数:tf.keras.layers.MaxPool2D或tf.keras.layers.AveragePooling2D。以MaxPool2D为例说明参数,AveragePooling2D同理:
tf.keras.layers.MaxPool2D(
pool_size=池化核尺寸, #正方形写核长整数,或(核高h,核宽w),一般池化核的高和宽是一样的
strides=池化步长, #步长整数, 或(纵向步长h,横向步长w),默认为pool_size
padding=‘valid’or‘same’ #使用全零填充是“same”,不使用是“valid”(默认)
)
# 写法1
MaxPool2D(2, 2)
# 写法2
MaxPool2D(2, (2,2))
# 写法3(推荐关键字传递参数的方法,代码可读性强)
MaxPool2D(strides=2, pool_size=(2,2))
舍弃(Dropout)
为了缓解神经网络过拟合,在神经网络训练过程中,常把隐藏层的部分神经元按照一定比例从神经网络中临时舍弃;在使用神经网络时,再把所有神经元恢复到神经网络中
![](https://i-blog.csdnimg.cn/blog_migrate/c5682d4364a832ab17416ecb01589e6a.png)
TensorFlow提供了Dropout的函数:tf.keras.layers.Dropout(舍弃的概率)
总结
利用上述知识,就可以构建出基本的卷积神经网络CNN了,其核心思路为:在CNN中利用卷积核(kernel)提取特征后,送入全连接网络
CNN模型的主要模块:一般包括卷积层、BN层、激活函数、池化层以及全连接层
model = tf.keras.models.Sequential([
Conv2D(filters=6, kernel_size=(5, 5), padding='same'), # 卷积层
BatchNormalization(), # BN层
Activation('relu'), # 激活层
MaxPool2D(pool_size=(2, 2), strides=2, padding='same'), # 池化层
Dropout(0.2), # dropout层 随机舍弃掉20%的神经元
])
卷积就是特征提取器,CBAPD
C:卷积层 Conv2D()
B:BN层 BatchNormalization()
A:激活层 Activation()
P:池化层 MaxPool2D() 或 AveragePooling2D()
D:dropout层 Dropout()
卷积神经网络搭建示例
构建神经网络的八股套路:
import引入TensorFlow、Keras、numpy等所需模块
读取数据集
像MNIST、CIFAR10等基础数据集可以直接从sklearn等模块中引入
实际应用中,大多需要从图片和标签文件中读取所需数据集
搭建所需的网络结构
当网络结构比较简单时,可以利用Keras模块中的tf.keras.models.Sequential来搭建顺序网络模型(更方便)
当网络不再是简单的顺序结构,而是有其它特殊结构出现(如ResNet中的跳连结构),便需要利用class定义自己的网络结构(实际应用中往往使用这种)
对搭建好的网络进行编译(compile)。通常在这一步指定所采用的优化器(如Adam、sgd、RMSProp等)、损失函数(如交叉熵函数、均方差函数等)。注意,选择哪种优化器和损失函数往往对训练的速度和效果有很大的影响
将数据输入编译好的网络来进行训练(model.fit)。在这一步中指定训练轮数epochs、batch_size等信息。由于神经网络的参数量和计算量一般都比较大,训练所需的时间也比较长,尤其是在硬件条件受限的情况下,所以在这一步中通常会加入断点续训、模型参数保存等功能,使训练更加方便,同时防止程序意外停止导致数据丢失的情况发生
将神经网络模型的具体信息打印出来(model.summary),包括网络结构、网络各层的参数等,便于对网络进行浏览和检查
代码实战见链接:
TensorFlow2基于CIFAR10数据集实现五个经典卷积网络LeNet/AlexNet/VGGNet/InceptionNet/ResNet - 知乎