阅读本文的基础,是默认已经理解了图像处理中正向卷积的过程(卷积特征提取 - UFLDL)。
什么是反卷积?
在应用在计算机视觉的深度学习领域,由于输入图像通过卷积神经网络(CNN)提取特征后,输出的尺寸往往会变小,而有时我们需要将图像恢复到原来的尺寸以便进行进一步的计算(e.g.:图像的语义分割),这个采用扩大图像尺寸,实现图像由小分辨率到大分辨率的映射的操作,叫做上采样(Upsample)。
- 反卷积(Transposed Convolution)
上采样有3种常见的方法:双线性插值(bilinear),反卷积(Transposed Convolution),反池化(Unpooling),我们这里只讨论反卷积。这里指的反卷积,也叫转置卷积,它并不是正向卷积的完全逆过程,用一句话来解释:
反卷积是一种特殊的正向卷积,先按照一定的比例通过补 来扩大输入图像的尺寸,接着旋转卷积核,再进行正向卷积。
反卷积的数学推导
假设输入图像 尺寸为 ,元素矩阵为:
卷积核 尺寸为 ,元素矩阵为:
步长 ,填充 ,即 ,
则按照卷积计算公式 ,输出图像 的尺寸为 。
把 的元素矩阵展开成一个列向量 :
把输出图像 的元素矩阵展开成一个列向量 :
对于输入的元素矩阵 和 输出的元素矩阵 ,用矩阵运算描述这个过程:
通过推导,我们可以得到稀疏矩阵 :
反卷积的操作就是要对这个矩阵运算过程进行逆运算,即通过 和 得到 ,根据各个矩阵的尺寸大小,我们能很轻易的得到计算的过程,即为反卷积的操作:
但是,如果你代入数字计算会发现,反卷积的操作只是恢复了矩阵 的尺寸大小,并不能恢复 的每个元素值,本文最后会在 tensorflow 平台进行这个实验。
在此之前,我们先给出反卷积图像尺寸变化的公式。
在进行反卷积时,简单来说,大体上可分为以下两种情况:
Relationship 1:
此时反卷积的输入输出尺寸关系为:
如上图所示,我们选择一个输入 尺寸为 ,卷积核 尺寸为 ,步长 ,填充 ,即 ,则输出 的尺寸为 。
Relationship 2:
此时反卷积的输入输出尺寸关系为:
如上图所示,我们选择一个输入 的尺寸为 ,卷积核 的尺寸为 ,步长 ,填充 ,即 ,则输出 的尺寸为 。
在图像语义分割网络 FCN-32s 中,上采样反卷积操作的输入每张图像的尺寸是 ,我们希望进行一次上采样后能恢复成原始图像的尺寸 ,代入公式:
根据上式,我们可以得到一个关于 三者之间关系的等式:
通过实验,最终找出了最合适的一组数据:
在 tensorflow 中实现反卷积
下面我们用一组实验更直观的解释一下在 tensorflow 中反卷积的过程:
我们令输入图像为:
卷积核为:
case 1
如果要使输出的尺寸是 ,步数 ,tensorflow 中的命令为:
transpose_conv = tf.nn.conv2d_transpose(value=input,
filter=kernel,
output_shape=[1,5,5,1],
strides=2,
padding='SAME')
当执行 transpose_conv 命令时,tensorflow 会先计算卷积类型、输入尺寸、步数和输出尺寸之间的关系是否成立,如果不成立,会直接提示错误,如果成立,执行如下操作:
1. 现根据步数 对输入的内部进行填充,这里 可以理解成输入放大的倍数,即在 的每个元素之间填充 , 的个数 与 的关系为:
例如这里举例的 ,即在 的每个元素之间填 个 :
因为卷积类型为 same,所以此时, 。
2. 接下来,用卷积核 对填充后的输入 进行步长 的正向卷积,根据正向卷积输出尺寸公式: 得到输出尺寸是 ,反卷积公式中我们给出的输出尺寸参数 也是为 ,两者相同,所以可以进行计算,结果为:
与 tensorflow 的运行结果相同。
case 2
我们将 case 1 中的输出尺寸 改成 ,其他参数均不变,tensorflow 中的命令为:
transpose_conv = tf.nn.conv2d_transpose(value=input,
filter=kernel,
output_shape=[1,6,6,1],
strides=2,
padding='SAME')
卷积类型是 same,我们首先在外围填充一圈 :
这时发现,填充后的输入尺寸与 的卷积核卷积后的输出尺寸是 ,没有达到 的 ,这就需要继续填充 ,tensorflow 的计算规则是优先在左侧和上侧填充一排 ,填充后的输入变为:
接下来,再对这个填充后的输入与 的卷积核卷积,结果为:
与 tensorflow 的运行结果相同。
最后,我们要验证一下前文中提到的“如果你代入数字计算会发现,反卷积的操作只是恢复了矩阵 的尺寸大小,并不能恢复 的每个元素值”。
1. 正向卷积:
value = tf.reshape(tf.constant([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]]),
[1, 5, 5, 1])
filter = tf.reshape(tf.constant([[1., 0.],
[0., 1.]]),
[2, 2, 1, 1])
output = tf.nn.conv2d(value, filter, [1, 2, 2, 1], 'SAME')
卷积的结果是:
2. 我们用和正向卷积完全相同的参数对这个结果进行反卷积:
input = tf.reshape(tf.constant([[6., 8., 3.],
[12., 14., 6.],
[7., 8., 9.]]),
[1, 3, 3, 1])
kernel = tf.reshape(tf.constant([[1., 0.],
[0., 1.]]),
[2, 2, 1, 1])
output = tf.nn.conv2d_transpose(value=input,
filter=kernel,
output_shape=[1, 3, 3, 1],
strides=[1, 2, 2, 1],
padding='SAME')
卷积的结果是:
可见,反卷积不能恢复数值,而且,在当 时,即便使用完全相同的参数进行转置卷积,输入的尺寸也不能恢复。 |