57

我知道,在 1D 情况下,两个向量a和之间的卷积b可以计算为,也可以计算为和conv(a, b)之间的乘积,其中是对应的 Toeplitz 矩阵。T_abT_aa

是否有可能将这个想法扩展到二维?

给定a = [5 1 3; 1 1 2; 2 1 3]b=[4 3; 1 2],是否可以a在 Toeplitz 矩阵中进行转换并计算 和 之间的矩阵-矩阵乘积T_ab就像在一维情况下一样?

4

4 回答 4

56

是的,这是可能的,您还应该使用双块循环矩阵(这是Toeplitz矩阵的特例)。我会给你一个小尺寸内核和输入的例子,但是可以为任何内核构造 Toeplitz 矩阵。所以你有一个二维输入x和二维内核k,你想计算卷积x * k。还让我们假设k已经翻转。我们还假设x是 大小n×n并且km×m

所以你展开k成一个稀疏的大小矩阵(n-m+1)^2 × n^2,然后展开x成一个长向量n^2 × 1。您计算此稀疏矩阵与向量的乘法,并将结果向量(将具有 size (n-m+1)^2 × 1)转换为n-m+1方阵。

我很确定这很难仅仅通过阅读来理解。所以这里是一个 2×2 内核和 3×3 输入的例子。

在此处输入图像描述*在此处输入图像描述

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

在此处输入图像描述

等于在此处输入图像描述

这与你通过滑动窗口得到的结果相同kover x

于 2017-05-18T05:23:15.703 回答
33

1-定义输入和过滤器

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

二维输入信号和滤波器

2-计算最终输出大小

如果 I 是m1 x n1并且 F 是 m2 x n2 输出的大小将是:

在此处输入图像描述

3- 对滤波器矩阵进行零填充

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

在此处输入图像描述

4- 为补零滤波器的每一行创建 Toeplitz 矩阵

在此处输入图像描述

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

现在所有这些小的 Toeplitz 矩阵都应该排列在一个大的双重阻塞 Toeplitz 矩阵中。 在此处输入图像描述

在此处输入图像描述

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

在此处输入图像描述

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

这个乘法给出了卷积结果。

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

在此处输入图像描述

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

在 python 中使用 toeplitz 矩阵将 2D 卷积实现为矩阵乘法的逐步解释

于 2018-08-15T19:52:20.207 回答
3

如果您将 k 解开为 am^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
于 2017-07-22T22:00:02.747 回答
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/

于 2018-04-09T20:54:26.703 回答