从卷积核生成线性运算矩阵的多种方法

时间:2019-09-17 14:46:07

标签: python numpy tensorflow vectorization convolution

形状为K的2D卷积核(k1, k2, n_channel, n_filter)应用于形状为A的2D向量(m1, m2, n_channel)并生成另一个2D向量{{1} },形状为B(具有 valid 填充)。

对于每个(m1 - k1 + 1, m2 - k2 + 1, n_filter),确实存在形状为K的{​​{1}},因此W_K(m1 - k1 + 1, m2 - k2 + 1, n_filter, m1, m2, n_channel)的张量点相等到W_K。即A

我正在尝试寻找一种纯粹的NumPy解决方案,以从B生成此B = np.tensordot(W_K, A, 3),而不使用任何python循环。

我可以看到W_K或简单地看到K

我正在寻找的东西几乎类似于Toeplitz矩阵。但是我需要多维的内容。

循环代码示例:

W_K[i,j,f] == np.pad(K[...,f], ((i,m1-i-k1), (j,m2-j-k2)), 'constant', constant_values=0)

1 个答案:

答案 0 :(得分:3)

您想要的内容需要一点fancy indexing的体操技巧,但是编写代码并不是很麻烦。这个想法是创建应用第二个循环示例的W_K[i, j, i:i+2, j:j+2, ...]部分的4维索引数组。

这里是示例的略微修改版本,只是为了确保某些相关维度有所不同(因为这使错误更易于发现:它们将是适当的错误,而不是值不正确的错误):

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

我们首先创建一个跨越i,j前两个维度的索引网格W_K,然后创建一个跨越其{pre {moveaxis)第二和第三个维度的相似网格。通过将两个尾随的单例维度注入到前者中,我们得到4d索引数组,这些数组一起跨越W_K的前四个维度。

剩下的就是使用原始的K将此切片分配给该切片,然后移回该尺寸。当表达式中的切片索引(非高级索引)不是彼此相邻时,由于高级索引如何改变行为,因此使用moveaxis方法更容易做到。我首先尝试创建具有最终尺寸的W_K2,但随后我们的W_K[i, j, :, i+k, j+l, ...]的行为却有所不同(特别是形状不同)。