Python 卷积核生成线性运算矩阵的一种新方法

Python 卷积核生成线性运算矩阵的一种新方法,python,numpy,tensorflow,vectorization,convolution,Python,Numpy,Tensorflow,Vectorization,Convolution,形状(k1,k2,n_通道,n_滤波器)的二维卷积核K应用于形状(m1,m2,n_通道)的二维向量A,并生成形状(m1-k1+1,m2-k2+1,n_滤波器)(具有有效填充)的另一二维向量B 同样,对于每个K,都存在一个W\u K形状(m1-k1+1,m2-k2+1,n\u滤波器,m1,m2,n\u通道),使得W\u K和a的张量点等于B。i、 e.B=np.tensordot(wk,A,3) 我试图找到一个纯粹的NumPy解决方案,从K生成这个wk,而不使用任何python循环 我可以看到W_

形状
(k1,k2,n_通道,n_滤波器)
的二维卷积核
K
应用于形状
(m1,m2,n_通道)
的二维向量
A
,并生成形状
(m1-k1+1,m2-k2+1,n_滤波器)
(具有有效填充)的另一二维向量
B

同样,对于每个
K
,都存在一个
W\u K
形状
(m1-k1+1,m2-k2+1,n\u滤波器,m1,m2,n\u通道)
,使得
W\u K
a
的张量点等于
B
。i、 e.
B=np.tensordot(wk,A,3)

我试图找到一个纯粹的NumPy解决方案,从
K
生成这个
wk
,而不使用任何python循环

我可以看到
W_K[I,j,f]==np.pad(K[…,f],((I,m1-I-k1),(j,m2-j-k2)),“常量”,常量值=0)
或者简单地
W_K[I,j,f,I:I+k1,j:j+k2]==K[…,f]

我要找的几乎和Toeplitz矩阵相似。但我需要它的多维性

循环代码中的示例:

import numpy as np

# 5x5 image with 3-channels
A = np.random.random((5,5,3))
# 2x2 Conv2D kernel with 2 filters for A  
K = np.random.random((2,2,3,2))

# It should be of (4,4,2,5,5,3), but I create this way for convenience. I move the axis at the end.
W_K = np.empty((4,4,5,5,3,2))
for i, j in np.ndindex(4, 4):
  W_K[i, j] = np.pad(K, ((i, 5-i-2),(j, 5-j-2), (0, 0), (0, 0)), 'constant', constant_values=0)

# above lines can also be rewritten as
W_K = np.zeros((4,4,5,5,3,2))
for i, j in np.ndindex(4, 4):
  W_K[i, j, i:i+2, j:j+2, ...] = K[...]

W_K = np.moveaxis(W_K, -1, 2)

# now I can do
B = np.tensordot(W_K, A, 3)

你想要的东西需要一些技巧,但编写代码不是很麻烦。我们的想法是创建四维索引数组,应用第二个循环示例的
wk[i,j,i:i+2,j:j+2,…]
部分

下面是您的示例的一个稍加修改的版本,只是为了确保某些相关维度不同(因为这使bug更容易发现:它们将是正确的错误,而不是损坏的值):

我们首先创建一个索引网格
i,j
,它跨越
W_K
的前两个维度,然后创建两个类似的网格,它跨越其(pre-
moveaxis
)的第二和第三维度。通过在前者中注入两个尾随的单态维度,我们最终得到了4d索引数组,它们共同跨越
W_K
的前四个维度

剩下的就是使用原始的
K
分配给该切片,并向后移动维度。由于高级索引在表达式中的切片(非高级)索引不是彼此相邻时会改变行为,因此使用
moveaxis
方法更容易做到这一点。我首先尝试创建带有最终维度的
W_K2
,但随后我们将创建具有微妙不同行为(特别是不同形状)的
W_K[I,j,:,I+K,j+l,…]

import numpy as np

# parameter setup
k1, k2, nch, nf = 2, 4, 3, 2 
m1, m2 = 5, 6 
w1, w2 = m1 - k1 + 1, m2 - k2 + 1 
K = np.random.random((k1, k2, nch, nf)) 
A = np.random.random((m1, m2, nch)) 

# your loopy version for comparison
W_K = np.zeros((w1, w2, nf, m1, m2, nch)) 
for i, j in np.ndindex(w1, w2): 
    W_K[i, j, :, i:i+k1, j:j+k2, ...] = K.transpose(-1, 0, 1, 2) 

W_K2 = np.zeros((w1, w2, m1, m2, nch, nf))  # to be transposed back
i,j = np.mgrid[:w1, :w2][..., None, None]  # shape (w1, w2, 1, 1) 
k,l = np.mgrid[:k1, :k2]  # shape (k1, k2) ~ (1, 1, k1, k2)  

W_K2[i, j, i+k, j+l, ...] = K 
W_K2 = np.moveaxis(W_K2, -1, 2) 

print(np.array_equal(W_K, W_K2))  # True