Pytorch 如何使Pyrotch中的卷积结合?

Pytorch 如何使Pyrotch中的卷积结合?,pytorch,convolution,Pytorch,Convolution,根据定义,离散卷积是关联的。但当我试图在pytorch中验证这一点时,我找不到一个可信的结果 结合定律是$f*(g*\psi)=(f*g)*\psi$,因此我创建了三个以零为中心的离散函数(作为张量),并使用适当的零填充对它们进行卷积,从而获得结果图中的所有非零元素 导入火炬 导入torch.nn作为nn def test_conv_compst(): #$\psi$ 输入=torch.randn((1,4,7,7)) #$g$ a=火炬的随机数((7,4,3,3)) #$f$ b=火炬的随机数

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

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

导入火炬
导入torch.nn作为nn
def test_conv_compst():
#$\psi$
输入=torch.randn((1,4,7,7))
#$g$
a=火炬的随机数((7,4,3,3))
#$f$
b=火炬的随机数((3,7,3,3))
int_1=torch.conv2d(输入,a,填充=2)
#由一阶导数得到的结果
res_1=torch.conv2d(int_1,b,padding=2)
comp_k=torch.conv2d(a.转置(1,0),b,padding=2).转置(1,0)
打印(复合形状)
#通过二阶方程得到的结果
res_2=torch.conv2d(输入、组件、填充=4)
打印(分辨率1.形状)
打印(分辨率2.形状)
打印(最大手电筒(手电筒abs(分辨率2-分辨率1)))
预期结果是,两个结果的差异可以忽略不计。但它的回报是:

torch.Size([3, 4, 5, 5])
torch.Size([1, 3, 11, 11])
torch.Size([1, 3, 11, 11])
tensor(164.8044)

长话短说,这是因为批处理。
torch.conv2d
的第一个参数解释为
[batch,channel,height,width]
,第二个参数解释为
[out\u channel,in\u channel,height,width]
,输出为
[batch,channel,height,width]
。因此,如果调用
conv2d(a,conv2d(b,c))
,则将
b
的前导维度视为批处理,如果调用
conv2d(conv2d(a,b,c)
,则将其视为
输出通道

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

卷积实现返回一个图像(向量、内核,不管你怎么称呼它),而不是相关的循环矩阵,这是荒谬的冗余,在大多数情况下无法放入内存。因此,我们还需要一些循环向量算子
V(矩阵)
,它返回
矩阵的第一列,因此是
M
的逆。在抽象的数学术语中,函数(实际上是
相关的
,因为卷积需要对其中一个输入进行额外翻转,为了清楚起见,我跳过了这一点)被实现为
卷积=λ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)
我希望我没有失去你,这只是通过利用
V
M
的倒数和矩阵乘法的关联性来移动括号,将一个转换成另一个。请注意,中间行基本上是“原始”
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的问题在于,它将第一个输入解释为
[批次、通道、高度、宽度]
,将第二个输入解释为
[输出通道、输入通道、高度、宽度]
。这意味着第一个参数和第二个参数的“转换”运算符
M
不同。让我们分别称它们为
M
N
。因为只有一个输出,所以只有一个
V
,它可以是
M
N
的倒数,但不能两者都是(因为它们不同)。如果您重新编写上述等式,注意区分
M
N
,您将看到,根据您的选择
V
是反转一个还是反转另一个,您无法写出第2行和第3行或第3行和第4行之间的等式


实际上,
通道
维度还有一个额外的问题,这在卷积的经典定义中是不存在的,但是我的第一个猜测是,与批处理不同,它可以由两个操作数的单个提升运算符
M
来处理。

Hi,感谢您的详细解释!我知道
M
N
是不同的,但我觉得
N(x)=M(x.transpose(1,0))
。这就是为什么在将内核作为输入输入输入到代码中之前,我会对其进行转置。为了让自己相信这是合理的,我画了一个草图。