微信公众号:python宝
关注可了解更多的python相关知识。若有问题或建议,请公众号留言;
一、输入层(训练数据)二、卷积层、卷积和池化的padding1、padding的理解2、卷积padding3、池化padding4、filter和kernel介绍5、权值共享三、池化层、最大值池化与均值池化的区别四、激活层五、全连接
CNN必须将4D数组作为输入。因此,输入数据的形状为(batch_size,height,width,depth),其中第一维表示图像的batch大小,其他三个维表示图像的各个属性,即高度,宽度和深度。深度就是色彩通道的数量。例如,RGB图像的深度为3,而灰度图像的深度为1。
输入矩阵格式(四个维度)::样本数、图像高度、图像宽度、图像通道数
卷积是从输入图像中提取特征的第一层,Conv层的目标是提取输入数据的特征。卷积通过使用小方块输入数据学习图像特征来保持像素之间的关系。
Tensorflow中使用tf.nn.conv2d()函数来实现卷积时的输入,其格式如下:
tf.nn.conv2d(input,filter,strides,padding,use_cudnn_on_gpu=None,name=None)
input:指定需要做卷积的输入图像,它要求是一个Tensor,具有[batch,in_height,in_width,in_channels]这样的形状(shape),具体含义是"训练时一个batch的图片数量,图片高度,图片宽度,图片通道数",注意这是一个四维的Tensor,要求类型为float32或者float64.
filter:相当于CNN中的卷积核,它要求是一个Tensor,具有[filter_height,filter_width,in_channels,out_channels]这样的shape,具体含义是"卷积核的高度,卷积核的宽度,图像通道数,滤波器个数",要求类型与参数input相同。有一个地方需要注意,第三维in_channels,就是参数input中的第四维。
strides:卷积时在图像每一维的步长,这是一个一维的向量,长度为4,与输入input对应,一般值为[1,x,x,1],x取步长。
padding:定义元素边框与元素内容之间的空间。string类型的量,只能是"SAME"和“VALID”其中之一,这个值决定了不同的卷积方式。
use_cudnn_on_gpu:bool类型,是否使用cudnn加速,默认是True.
name:指定名字
该函数返回一个Tensor,这个输出就是常说的feature map。
注意:在卷积核函数中,padding参数最容易引起歧义,该参数仅仅决定是否要补0,因此一定要清楚padding设置为SAME的真正意义。在设SAME的情况下,只有在步长为1时生成的feature map才会与输入大小相等。
卷积操作过程其实很简单,也就是将卷积核与数据对应相乘,然后求和。剩下的都是些重复性操作,而整个卷积过程的输出便是这每一小小步产生结果的组合了。(卷积在此其实就是内积,步骤很简单,就是根据多个一定的权重(即卷积核),对一个块的像素进行内积运算,其输出就是提取的特征之一)
多卷积,我们用一个卷积核操作只能得到一部分特征,可能获取不到全部特征,这么一来我们就引入了多核卷积。用每个卷积核来学习不同的特征(每个卷积核学习到不同的权重)来提取原图特征。层数越高,提取到的特征就越全局化。
之前在讨论卷积神经网络的时候,我们是使用filter来做元素乘法运算来完成卷积运算的。目的是为了完成探测垂直边缘这种特征。但这样做会带来两个问题。
1)卷积运算后,输出图片尺寸缩小;
2)越是边缘的像素点,对于输出的影响越小,因为卷积运算在移动的时候到边缘就结束了。中间的像素点有可能会参与多次计算,但是边缘像素点可能只参与一次。所以我们的结果可能会丢失边缘信息。
那么为了解决这个问题,我们引入padding, 什么是padding呢,就是我们认为的扩充图片, 在图片外围补充一些像素点,把这些像素点初始化为0.
padding的用途:
(1)保持边界信息,如果没有加padding的话,输入图片最边缘的像素点信息只会被卷积核操作一次,但是图像中间的像素点会被扫描到很多遍,那么就会在一定程度上降低边界信息的参考程度,但是在加入padding之后,在实际处理过程中就会从新的边界进行操作,就从一定程度上解决了这个问题。
(2)可以利用padding对输入尺寸有差异图片进行补齐,使得输入图片尺寸一致。
(3)卷积神经网络的卷积层加入Padding,可以使得卷积层的输入维度和输出维度一致。
(4)卷积神经网络的池化层加入Padding,一般都是保持边界信息和(1)所述一样。
padding模式:SAME和VALID
SAME:是填充,填充大小,padding的像素值 p = (f-1)/2;
VALID:是不填充,直接计算输出padding的像素值 p=0。
卷积和池化中padding区别
卷积层加入Padding,可以使得卷积层的输入维度和输出维度一致。
池化层加入Padding,一般都是保持边界信息。如果没有加padding的话,输入图片最边缘的像素点信息只会被卷积核操作一次,但是图像中间的像素点会被扫描到很多遍,那么就会在一定程度上降低边界信息的参考程度,但是在加入padding之后,在实际处理过程中就会从新的边界进行操作,就从一定程度上解决了这个问题。。
输入:n x n
卷积核:f x f
padding:p
步长strides:s
W:矩阵宽
H:矩阵高
F:卷积核宽和高
P:padding(需要填充的0的个数)
N:卷积核的个数
S:步长卷积输出大小
width:卷积后输出矩阵的宽,height:卷积后输出矩阵的高
width = (W - F + 2P)/ S + 1
height = (H - F + 2P) / S + 1
import tensorflow as tf
import numpy as np
input = tf.Variable(tf.random_normal([64, 5, 5, 3]))
filter = tf.Variable(tf.random_normal([3, 3, 3, 16]))
op_1 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding="SAME")
# p = (3-1)/2=1, s=1输出n = (5+2-3)/1+1=5 = 5
op_2 = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding="SAME")
# p = (3-1)/2=1, s=2 输出n = (5+2-3)/2+1=3
op_3 = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding="VALID")
# p = 0 ,s=1 ,n = (5+0-3)/1+1=3
op_4 = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding="VALID")
# p = 0 ,s=2 ,n = (5+0-3)/2+1=2
init = tf.global_variables_initializer()
sess = tf.InteractiveSession()
sess.run(init)
print(np.shape(sess.run(op_1))) #(64, 5, 5, 16)
print(np.shape(sess.run(op_2))) #(64, 3, 3, 16)
print(np.shape(sess.run(op_3))) #(64, 3, 3, 16)
print(np.shape(sess.run(op_4))) #(64, 2, 2, 16)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# print(sess.run(filter))
print(type(sess.run(filter))) #
print(np.shape(sess.run(filter))) #(3, 3, 3, 16)
print(np.shape(sess.run(filter)[0])) #(3, 3, 16)
print(np.shape(sess.run(filter)[0][0])) # (3, 16)
print(np.shape(sess.run(filter)[0][0][0])) #(16,)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(input))
print(type(sess.run(input))) #
print(np.shape(sess.run(input))) #(64, 5, 5, 3)
print(np.shape(sess.run(input)[0])) #(5, 5, 3)
print(np.shape(sess.run(input)[0][0])) # (5, 3)
print(np.shape(sess.run(input)[0][0][0])) #(3,)
如果 Padding='SAME',输出尺寸为:W / S
如果 Padding='VALID',输出尺寸为:(W - F + 1) / S
# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np
# 首先,模拟输入一个特征图,大小为5*5
# 输入图像矩阵的shape为[批次大小,图像的高度,图像的宽度,图像的通道数]
input = tf.Variable(tf.constant(1.0, shape=[1, 5, 5, 1]))
# 最大池化操作 strides为[批次大小,高度方向的移动步长,宽度方向的移动步长,通道数]
# ksize为[1, 池化窗口的高,池化窗口的宽度,1]
op1_max_pooling_same = tf.nn.max_pool(input, [1,2,2,1], strides=[1,2,2,1],padding='SAME')
#p = 1, s = 2,f = 2, 输入n=5,输出n = [n/s]=5/2=3
op2_max_pooling_valid = tf.nn.max_pool(input, [1,2,2,1], strides=[1,2,2,1],padding='VALID')
#p = 0, s = 2,f = 2, 输入n=5,输出n = [(n−f+1))/s]=2
op3_avg_pooling_same = tf.nn.avg_pool(input, [1,2,2,1], strides=[1,2,2,1],padding='SAME')
# 全局池化,filter是一个与输入矩阵一样大的过滤器
#p = 1, s = 2, f = 2, 输入n=5,输出n = [n/s]=5/2=3
op4_global_pooling_same = tf.nn.avg_pool(input, [1,5,5,1], strides=[1,5,5,1],padding='SAME')
#p = 1, s = 5, f = 5, 输入n=5,输出n = [n/s]=5/5=1
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
print(np.shape(sess.run(op1_max_pooling_same))) #(1, 3, 3, 1)
print(np.shape(sess.run(op2_max_pooling_valid))) #(1, 2, 2, 1)
print(np.shape(sess.run(op3_avg_pooling_same))) #(1, 3, 3, 1)
print(np.shape(sess.run(op4_global_pooling_same))) #(1, 1, 1, 1)
卷积使用“kernel”从输入图像中提取某些“特征”。kernel是一个矩阵,可在图像上滑动并与输入相乘,从而以某种我们期望的方式增强输出。
一个普通的卷积层实际上由多个这样的filter组成。为了简化下面的讨论,除非另有说明,否则假定仅存在一个filter,因为所有filter都会重复相同的操作。
卷积核kernel:二维的矩阵,kernel是权重矩阵,将权重矩阵与输入相乘以提取相关特征。卷积名称就是kernel矩阵的维数。例如,2D卷积的kernel矩阵就是2D矩阵。
滤波器filter:多个卷积核组成的三维矩阵,多出的一维是通道。filter是多个kernel的串联,每个kernel分配给输入的特定通道。filter总是比kernel大一维。例如,在2D卷积中,filter是3D矩阵(本质上是2D矩阵(即kernel)的串联)。因此,对于具有kernel尺寸h * w和输入通道k的CNN层,filter尺寸为k * h * w。
tf.nn.conv2d()中filter:卷积核(滤波器)filter应该输入的是卷积的参数,filter=[filter_height, filter_width, in_channels, out_channels]
一个“Kernel”更倾向于是2D的权重矩阵。而“filter”则是指多个Kernel堆叠的3D结构。如果是一个2D的filter,那么两者就是一样的。但是一个3Dfilter,在大多数深度学习的卷积中,它是包含kernel的。每个卷积核都是独一无二的,主要在于强调输入通道的不同方面。
keras中,
当channels=1时,那么filter就是kernel
当channels>1时,那么filter就是指一堆kernel
其中channels表示卷积核的数量,一般为2的指数次方
权值共享,在卷积过程中卷积核的权重是不会改变的,简单来说就是在一幅图片中的不同位置的相同目标,它们的特征是基本相同的;同一个新图像中的每一个像素都来自完全相同的卷积核,这就是卷积核的权值共享。权值共享的目的减轻过拟合 & 降低计算量。
pooling池化的作用则体现在降采样:保留显著特征、降低特征维度,增大kernel的感受野。max_pool(value, ksize, strides, padding, name=None), ksize一般是[1, height, width, 1],因为我们不想在batch和channels上做池化,所以这两个维度设为了1。池化输出特征图计算和卷积计算公式相同,区别是池化是求卷积区域中的max,不涉及卷积计算。
池化层可对提取到的特征信息进行降维,一方面使特征图变小,简化网络计算复杂度并在一定程度上避免过拟合的出现;另一方面进行特征压缩,提取主要特征。
最大值池化与均值池化的区别
因为图像识别的过程通常是靠边缘才能识别图像和物体,max pooling通常能够尽量保存边缘,而mean pooling用到了窗口内的所有像素,相当于是进行了模糊处理。但是也不是只使用max pooling,只是更常见。
激活层,实际上是对卷积层的输出结果做一次非线性映射。通常输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是激活函数 Activation Function。如果不用激励函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合。如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。
经过前面若干次卷积+激励+池化后,模型会将学到的一个高质量的特征图片全连接。其实在全连接层之前,如果神经元数目过大,学习能力强,有可能出现过拟合。因此,可以引入dropout一种正则化的方法,来随机删除神经网络中的部分神经元。当来到了全连接层之后,可以理解为一个简单的多分类神经网络(如:BP神经网络),通过softmax函数得到最终的输出。整个模型训练完毕。
About Me:小婷儿
● 本文作者:小婷儿,专注于python、数据分析、数据挖掘、机器学习相关技术,也注重技术的运用
● 作者博客地址:https://blog.csdn.net/u010986753
● 本系列题目来源于作者的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
● 微信:tinghai87605025 联系我加微信群
● QQ:87605025
● QQ交流群py_data :483766429
● 公众号:python宝 或 DB宝
● 提供 OCP、OCM 和高可用最实用的技能培训
● 题目解答若有不当之处,还望各位朋友批评指正,共同进步
如果你觉得到文章对您有帮助,欢迎赞赏,有您的支持,小婷儿一定会越来越好!