如何将两个二维卷积合并在一起



" * "表示卷积

你好,

我正在尝试找到一种方法将两个二维卷积合并在一起。

假设我有一个尺寸为(1x20x20)的图像"Img"和两个尺寸为(1x3x3)的内核"k1"one_answers"k2"。

通常你会先将Img与k1进行卷积然后将结果与k2进行卷积:

(Img * k1) * k2

我的目标是找到一个内核k3,如果应用于Img,它可以完成与上面表达式相同的事情。因为卷积是线性算子,这是可能的。为了做到这一点(至少从数学上讲)我们可以首先将k1与k2进行卷积然后将结果应用于Img:

k3 = k1 * k2

(Img * k1) * k2 = Img * (k1 * k2) = Img * k3

这个公式虽然在数学世界中很有效,但在实现层面上却完全不起作用。以上面的例子为例。k1和k2的维数都是(1x3x3)如果我盲目地应用上面的公式将k1和k2进行卷积那么输出的维数将是(1x1x1)这显然不是我想要的。因此,即使在这个非常简单的场景中,这个公式也是"错误的"。在这种情况下,我们应该做的是用2个像素填充k1,以获得我们正在寻找的正确内核k3。

我在这里找到了一个代码。

为了简单起见,我将在这里报告代码:

import torch
def merge_conv_kernels(k1, k2):
"""
:input k1: A tensor of shape ``(out1, in1, s1, s1)``
:input k1: A tensor of shape ``(out2, in2, s2, s2)``
:returns: A tensor of shape ``(out2, in1, s1+s2-1, s1+s2-1)``
so that convolving with it equals convolving with k1 and
then with k2.
"""
padding = k2.shape[-1] - 1
# Flip because this is actually correlation, and permute to adapt to BHCW
k3 = torch.conv2d(k1.permute(1, 0, 2, 3), k2.flip(-1, -2),
padding=padding).permute(1, 0, 2, 3)
return k3

然而,当两个卷积具有不同的填充和步长时,此代码根本无法工作。

我想知道是否仍然有可能合并卷积时,考虑到填充和跨步,如果有人可以提供一个提示如何做到这一点或工作代码为这个更复杂的场景(PyTorch)。

谢谢

当第一次卷积填充足够(内核大小- 1)且没有跨步时,你可以合并你的卷积与任何垫/步幅的第二次卷积:

def merge_conv_kernels(k1, k2, s2, p2):
# Assuming p1 = k1.shape[-1] - 1 and s1 = 1
kernel_pad = k2.shape[-1] - 1
k3 = torch.conv2d(k1.permute(1, 0, 2, 3), k2.flip(-1, -2),
padding=kernel_pad,
stride=1).permute(1, 0, 2, 3)
p3 = k1.shape[-1] - 1 + p2
s3 = s2
return k3, s3, p3

如果你在第一次卷积中pad=0,你可以找到反例。例如,在3*3图像和:

kernel1 = tensor([[1, 0, -1],
[1, 0, -1],
[1, 0, -1]])
p1, s1 = 0, 1
kernel2 = ones(3, 3)
p2, s2 = 2, 1

基本上,两个卷积的组合应用内核1并复制3*3图像中的值。你不能只用一次卷积得到它。首先,内核的大小应该是4或5,并填充1或2,以便每次都获得所有输入值(或者大于5,但这会导致内核中永远不会使用值)。现在通过考虑每个像素和任何输入矩阵,我们可以看到核必须包含核1在其所有3*3子矩阵中。但由于内核的不对称性,这是不可能的。

你可以在stride>1或padding

最新更新