二维卷积作为矩阵 - 矩阵乘法

时间:2013-05-28 18:24:39

标签: neural-network deep-learning conv-neural-network matrix-multiplication convolution

我知道,在1D情况下,两个向量ab之间的卷积可以计算为conv(a, b),但也可以计算为{{1}之间的乘积}和T_a,其中bT_a的相应Toeplitz矩阵。

是否可以将这个想法扩展到2D?

鉴于aa = [5 1 3; 1 1 2; 2 1 3],是否可以在Toeplitz矩阵中转换b=[4 3; 1 2]并计算aT_a之间的矩阵 - 矩阵乘积在一维情况下?

4 个答案:

答案 0 :(得分:27)

是的,这是可能的,您还应该使用双块循环矩阵(这是Toeplitz矩阵的特殊情况)。我将给你一个小内核和输入的例子,但是可以为任何内核构造Toeplitz矩阵。所以你有一个2d输入x和2d内核k,你想要计算卷积x * k。我们也假设k已被翻转。我们还假设x的大小为n×nkm×m

因此,您将k展开为大小为(n-m+1)^2 × n^2的稀疏矩阵,并将x展开为长向量n^2 × 1。您计算此稀疏矩阵与向量的乘法,并将得到的向量(其大小为(n-m+1)^2 × 1)转换为n-m+1方阵。

我很确定这只是从阅读中难以理解。所以这里是2×2内核和3×3输入的例子。

enter image description here * enter image description here

这是一个带矢量的构造矩阵:

enter image description here

等于enter image description here

这与通过k超过x的滑动窗口获得的效果相同。

答案 1 :(得分:6)

1-定义输入和过滤器

I 为输入信号,让 F 为滤波器或内核。

2d input signal and filter

2-计算最终输出大小

如果I为 m1 x n1 ,F为m2 x n2,则输出大小为:

enter image description here

3-对滤镜矩阵进行零填充

对滤镜进行零填充,使其与输出大小相同。

enter image description here

4-为填充零的滤镜的每一行创建Toeplitz矩阵

enter image description here

5-创建一个双重阻止的Toeplitz矩阵

现在,所有这些小的Toeplitz矩阵都应排列在一个大双阻塞的Toeplitz矩阵中。 enter image description here

enter image description here

6-将输入矩阵转换为列向量

enter image description here

7-将双重阻塞的Toeplitz矩阵与矢量化的输入信号相乘

此乘法给出了卷积结果。

8-最后一步:将结果重塑为矩阵形式

enter image description here

有关更多详细信息和python代码,请查看我的github存储库:

Step by step explanation of 2D convolution implemented as matrix multiplication using toeplitz matrices in python

答案 2 :(得分:2)

如果你将k解释为m ^ 2向量并展开X,那么你将获得:

  • m**2向量k
  • ((n-m)**2, m**2)
  • unrolled_X矩阵

其中unrolled_X可以通过以下Python代码获得:

from numpy import zeros


def unroll_matrix(X, m):
  flat_X = X.flatten()
  n = X.shape[0]
  unrolled_X = zeros(((n - m) ** 2, m**2))
  skipped = 0
  for i in range(n ** 2):
      if (i % n) < n - m and ((i / n) % n) < n - m:
          for j in range(m):
              for l in range(m):
                  unrolled_X[i - skipped, j * m + l] = flat_X[i + j * n + l]
      else:
          skipped += 1
  return unrolled_X

展开X而不是k允许比每个X的相反方式更紧凑的表示(更小的矩阵) - 但是你需要展开每个X.你可能更喜欢展开k取决于你想要做什么。

在这里,unrolled_X不是稀疏的,而unrolled_k是稀疏的,但大小为((n-m+1)^2,n^2),正如@Salvador Dali所提到的那样。

展开k可以这样做:

from scipy.sparse import lil_matrix
from numpy import zeros
import scipy 


def unroll_kernel(kernel, n, sparse=True):

    m = kernel.shape[0]
    if sparse:
         unrolled_K = lil_matrix(((n - m)**2, n**2))
    else:
         unrolled_K = zeros(((n - m)**2, n**2))

    skipped = 0
    for i in range(n ** 2):
         if (i % n) < n - m and((i / n) % n) < n - m:
             for j in range(m):
                 for l in range(m):
                    unrolled_K[i - skipped, i + j * n + l] = kernel[j, l]
         else:
             skipped += 1
    return unrolled_K

答案 3 :(得分:0)

上面显示的代码不会生成正确尺寸的展开矩阵。尺寸应为(n-k + 1)*(m-k + 1),(k)(k)。 k:过滤器维度,n:输入矩阵中的num行,m:num列。

def unfold_matrix(X, k):
    n, m = X.shape[0:2]
    xx = zeros(((n - k + 1) * (m - k + 1), k**2))
    row_num = 0
    def make_row(x):
        return x.flatten()

    for i in range(n- k+ 1):
        for j in range(m - k + 1):
            #collect block of m*m elements and convert to row
            xx[row_num,:] = make_row(X[i:i+k, j:j+k])
            row_num = row_num + 1

    return xx

有关详细信息,请参阅我的博文:

http://www.telesens.co/2018/04/09/initializing-weights-for-the-convolutional-and-fully-connected-layers/