二维卷积作为矩阵-矩阵乘法[关闭]

Posted

技术标签:

【中文标题】二维卷积作为矩阵-矩阵乘法[关闭]【英文标题】:2-D convolution as a matrix-matrix multiplication [closed] 【发布时间】:2013-05-23 19:52:17 【问题描述】:

我知道,在一维情况下,ab 两个向量之间的卷积可以计算为 conv(a, b),但也可以计算为 T_ab 之间的乘积,其中T_aa 对应的 Toeplitz 矩阵。

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

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

【问题讨论】:

见How can the convolution operation be implemented as a matrix-vector multiplication?。 我投票结束这个问题,因为它与 help center 中定义的编程无关,而是关于 ML 理论和/或方法 - 请参阅 deep-learning @ 中的介绍和注意事项987654323@. 【参考方案1】:

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

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

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

*

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

等于。

这与在x 上滑动k 得到的结果相同。

【讨论】:

最后必须进行某种重塑,对吗?最后一个向量是 4 x 1,但卷积的结果是 2 x 2 @jvans 是的,最后你应该重塑你的向量。它写在这里:将结果向量(大小为 (n-m+1)^2 X 1)转换为 n-m+1 方阵 在您的示例中,这不是 Toeplitz 矩阵。所以你的回答只是部分正确,是吗? Also let's assume that k is already flipped 是什么意思?是因为我们想要执行相关而不是卷积吗?就 numpy 操作而言,flipped 是什么? @mrgloom 是的,上面的操作是一种相关性,这就是为什么他首先垂直(倒置)翻转过滤器,然后它就等同于卷积。 numpy 是flip(m, 0),相当于flipud(m)【参考方案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 会是稀疏的,但 @Salvador Dali 提到的大小为 ((n-m+1)^2,n^2)

展开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】:

上面显示的代码不会产生正确维度的展开矩阵。维度应该是 (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/

【讨论】:

【参考方案4】:

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 存储库:

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

【讨论】:

我认为有错误。结果的第一个元素应该是 10*0 + 20*0 + 30*0 +40*1 = 40。位置 2,2 的元素应该是 1*10 + 2*20 + 4*30 + 5*40 = 370。我认为您的结果对于等于 [40 30; 的矩阵 F 是正确的; 20 10] 正好是 F 翻转行和列。因此,程序中存在错误 它是在做卷积(数学卷积,不是互相关),所以如果是手工做的话,需要垂直和水平翻转滤波器。您可以在我的 GitHub 存储库中找到更多信息。 这是对二维卷积作为矩阵运算的一个很好的解释。有没有办法代表“mode='same'”? (即保持输出形状与图像相同)? @ajl123 我认为应该是。如果我有时间我会努力的。如果您得到答案,请随时深入研究代码和数学,并在 Github 上向我发送拉取请求。 结果矩阵的维度不应该减少吗?

以上是关于二维卷积作为矩阵-矩阵乘法[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

1571:基础练习 矩阵乘法

卷积操作转化成矩阵乘法

二维数据练习--矩阵的加法和乘法

CUDNN入坑指南(0)卷积算法实现类型

[BZOJ2738]矩阵乘法 整体二分+二维树状数组

OpenCV 简单的二维矩阵乘法失败