卷积算子是可交换的。所以,I*(A*B) 应该等于 (I*A)*B 是对的。让我们首先将其转换为矩阵形式。内核 A 的卷积可以转换为与以下卷积矩阵 C 相乘:
C=
[-1 0 1 0 0 0 0 0]
[ 0 -1 0 1 0 0 0 0]
[ 0 0 -1 0 1 0 0 0]
[ 0 0 0 -1 0 1 0 0]
[ 0 0 0 0 -1 0 1 0]
[ 0 0 0 0 0 -1 0 1]
[ 1 0 0 0 0 0 -1 0]
[ 0 1 0 0 0 0 0 -1]
我们采用内核 [-1 0 1] 并在第一行用 5 个零填充它。然后,下面的每一行都是在前一行的右侧循环移位 1 个位置。
所以,如果我们想计算核 A 与 I 的卷积:
I = [1 0 -1 0 1 0 1 1]
我们只计算矩阵-向量乘法:
C*I^T
(其中 I^T 是转置向量)。
根据上述公式,我们得到:
I*(A*B) = (C*C)*I^T
计算C*C得到如下矩阵:
C^2=
[ 1 0 -2 0 1 0 0 0]
[ 0 1 0 -2 0 1 0 0]
[ 0 0 1 0 -2 0 1 0]
[ 0 0 0 1 0 -2 0 1]
[ 1 0 0 0 1 0 -2 0]
[ 0 1 0 0 0 1 0 -2]
[-2 0 1 0 0 0 1 0]
[ 0 -2 0 1 0 0 0 1]
和:
I*(A*B) = (C*C)*I^T =
[ 4]
[ 0]
[-2]
[ 1]
[ 0]
[-2]
[-2]
[ 1]
而(I*A)*B 是矩阵公式C*(C*I^T),它等于矩阵关联性的I*(A*B)=(C*C)*I^T。
您可以通过运行以下 numpy 代码来验证此结果:
import numpy as np
C = [[-1,0,1,0,0,0,0,0]]
for k in range(7):
C.append(np.roll(C[0],k+1))
C = np.array(C)
#print(C)
I = np.transpose(np.array([[1,0,-1,0,1,0,1,1]]))
print(f'C=\n{C}\n')
print(f'C^2=\n{C@C}\n')
print(f'(C*C)*I=\n{(C@C)@I}\n')
print(f' C*(C*I)=\n{C@(C@I)}\n')
以上代码运行结果为:
C=
[[-1 0 1 0 0 0 0 0]
[ 0 -1 0 1 0 0 0 0]
[ 0 0 -1 0 1 0 0 0]
[ 0 0 0 -1 0 1 0 0]
[ 0 0 0 0 -1 0 1 0]
[ 0 0 0 0 0 -1 0 1]
[ 1 0 0 0 0 0 -1 0]
[ 0 1 0 0 0 0 0 -1]]
C^2=
[[ 1 0 -2 0 1 0 0 0]
[ 0 1 0 -2 0 1 0 0]
[ 0 0 1 0 -2 0 1 0]
[ 0 0 0 1 0 -2 0 1]
[ 1 0 0 0 1 0 -2 0]
[ 0 1 0 0 0 1 0 -2]
[-2 0 1 0 0 0 1 0]
[ 0 -2 0 1 0 0 0 1]]
(C*C)*I=
[[ 4]
[ 0]
[-2]
[ 1]
[ 0]
[-2]
[-2]
[ 1]]
C*(C*I)=
[[ 4]
[ 0]
[-2]
[ 1]
[ 0]
[-2]
[-2]
[ 1]]
所以你可以看到得到了同样的结果。
请注意,A*B 不是 [0 -2 0] 而是 [1 0 -2 0 1],因为您采用 A=[-1 0 1] (将其两侧的零填充到它的两侧各有 2 个零side) 并将其作为滑动窗口移动到 B=[-1 0 1] 所以首先你得到: [-1-1, 0-1 + -10, -11 + 00 + 1-1, 10 + 01, 11] = [1 0 -2 0 1]。 2 个宽度为 n 的内核的卷积结果将是长度为 2n-1。