卷积神经网络的深入理解-基础篇
日志:(红色部分为修改部分)
2022年5月4日:激活函数部分加入饱和与非饱和激活函数的概念
2022年6月19日:卷积理解补充与修改,
之前对MP模型、单层感知器、多层感知器(BP)的基础进行了理解,接下来写一下对卷积神经网络的理解。卷积神经网络(CNN)由卷积(convolution), 激活(activation),池化(pooling)和全连接四种结构组成。下面也从这三部分对CNN进行介绍。
一、卷积(convolution)
1、无padding时
首先我们来看下面这张图
这张图其实就是卷积的过程,中间黄色的那个框就是卷积核,我们能够看出它的大小是3x3,这里将卷积核的大小设为
k
k
k,
k
k
k的值就是3,接着我们看input这部分,这个式子其实就包含了卷积结果,它是核位置权重与原图对应位置相乘然后相加的结果0,output中是绿色的部分,那么output中其它部分的数字是哪里来的呢?,这里就不得不提到卷积核的步长了,顾名思义,卷积核的步长就是卷积核的移动步数,如下图所示,卷积核的步长为1时的动图:
聪明的小伙伴可能就已经看出,在已知原图大小
W
∗
W
W*W
W∗W,经过卷积核大小k和步长s时,可以得到经过卷积后的特征图的大小,比如第一幅图,原图大小是5x5,卷积核大小是3x3,卷积核的步长为1,在从左到右的方向上,卷积核可以移动三次,同理在从上到下的方向上,卷积核移动也是三次。由此得到卷积后的特征层大小为3x3。下面给出计算公式:
L
=
⌊
W
−
k
s
+
1
⌋
,
⌊
⌋
是
向
下
取
整
L=\left \lfloor \frac{W-k}{s}+1 \right \rfloor,\left \lfloor \right \rfloor是向下取整
L=⌊sW−k+1⌋,⌊⌋是向下取整
2、有padding时
如上图所示,padding其实是原图周围的白色空格,相当于在原图中添加了像素值为0的像素(不一定非得是0),使得原图的大小扩大变为
(
w
+
2
∗
p
a
d
d
i
n
g
)
x
(
w
+
2
∗
p
a
d
d
i
n
g
)
(w+2*padding)x(w+2*padding)
(w+2∗padding)x(w+2∗padding),公式如下:
L
=
⌊
W
−
k
+
2
∗
p
a
d
d
i
n
g
s
⌋
+
1
,
,
⌊
⌋
是
向
下
取
整
L=\left \lfloor \frac{W-k+2*padding}{s} \right \rfloor+1,,\left \lfloor \right \rfloor是向下取整
L=⌊sW−k+2∗padding⌋+1,,⌊⌋是向下取整
那么为什么要加入padding呢?
1、为了不丢弃原图信息。
2、为了保持特征图的大小与原图一致。
3、为了让更深层的layer的input依旧保持有足够大的信息量。
4、为了实现上述目的,且不做多余的事情,padding出来的pixel的值都是0,不存在噪音问题。
5、为了保持边缘信息。
3、卷积与全连接
如之前的多层感知器(BP),如下三层感知器,每个神经元都与上层所有的神经元相连接,这就是全连接。
那么为什么要用卷积呢?下面我们来看一组图(来自于阿里公开课)
左边是全连接,右边是卷积,假设图片大小为1000x1000,隐藏层含有1000x1000个神经元。那么我们来看一下他们的参数量:
全连接:首先一个神经元与图片中的每个像素点相连,每条连接都会有一个对应的权重。每个神经元一共会有1000x1000个参数权重,一共有1000x1000个神经元,
所以说参数量大小是1000x1000x1000x1000=10的12次方个参数,可以说是很大了。
卷积:这里假设隐藏层每个神经元只和输入10x10的局部patch相连接,且卷积核移动的步长为10
这样其实每个神经元对应权重的参数量其实为10x10=100,一共有1000x1000个神经元,因此,总的参数量大小为:1000x1000x10x10=10的8次方,与全连接的参数量整整差了4个数量级。
卷积能够通过权值共享将图片各个部分联系起来,这就使得各个部分相互关联,而全连接各个部分是相互独立的。
在这里给大家带来一个链接,上面有很多图能够帮助大家深入的理解卷积神经网络的卷积过程。如下:卷积大全
通道数(一般一张彩色图像为三通道RGB)
接着我们考虑一种实际的情况,这个情况就是通道数,开始时我并不能理解这个通道数,就比如上面图中输入图像的通道数为3,在经历卷积后,通道数为什么变成了64通道呢?之后慢慢明白,其实这个通道数就相当于卷积核的个数即输出通道数=卷积核个数,如上图是64个2x2大小的卷积核在原图上进行卷积操作,每个卷积核的权重并不一样且卷积过后得到的卷积层通道数为1,卷积过后将得到的通道数叠在一起就得到了卷积层的通道数。
这里进行一下补充,卷积核的通道数等于输入通道数
下面再给出我做的一张图方便大家理解
这里进行一下修改补充,图中黄色是卷积核的数量,输入通道数是3,显然每个卷积核通道数必须为3,因此这里将卷积核的3个通道看作是一样的。
二、激活(激活函数)(持续补充)
相信大家对激活函数都有一定了解,像Sigmoid函数,tanh函数,Relu函数等等,卷积操作为线性操作,这就导致一个问题,那就是模型的表达能力不够,而激活函数是非线性函数,它可以为计算加入非线性因素以增加非线性表达的能力,主要分成饱和激活函数和非饱和激活函数两类。
1、饱和与非饱和
这段栽自这篇文章 饱和和非饱和激活函数
右饱和:
当x趋向于正无穷时,函数的导数趋近于0,此时称为右饱和。
左饱和:
当x趋向于负无穷时,函数的导数趋近于0,此时称为左饱和。
饱和函数和非饱和函数:
当一个函数既满足右饱和,又满足左饱和,则称为饱和函数,否则称为非饱和函数。
2、饱和函数
(1)Sigmoid激活函数
![](https://i-blog.csdnimg.cn/blog_migrate/bc09dd819ad6951db530456eb654aa87.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5fca8746e9a5112ac0904d01336a003d.png)
优点:
输出到0~1,映射平滑适合预测概率。
缺点:
函数不过0点,没有负值激活,影响梯度下降效率;
饱和区梯度消失问题。
(2)tanh激活函数
![](https://i-blog.csdnimg.cn/blog_migrate/cd8beac9147e393e0cfb05b5abfd3e3b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fc9fb732b6548457cee917eb03c725fc.png)
优点:
映射到(-1,1)之间,过0点,值域比sigmoid更大。
缺点:
饱和区梯度消失问题。
3、非饱和激活函数
(1)ReLU
![](https://i-blog.csdnimg.cn/blog_migrate/4917f9e7563625357172b581c6e0c805.png)
![](https://i-blog.csdnimg.cn/blog_migrate/851788111ed9f3189077bf8a6965340e.png)
优点:
计算简单,导数恒定;
具有稀疏性,符合人脑的神经元活跃特性。
缺点:
非0中心化,没有负的激活值,影响梯度下降效率;
若一次不恰当的参数更新后,所有数据不能激活某个神经元,则其对应的参数梯度为0,以后也无法激活,陷入“死亡”。
(2)Leaky ReLU
L
e
a
k
y
R
e
L
U
(
x
)
=
m
a
x
(
x
∗
0.2
,
x
)
Leaky ReLU(x)=max(x*0.2,x)
LeakyReLU(x)=max(x∗0.2,x)
在ReLU优点的基础上解决了ReLu的Dead ReLu的缺点。
根据后面的学习持续更新
网络 | 采用激活函数 | 理由 |
---|---|---|
AlexNet | ReLu | 采用非饱和激活函数加快梯度下降,提高训练速度 |
代码块(做图的代码):
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-10,10)
y1_sigmoid = 1/(1+np.exp(-x))
y2_tanh = (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
y3_ReLU = []
for item in x:
if item<=0:
y3_ReLU.append(0)
else:
y3_ReLU.append(item)
y3_ReLU = np.array(y3_ReLU)
y4_LeakyReLU = []
for item in x:
if item<=0:
y4_LeakyReLU.append(item*0.2)
else:
y4_LeakyReLU.append(item)
y4_LeakyReLU = np.array(y4_LeakyReLU)
fig = plt.figure()
a = fig.add_subplot(221)
a.grid()
a.plot(x,y1_sigmoid)
a.set_title('Sigmoid')
a = fig.add_subplot(222)
a.grid()
a.plot(x,y2_tanh)
a.set_title('tanh')
a = fig.add_subplot(223)
a.grid()
a.plot(x,y3_ReLU)
a.set_title('ReLU')
a = fig.add_subplot(224)
a.grid()
a.plot(x,y4_LeakyReLU)
a.set_title('Leaky ReLU')
plt.show()
三、池化(池化层)
经过卷积操作后,卷积层的参数量依旧很多,因此通过池化将输入的特征图进行压缩,
一方面来说特征图得以变小减少了网络的参数量;
另一方面则将主要特征提取出来。
下图是池化的操作,左边是平均池化,右边是最大池化,经学者经过大量实验证明随着训练轮数的增加发现“平均池化”和“最大池化”最终结果都差不多,所以选择的时候不必纠结池化方式的选择,关于池化给大家推荐一个B站上的视频
同济自豪兄
关于池化也可以根据需要将其融合进卷积操作当中(设置步长),这样可省略池化操作。
四、全连接
全连接,顾名思义就是将经历卷积以后的特征层的特征都连接起来,连接过后就可以进行统计分类了。下面是整个CNN的网络图:
下面是网络层的计算的推导过程:
五、误差反传
关于卷积神经网络的误差反传我并没有做太多深入的了解,不过有一个知乎上推导的链接,有想学习的朋友可以去学一下,链接如下:
卷积神经网络反向传播最清晰的解释?
这就是神经网络的基础操作理解,之后在刨析代码时会进行进一步的补充,如有不对的地方欢迎指正。