二维卷积作为矩阵-矩阵乘法[关闭]
Posted
技术标签:
【中文标题】二维卷积作为矩阵-矩阵乘法[关闭]【英文标题】:2-D convolution as a matrix-matrix multiplication [closed] 【发布时间】:2013-05-23 19:52:17 【问题描述】:我知道,在一维情况下,a
和 b
两个向量之间的卷积可以计算为 conv(a, b)
,但也可以计算为 T_a
和 b
之间的乘积,其中T_a
是 a
对应的 Toeplitz 矩阵。
是否可以将这个想法扩展到二维?
给定a = [5 1 3; 1 1 2; 2 1 3]
和b=[4 3; 1 2]
,是否可以将a
转换为Toeplitz 矩阵并计算T_a
和b
之间的矩阵乘积,就像一维情况一样?
【问题讨论】:
见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 上向我发送拉取请求。 结果矩阵的维度不应该减少吗?以上是关于二维卷积作为矩阵-矩阵乘法[关闭]的主要内容,如果未能解决你的问题,请参考以下文章