赞
踩
最近在看全连接网络的一些代码,里面的反卷积操作还是有点意思的,写个博客记录一下自己的心得,以便后续自己的查看
1.卷积操作
在TensorFlow中,由tf.nn.conv2d()函数来实现卷积操作,举个例子
y4 = tf.nn.conv2d(x2, kernel, strides=[1,2,2,1], padding=“SAME”)具体讲一下函数的参数含义
第一个参数:是一个四维的张量输入,具有[batch_size,height,width,channel]这样的shape,切记这个是一个四维的tensor,数据类型一般为float32或者是float64.四个参数的具体含义是训练一个batch里面的图片数量,图片的高宽和通道数,或者说是图像的特征层的数量。
第二个参数:filter的表示,同样也是一个四维的tensor,具有[filter_height,filter_width,in_channel,out_channel]的shape,一般来说卷积核的高宽都是一样的,第三个参数表示输入图像的channel,这个数值和被卷积对象的channel是一致的,即与四维张量输入的channel是相等的,第四个参数表示输出的channel,表示经过卷积核卷积操作后的图像的通道数。
第三个参数:表示卷积核在卷积操作过程中每一维中的移动步长,是一个一维的向量,如上述的例子所述,第一个和第四个参数一般都为1,因为在batch_size和channel中的步长都默认为1,第二个和第三个参数表示卷积核在图像中水平和垂直方向的滑动步长,视情况而变。
第四个参数:卷积的操作形式,只能是SAME和VALID两者二选一,表示不同的卷积形式
SAME和VALID的区别
卷积方式: SAME(卷积后的特征图大小跟卷积核大小无关,只跟卷积所用的步长有关,当卷积步长是1时,卷积前后特征图大小保持不变)
输入特征图大小:W×W
卷积核大小: k×k
卷积步长: s×s
输出特征图大小: W1×W1
W1 = math.ceil(W / s) 表示向上取整函数
卷积方式是SAME时,会根据卷积核大小,按需在图像上扩充padding,即在宽度或高度方向上补充若干个0,保证图像上的当前点处于卷积中心。
如果补充的padding个数为偶数会在两侧补充相同个数个0,如果padding为奇数2n+1,会在左侧补n个0,右侧补n+1个0。
总结:其实我们没必要看SAME方式是如何padding补零的,既然是写了SAME方式,经过卷积核的输出大小只和步长stride有关,直接除步长向上取整就能得到输出图像的大小。
卷积方式: VALID(根据图片实际大小执行卷积,不对图像边界填充,一般卷积后特征图变小)
输入特征图大小:W×W
卷积核大小: k×k
卷积步长: s×s
输出特征图大小: W1×W1
W1 = math.ceil((W-k+1)) / s 表示向上取整函数
VALID方式和上述的SAME就不大一样了,VALID对图像是没有补零操作的,所以经过卷积后图像的尺寸会变小,而且根据上述的公式卷积后的大小和卷积核尺寸步长都有关系。
到这里卷积操作就基本上讲的差不多了,上个卷积的图
2.反卷积操作
反卷积操作就是个骚操作了,也是写这篇博客的主要目的,也不知道我的理解是不是对的,如有错误,请指正。
原理上来讲,反卷积操作就是卷积的转置操作,卷积的反向操作,在TensorFlow中,通过tf.nn.conv2d_transpose()函数来实现,还是先讲一下函数的参数含义
conv2d_transpose(
value,
filter,
output_shape,
strides,
padding='SAME',
data_format='NHWC',
name=None
)
第一个参数:和前面讲的正向卷积是一样的,四维的一个输入张量[N,H,W,C],没啥区别。
第二个参数:卷积核的大小,这里要注意一下,和前面的有点不同[filter_size,filter_size,out_channel,in_channel],可以看出输入和输出的channel和正向卷积刚好相反,因为毕竟是反向卷积吗,所以反过来也好理解QAQ。这个算是一个不同点了
第三个参数:输出shape,这是反卷积的一个骚操作,为什么要指定输出的输出大小呢?后面会仔细讲。
第四个参数:步长,和前面的正向卷积差不多吧
第五个参数:padding模式,也是SAME和VALID这两种
反卷积的实现过程
主要是包括两个过程:
#Step 1 扩充:
将 inputs 进行填充扩大,扩大的倍数与strides有关(strides倍)。扩大的方式是在元素之间插[ strides - 1 ]个 0。padding="VALID"时,在插完值后继续在周围填充的宽度为[ kenel_size - 1 ],填充值为0;padding = "SAME"时,在插完值后根据output尺寸进行填充,填充值为0。
#Step 2 卷积:
对扩充变大的矩阵,用大小为kernel_size卷积核做卷积操作,并且这里的卷积步长一定为1(与参数strides无关,一定是1)
其实我们也能看出反卷积其实就是先把原始的输入进行一系列的插值进行扩大然后在进行正常的卷积操作,其实也是个paper tiger,还有一个问题为什么要指定输出的outshape?因为好像根据卷积核大小、步长、输入能推导出最后的输出结果,最重要的一点是因为一个输入的反卷积结果可能会有好几个,我们需要通过指定输出的大小来确定到底是哪个结果
那我们应该如何计算最后输出的大小呢?其实这有要关注反卷积的逆向操作卷积了,我们可以通过正向的卷积来判断是否输出的shape合理。
屁话少说,代码详解,更有利于理解
import tensorflow as tf x1 = tf.constant(1.0, shape=[1, 3, 3, 1]) x2 = tf.constant(1.0, shape=[1, 6, 6, 3]) x3 = tf.constant(1.0, shape=[1, 5, 5, 3]) kernel = tf.constant(1.0, shape=[3, 3, 3, 1]) y1 = tf.nn.conv2d_transpose(x1, kernel, output_shape=[1, 6, 6, 3], strides=[1, 2, 2, 1], padding="SAME") y2 = tf.nn.conv2d_transpose(x1, kernel, output_shape=[1, 5, 5, 3], strides=[1, 2, 2, 1], padding="SAME") # 同样输入具有两个不同的输出 """ 上述的两个反卷积操作是SAME模式,我不管它是怎么padding0的,根据正向卷积的操作, 输出的大小是W/S向上取整,和卷积核大小无关,5/2和6/2向上取整都为3,所以可以有两个输出 """ y3 = tf.nn.conv2d(x3, kernel, strides=[1, 2, 2, 1], padding="SAME") y4 = tf.nn.conv2d_transpose(y3, kernel, output_shape=[1, 7, 7, 3], strides=[1, 2, 2, 1], padding="VALID") y5 = tf.nn.conv2d_transpose(y3, kernel, output_shape=[1, 8, 8, 3], strides=[1, 2, 2, 1], padding="VALID") # 同样输入具有两个不同的输出 """ 上述的两个反卷积操作是VALID模式,我不管它是怎么padding0的,根据正向卷积的操作, 输出的大小(W-k+1))/s是向上取整,(7-3+1)/2和(8-3+1)/2向上取整都为3,所以可以有两个输出 """ y6 = tf.nn.conv2d_transpose(x1,kernel,output_shape=[1,9,9,3],strides=[1,3,3,1],padding="SAME") """ 上述代码可以直接使用stride的倍数来进行扩张输出的倍数,输出=输入×stride """ sess= tf.Session() tf.global_variables_initializer().run(session=sess) x1_decov, x1_decov2, x3_cov, y3_decov, y3_decov2,x1_decov3 = sess.run([y1, y2, y3, y4, y5,y6]) print(x1_decov.shape) print(x1_decov2.shape) print(x3_cov.shape) print(y3_decov.shape) print(y3_decov2.shape) print(x1_decov3.shape)
讲的差不多了,总的来说反卷积就是卷积的逆向操作,好好理解卷积的操作能够很好的反向推导反卷积。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。