文章目录
卷积神经网络原理知识
网上关于卷积神经网络的相关知识数不胜数,本文在学习了前人的博客、知乎等,整理了知识点,便于 自己理解,以及以后的复习。如果侵犯到哪位大神的权利,请联系小编,谢谢。好了下面言归正传。
卷积神经网络,是深度学习算法应用最成功的领域之一,卷积神经网络包括一维卷积神经网络,二维卷积神经网络以及三维卷积神经网络。一维卷积神经网络主要用于序列类的数据处理,二维卷积神经网络常应用于图像类文本的识别,三维卷积神经网络主要应用于医学图像以及视频类数据识别。
1. 卷积基础
1.1 一维卷积
设滤波器序列为
(
w
1
,
w
2
,
⋯
,
w
k
,
⋯
,
w
m
)
\left(w_1,w_2,\cdots,w_k,\cdots,w_m\right)
(w1,w2,⋯,wk,⋯,wm),其与信号序列
(
x
1
,
x
2
,
⋯
,
x
t
,
⋯
)
\left(x_1,x_2,\cdots,x_t,\cdots\right)
(x1,x2,⋯,xt,⋯)的卷积
y
t
=
∑
k
=
1
m
w
k
⋅
x
t
−
k
+
1
y_t=\sum_{k=1}^m w_k\cdot x_{t-k+1}
yt=k=1∑mwk⋅xt−k+1
信号序列
x
\mathbf{x}
x和滤波器
w
\mathbf{w}
w的卷积
y
=
w
⊗
x
\mathbf{y}=\mathbf{w}\otimes\mathbf{x}
y=w⊗x
1.2 二维卷积
给定二维图像数据
X
∈
R
M
×
N
X\in\mathbb{R}^{M\times N}
X∈RM×N和二维滤波器
W
∈
R
m
×
n
W\in\mathbb{R}^{m\times n}
W∈Rm×n,其卷积为
y
i
j
=
∑
u
=
1
m
∑
v
=
1
n
w
u
v
⋅
x
i
−
u
+
1
,
j
−
v
+
1
y_{ij}=\sum_{u=1}^m\sum_{v=1}^n w_{uv}\cdot x_{i-u+1, j-v+1}
yij=u=1∑mv=1∑nwuv⋅xi−u+1,j−v+1
互相关:给定二维图像数据
X
∈
R
M
×
N
X\in\mathbb{R}^{M\times N}
X∈RM×N和二维卷积核
W
∈
R
m
×
n
W\in\mathbb{R}^{m\times n}
W∈Rm×n,其互相关定义为
y
i
j
=
∑
u
=
1
m
∑
v
=
1
n
w
u
v
⋅
x
i
+
u
−
1
,
j
+
v
−
1
y_{ij}=\sum_{u=1}^m\sum_{v=1}^n w_{uv}\cdot x_{i+u-1,j+v-1}
yij=u=1∑mv=1∑nwuv⋅xi+u−1,j+v−1
图像数据
X
R
M
×
N
X\mathbb{R}^{M\times N}
XRM×N和卷积核
W
∈
R
m
×
n
W\in\mathbb{R}^{m\times n}
W∈Rm×n的互相关
Y
=
W
⊗
X
Y=W\otimes X
Y=W⊗X
其中
Y
∈
R
M
−
m
+
1
,
N
−
n
+
1
Y\in\mathbb{R}^{M-m+1,N-n+1}
Y∈RM−m+1,N−n+1为输出矩阵。
卷积扩展
- 卷积核步长:卷积核在滑动时的间隔;
- 数据零填充:在输入数据各维度的两端进行补零。
卷积的计算:
假设一维卷积的输入向量元素个数为
M
M
M,卷积核元素个数为
m
m
m,卷积核步长为
s
s
s,输入数据两端各
p
p
p个零填充,则卷积输出向量个数为
(
n
−
m
+
2
p
)
/
s
+
1
\left(n-m+2p\right)/s+1
(n−m+2p)/s+1。
-
窄卷积(Narrow Convolution):步长 s = 1 s=1 s=1,两端补零 p = 0 p=0 p=0,卷积输出长度为 M − m + 1 M-m+1 M−m+1
-
宽卷积(Wide Convolution):步长 s = 1 s=1 s=1,两端补零 p = m − 1 p=m-1 p=m−1,卷积后输出长度为 M + m − 1 M+m-1 M+m−1
-
等宽卷积(Equal-Width Convolution):步长 s = 1 s=1 s=1,两端补零 p = ( m − 1 ) / 2 p=\left(m-1\right)/2 p=(m−1)/2,卷积后输出长度为 M M M
1.3 CNN的基本结构
基本结构包括两层:
- 特征提取层,每个神经元的输入与前一层的局部接受域相连,并提取改局部的特征。一旦该局部特征被提取后,它与其他特征的位置关系也随之确定下来(保留图像的空间排列)
- 特征映射层:网格的每个计算层由多个特征映射组成,每个特征映射是一个平面,平面上所有神经元的权值相等。特征映射结构采用影响函数核小的Sigomid函数作为卷积网络的激活函数,使得特征映射具有位移不变性。此外,由于一个映射面上的神经元共享权值,可以减少网络自由参数的个数。
卷积神经网络中的每一个卷积层都紧跟着一个用来求局部平均与二次提取的计算层,这种特有的两次特征提取结构减小了特征分辨率。
CNN主要用来识别位移、缩放及其他形式扭曲不变性的二维图形,该部分功能主要由池化层实现。由于CNN的特征检测层通过训练数据进行学习,所以在使用CNN时,避免了显式的特征抽取,而隐式地从训练数据中进行学习;再者由于同一特征映射面上的神经元权值相同,所以网络可以并行学习,这也是卷积网络相对于神经元彼此相连网络的一大优势。
权值共享降低了网络的复杂性,特别是多维输入向量的图像可以直接输入网络这一特点避免了特征提取和分类过程中数据重建的复杂度。
2. 案例说明
假设给定一张图(可能是字母X或者字母O),通过CNN即可识别出是X还是O,如下图所示。
2.1 图像输入
左边是经典的神经网络,如图读取图像,当图像的尺寸越大时,其连接的参数将变的更多,从而计算量非常大。
我们人类的认知一般是从局部到全局,这是我们的认知模式。而图像中的空间联系也是类似,局部范围内的像素之间联系较为紧密,而距离较远的像素相关性较弱。因为,每个神经元其实没有必要对全局图像进行感知,只需要对局部进行感知,然后在更高层将局部的信息综合起来就得到了全局的信息。这种模式就是卷积神经网络中降低参数目的的重要神器:局部感受野。
2.2 提取特征
如果字母X、O是固定不变的,那么最简单的方式就是图像之间的像素一一对比就行,但在现实生活中,字体都有着各个形态上的变化(例如手写文字识别),例如平移、缩放、旋转、微变形等,如下图。
对于各种形态的图像,都能通过CNN准确地识别出来,这就涉及到应该如何有效地提取特征,作为识别的关键因子。
对于CNN来讲,一小块一小块的进行比较,在两幅图像中大致相同的位置找到一些粗糙的特征进行匹配,相比起传统的整幅图逐一比对的方式,CNN的这种小块匹配方式能够更好的比较两幅图像之间的相似性。见下图。
以字母X为例,提取三个重要特征(两个交叉线,一个对角线),如下图所示。
三个重要的特征如何与原始图匹配计算?从而引出卷积。
2.3 卷积
当给定一张新图的时,CNN并不能准确地知道这些特征到底要匹配原图的哪些部分,所以它会在原图中把每一个可能的位置都进行尝试,相当于把这个特征变成了一个过滤器。这个用来匹配的过程就被称为卷积操作。
下面是卷积的动态图。
以字母图为例,三个特征矩阵(卷积核)与 原始图匹配可以得到
注:也可以不除以总个数
以此类推,对三个特征图像不断地重复着上述过程,通过每一个feature(特征)的卷积操作,会得到一个新的二维数组,称之为feature map。其中的值,越接近1表示对应位置和feature的匹配越完整,越是接近-1,表示对应位置和feature的反面匹配越完整,而值接近0的表示对应位置没有任何匹配或者说没有什么关联。如下图所示:
可以看出,当图像尺寸增大时,其内部的加法、乘法操作的次数会增加的很快,每一个filter的大小和filter的数目呈线形增长。由于有这么多因素的影响,很容易使得计算量变的相当庞大。
2.4 池化(Pooling)
为了有效地减少计算量,CNN使用的另一个有效的工具被称为“池化”。池化就是将输入图像进行缩小,减少像素信息,只保留重要信息。
池化的操作比较简单,通常情况下,池化区域的大小为2*2,然后按一定规则转换成相应的值,例如取这个池化区域的最大值(max-pooling),平均值(mean-pooling)等,以这个值作为结果的像素值。见下图
注意:不为整数时,卷积向下取整,池化向上取整
对所有的特征图执行池化操作,结果如下:
最大池化保留了每一小块内的最大值,也就相当于保留了这一块最佳匹配结果。也就是说,不会具体关注窗口内到底哪个地方匹配了,而只关注是不是有某个地方匹配上了。
通过池化层,图像缩小了,能很大程度减少计算量,降低机器负载。
2.5 激活层
激活层是通过激活函数来执行操作。常用的激活函数有Sigmoid,tanh,relu等,前两Sigmoid/tanh比较常见于全连接层,后者ReLU常用于卷积层。激活函数的作用是用来加入非线形映射,把卷积层输出结果做非线形映射。
通过上图的演示操作,对所有的feature map执行ReLu激活函数操作,结果如下:
2.7 深度神经网络
由原始图,通过卷积、激活、池化组合在一起,就可以得到下图
加大网络的深度,增加更多的层数,就得到深度神经网络,如下图:
2.8 全连接层
全连接层在整个卷积神经网络中起到“分类器”的作用,即通过卷积、激活、池化等深度网络后,再经过全连接层对结果进行识别分类。
加权求和,得到各个结果的预测值,进行分类。
这只是个单层的全连接,也是可以有多个全连接层的。如下:
2.9 卷积全流程
将上述的结果串起来,就形成了一个卷积神经网络。
最后,再回顾总结一下,卷积神经网络主要由两部分组成,一部分是特征提取(卷积、激活函数、池化),另一部分是分类识别(全连接层),下图便是著名的手写文字识别卷积神经网络结构图:
3. 常见的几种卷积神经网络介绍
- 图像分类中的ResNet
- 目标检测领域占统治地位的Faster R-CNN
- 图像分割中的Mask-RCNN、U-Net
4. 总结
CNN作为一个深度学习架构被提出的最初诉求,是降低对图像数据预处理的要求,以及避免复杂的特征工程。
卷积网络在本质上是一种输入到输出的映射,它能够学习大量的输入与输出之间的映射关系,而不需要任何输入和输出之间的精确的数学表达式,只要用已知的模式对卷积网络加以训练,网络就具有输入输出对之间的映射能力。
CNN一个非常重要的特点就是头重脚轻(越往输入权值越小,越往输出权值越多),呈现出一个倒三角的形态,这就很好地避免了BP神经网络中反向传播的时候梯度损失得太快。
5. CNN在MNIST数据集上的分类
# _*_coding:utf-8_*_
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
sess = tf.InteractiveSession()
# 权重和偏置需要创建,我们这里定义好初始化函数以便重复使用
# 这里我们使用截断的正态分布噪声,标准差设为0.1
def weight_varibale(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
# 卷积层,池化层也是重复使用的,因此分别定义函数,以便后面重复使用
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],
padding='SAME')
# 定义输入的: x 特征,y_ 真实的label
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
# 因为需要将1D 的输入向量转为2D的图片结构,而且颜色只有一个通道
# 所以最终尺寸如下,当然-1代表样本数量不固定,不用担心
x_image = tf.reshape(x, [-1, 28, 28, 1])
# 第一个卷积层
W_conv1 = weight_varibale([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
# 第二个卷积层(区别第一个卷积,卷积核变为64,也就是说这一层会提取64个特征)
W_conv2 = weight_varibale([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
# 当经历了两次步长为2*2的最大池化,所以边长已经只有1/4
# 所以图片尺寸由28*28边长7*7
W_fc1 = weight_varibale([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
# 为了防止过拟合,下面使用一个Dropout层
# Dropout层是通过一个placeholder传入 keep_prob比率来控制的
# 在训练时候,我们随机丢掉一部分节点的数据来减轻过拟合
# 在预测时候,我们则保留全部数据来追求最好的预测性能
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 将Dropout层的输出连接一个Softmax层,得到最后的概率输出
W_fc2 = weight_varibale([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
# 定义损失函数cross_entropy,但是优化器使用Adam 并给予学习率1e-4
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.global_variables_initializer().run()
for i in range(20000):
batch = mnist.train.next_batch(50)
if i % 100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x: batch[0], y_: batch[1], keep_prob: 1.0
})
print("step %d, training accuracy %g" % (i, train_accuracy))
train_step.run(feed_dict={x: batch[0], y_: batch[1],
keep_prob: 0.5})
print("test accuracy %g" % accuracy.eval(feed_dict={
x: mnist.test.images,
y_: mnist.test.labels,
keep_prob: 1.0
}))
结果如下:
step 0, training accuracy 0.1
step 100, training accuracy 0.9
step 200, training accuracy 0.86
step 300, training accuracy 0.92
step 400, training accuracy 0.92
step 500, training accuracy 0.94
step 600, training accuracy 0.98
step 700, training accuracy 0.96
......
step 7500, training accuracy 1
step 7600, training accuracy 0.98
... ...
step 19700, training accuracy 1
step 19800, training accuracy 1
step 19900, training accuracy 1
test accuracy 0.9929