如何在pytorch关联中进行卷积?

问题描述 投票:0回答:1

根据定义,离散卷积是关联的。但是当我试图在pytorch中验证这一点时,我找不到合理的结果。

关联定律是$ f *(g * \ psi)=(f * g)* \ psi $,所以我创建了三个以零为中心的离散函数(作为张量)并用适当的零填充卷积它们以使所有非零获得结果图中的元素。

import torch
import torch.nn as nn

def test_conv_compst():
    # $\psi$
    inputs = torch.randn((1,4,7,7))
    # $g$
    a = torch.randn((7, 4, 3, 3))
    # $f$
    b = torch.randn((3, 7, 3, 3))
    int_1 = torch.conv2d(inputs, a, padding=2)
    # results obtained by the first order
    res_1 = torch.conv2d(int_1, b, padding=2)

    comp_k = torch.conv2d(a.transpose(1, 0), b, padding=2).transpose(1, 0)
    print(comp_k.shape)
    # results obtained through the second order
    res_2 = torch.conv2d(inputs, comp_k, padding=4)
    print(res_1.shape)
    print(res_2.shape)
    print(torch.max(torch.abs(res_2-res_1)))

预期结果是两个结果的差异可以忽略不计。但它返回:

torch.Size([3, 4, 5, 5])
torch.Size([1, 3, 11, 11])
torch.Size([1, 3, 11, 11])
tensor(164.8044)
pytorch convolution
1个回答
0
投票

长话短说,这是因为批处理。 torch.conv2d的第一个论点被解释为[batch, channel, height, width],第二个被解释为[out_channel, in_channel, height, width],输出被称为[batch, channel, height, width]。因此,如果你调用conv2d(a, conv2d(b, c)),你将b的领先维度视为批处理,如果你调用conv2d(conv2d(a, b), c),则将其视为out_channels

话虽如此,我得到的印象是你在这里问数学,所以让我扩展一下。你的想法在理论上是正确的:卷积是线性算子,应该是关联的。但是,由于我们为它们提供内核而不是代表线性运算符的实际矩阵,因此需要在幕后进行一些“转换”,以便将内核正确地解释为矩阵。经典地,这可以通过构造相应的circulant matrices(边界条件除外)来完成。如果我们用abcM的循环矩阵创建算子表示内核,我们得到M(a) @ [M(b) @ M(c)] = [M(a) @ M(b)] @ M(c),其中@表示矩阵 - 矩阵乘法。

卷积实现返回一个图像(矢量,内核,但你称之为),而不是相关的循环矩阵,这是非常冗余的,并且在大多数情况下不适合存储器。因此,我们还需要一些循环到矢量的算子V(matrix),它返回matrix的第一列,因此是M的逆。在抽象的数学术语中,像scipy.signal.convolve这样的函数(实际上是correlate,因为卷积需要额外翻转其中一个输入,为了清楚起见,我跳过)被实现为convolve = lambda a, b: V(M(a) @ M(b)),因此

convolve(a, convolve(b, c)) =
                            = V(M(a) @ M(V[M(b) @ M(c)])
                            = V(M(a) @ M(b) @ M(c))
                            = V(M(V[M(a) @ M(b)]) @ M(c))
                            = convolve(convolve(a, b), c)

我希望我没有失去你,这只是通过利用VM的逆和矩阵乘法的关联来移动括号的事实将一个转换为另一个。请注意,中间线基本上是“原始”ABC。我们可以使用以下代码进行验证:

import numpy as np
import scipy.signal as sig

c2d = sig.convolve2d

a = np.random.randn(7, 7)
b = np.random.randn(3, 3)
c = np.random.randn(3, 3)

ab = c2d(a, b)
ab_c = c2d(ab, c)

bc = c2d(b, c)
a_bc = c2d(a, bc)

print((a_bc - ab_c).max())

PyTorch的问题在于它将第一个输入解释为[batch, channel, height, width],第二个输入解释为[out_channels, in_channels, height, width]。这意味着“转换”运算符M对于第一个参数和第二个参数是不同的。我们分别称他们为MN。由于只有一个输出,只有一个V,它可以是MN的倒数,但不是两者(因为它们不同)。如果你重写上面的等式,注意区分MN你会看到,根据你的选择V是否反转了一个或另一个,你无法在第2行和第3行或第3行和第4行之间写出相等。

在实践中,还有channel维度的附加问题,这在经典的卷积定义中并不存在,但是我的第一个猜测是它可以用两个操作数的单个提升算子M处理,不像批处理。

© www.soinside.com 2019 - 2024. All rights reserved.