一维卷积
在tensorflow中,可以使用tf.layers.conv1d
实现一维卷积。
inputs
: 输入Tensorfilters
: int, 输出Tensor通道数kernel_size
: int/tuple or list of int, 一维卷积窗口的大小strides
: int/tuple or list of int, 卷积步长padding
:valid
/same
dilation_rate
: int/tuple or list of int, 默认为1,定义了卷积核处理数据时各值的间距,即实现“空洞卷积”。在相同的计算条件下,空洞卷积提供了更大的感受野,空洞卷积经常用在图像分割中,当网络层需要较大的感受野但计算资源有限时而无法提高卷积核数量或大小时,可以考虑空洞卷积。
padding在valid
和same
时的不同:以input width=13, filter width=6, stride=5
为例,
-
valid
-> without padding: -
same
-> padding:
注意:在TensorFlow中,valid
表示绝不补零,若剩余数据不够卷积则直接丢弃;same
表示尽量补零,这会使得所有数据参与卷积,特别地,在stride=1
时,same
会使得卷积前后的feature map等大。部分例子:
# 在这个例子中,使用tf.nn.conv1d()实现一维卷积
# tf.nn.conv1d(): https://tensorflow.google.cn/api_docs/python/tf/nn/conv1d?hl=en
# shape=(batch,in_width,in_channels)
inputs = tf.constant(1,shape=(64,10,1),dtype=tf.float32,name='input')
# shape=(filter_size,in_channels,out_channels)
w = tf.constant(3,shape=(3,1,32),dtype=tf.float32,name='w')
# VALID 示例,绝不补零
# <tf.Tensor 'conv1d_1/Squeeze:0' shape=(64, 4, 32) dtype=float32>
conv_1=tf.nn.conv1d(inputs,w,stride=2,padding='VALID')
# <tf.Tensor 'conv1d_2/Squeeze:0' shape=(64, 8, 32) dtype=float32>
conv_2=tf.nn.conv1d(inputs,w,stride=1,padding='VALID')
# <tf.Tensor 'conv1d_3/Squeeze:0' shape=(64, 3, 32) dtype=float32>
conv_3=tf.nn.conv1d(inputs,w,stride=3,padding='VALID')
# SAME 示例,尽量补零,stride=1时,卷积前后feature map等大
# <tf.Tensor 'conv1d_4/Squeeze:0' shape=(64, 10, 32) dtype=float32>
conv_4=tf.nn.conv1d(inputs,w,stride=1,padding='SAME')
# <tf.Tensor 'conv1d_5/Squeeze:0' shape=(64, 5, 32) dtype=float32>
conv_5=tf.nn.conv1d(inputs,w,stride=2,padding='SAME')
# <tf.Tensor 'conv1d_6/Squeeze:0' shape=(64, 4, 32) dtype=float32>
conv_6=tf.nn.conv1d(inputs,w,stride=3,padding='SAME')
# stride=1,filter_size无论多大,卷积前后feature map等大
# shape=(filter_size,in_channels,out_channels)
w_2 = tf.constant(3,shape=(4,1,32),dtype=tf.float32,name='w')
# <tf.Tensor 'conv1d_7/Squeeze:0' shape=(64, 10, 32) dtype=float32>
conv_7=tf.nn.conv1d(inputs,w_2,stride=1,padding='SAME')
# shape=(filter_size,in_channels,out_channels)
w_3=tf.constant(3,shape=(5,1,32),dtype=tf.float32,name='w')
# <tf.Tensor 'conv1d_8/Squeeze:0' shape=(64, 10, 32) dtype=float32>
conv_8=tf.nn.conv1d(inputs,w_3,stride=1,padding='SAME')
在卷积时,假定卷积层有M个filter,则在卷积时,每个filter对每一个通道都去卷积一遍,然后把每个通道的卷积结果通过线性组合(如求和等),得到最终卷积结果
上图为一个1x1卷积核(conv1d)对具有三个通道的feature map进行卷积的图示,由此得到的下一层1个feature map。注意:卷积核的深度必须与输入数据的深度一致,一维卷积不改变feature map的长宽
卷积核需要指定大小和数量,对于一维卷积而言,大小即为1×1,而数量即等于下一层feature map的channel:
这即完成了一维卷积中升降维和各通道信息混合。并且减少了参数,当然也可加入非线性,提升网络表达能力。
若一维卷积作用在一个平面图上时(通道为1),1×1卷积并无太大意义(相当于对每个像素乘一个常数而已),但当我们对多通道的图片进行1×1卷积时,就是对每个通道进行了线性加权求和,其权重即是待训练的卷积核的值。
1×1卷积核和全连接层最大的区别就是输入尺寸是否可变,全连接层的输入尺寸是固定的,而卷积层的输入尺寸是任意的。
在文本中,一维卷积的行为也是类似的,贯穿embedding_size层卷积:
二维卷积
在卷积中,卷积核在张量的width、height维是局部连接的,在depth维是贯穿整个channel维。注意:在下一层feature map的depth维度上,各个channel上的卷积核并不共享。即当深度变为m时,一组卷积核也随之扩展到m个。不同的channel上,用的是自己的卷积核。一组卷积核将含有channel个的feature maps全部卷积生成一个输出feature map,n组卷积核生成n个feature map。
其中,(y_0)是标量;([w_{r_0},w_{r_1},w_{r_2},w_{r_3}])是权重0,也即是卷积核0,([x_{r_0},x_{r_1},x_{r_2},x_{r_3}])是feature_map0第一个卷积区域(如上图蓝色框中的[0,1,4,5]);([w'_{r_0},w'_{r_1},w'_{r_2},w'_{r_3}])是权重1,也即卷积核1,([x'_{r_0},x'_{r_1},x'_{r_2},x'_{r_3}])是feature_map1第一个卷积区域;(b_0)是偏置值
卷积核对feature map以贯穿整个depth维形式卷积一次获得一个scalar,每一组卷积核获得一个feature map。通常想要在卷积时保持图片的原始尺寸,选择3×3的filter和1的zero padding,或5×5的filter和2的zero padding可以保持图片的原始尺寸,这也是多选择3×3和5×5的filter的原因。另外,3×3的filter考虑到了像素与其距离为1以内的所有其它像素的关系,而5×5则是考虑像素与其距离为2以内所有像素的关系。
(input\_size+2×padding)是经过zero_padding之后要卷积的尺寸,减去(filter\_size)表示可以滑动的范围,再除以每一次滑动移动的大小(stride),即可得到可以滑动多少次,每一次滑动都会产生一个输出节点(一个scalar),再加上第一个不需要滑动也会产生的输出节点,即可得到下一层feature map的尺寸。
全连接层和卷积层的区别:
-
全连接层没有发生权重共享;而对于每个输出feature map而言,在上一层的每个feature map上,feature map都是由一个小卷积核完成的卷积,即“局部连接,全局共享”。上一层的每个feature map对应的小卷积核组成一个卷积核组,每个卷积核组计算生成一个feature map,文献中可称作“filter”。每一层的参数量为:
[(K×K×D_1+1)×F ]其中,(K)为filter_size;(D_1)为上一层feature map的channel数;(F)为“filter”个数,也即输出feature map的channel数;(+1)为每个“卷积核组”对应的偏置值。
-
全连接层和卷积层都发生了“信息混合”。全连接层的信息混合的信息源来自全feature map全channel,接受的信息来源更广泛;而卷积的信息源是filter_size大小全channel。
-
另外,还有三维卷积,原理类似,不赘述。