【问题标题】:Understanding behaviour of MATLAB's convn了解 MATLAB 的 convn 的行为
【发布时间】:2015-03-07 07:15:20
【问题描述】:

我正在对一些张量进行卷积。

这是 MATLAB 中的小测试:

    ker= rand(3,4,2);
    a= rand(5,7,2);
    c=convn(a,ker,'valid');
    c11=sum(sum(a(1:3,1:4,1).*ker(:,:,1)))+sum(sum(a(1:3,1:4,2).*ker(:,:,2)));
    c(1,1)-c11  % not equal!

第三行与convn 进行N-D 卷积,我想将convn 的第一行第一列的结果与手动计算值进行比较。但是,与convn 相比,我的计算并不相等。

那么 MATLAB 的 convn 背后是什么?难道我对张量卷积的理解是错误的?

【问题讨论】:

    标签: matlab signal-processing convolution


    【解决方案1】:

    几乎说对了。您的理解有两点略有错误:

    1. 您选择valid 作为卷积标志。这意味着从卷积返回的输出具有其大小,因此当您使用内核扫描矩阵时,它必须舒适地适合矩阵本身。因此,返回的第一个“有效”输出实际上是用于矩阵的位置 (2,2,1) 的计算。这意味着您可以在此位置舒适地安装内核,这对应于输出的位置(1,1)。为了演示,这就是 aker 使用上面的代码对我来说的样子:

      >> a
      
      a(:,:,1) =
      
      0.9930    0.2325    0.0059    0.2932    0.1270    0.8717    0.3560
      0.2365    0.3006    0.3657    0.6321    0.7772    0.7102    0.9298
      0.3743    0.6344    0.5339    0.0262    0.0459    0.9585    0.1488
      0.2140    0.2812    0.1620    0.8876    0.7110    0.4298    0.9400
      0.1054    0.3623    0.5974    0.0161    0.9710    0.8729    0.8327
      
      
      a(:,:,2) =
      
      0.8461    0.0077    0.5400    0.2982    0.9483    0.9275    0.8572
      0.1239    0.0848    0.5681    0.4186    0.5560    0.1984    0.0266
      0.5965    0.2255    0.2255    0.4531    0.5006    0.0521    0.9201
      0.0164    0.8751    0.5721    0.9324    0.0035    0.4068    0.6809
      0.7212    0.3636    0.6610    0.5875    0.4809    0.3724    0.9042
      
      >> ker
      
      ker(:,:,1) =
      
      0.5395    0.4849    0.0970    0.3418
      0.6263    0.9883    0.4619    0.7989
      0.0055    0.3752    0.9630    0.7988
      
      
      ker(:,:,2) =
      
      0.2082    0.4105    0.6508    0.2669
      0.4434    0.1910    0.8655    0.5021
      0.7156    0.9675    0.0252    0.0674
      

      如您所见,在矩阵a 中的位置(2,2,1) 处,ker 可以舒适地放入矩阵内,如果您从卷积中回忆,它只是内核和位置(2,2,1) 处的矩阵子集与您的内核大小相同(实际上,您需要对内核执行其他操作,我将为我的下一点保留 - 见下文)。因此,您计算的系数实际上是(2,2,1) 的输出,而不是(1,1,1) 的输出。虽然从它的要点来看,你已经知道了,但我想把它放在那里以防你不知道。

    2. 您忘记了对于 N-D 卷积,您需要在每个维度中翻转掩码。如果您还记得一维卷积,那么掩码必须水平翻转。我所说的翻转是指您只需将元素以相反的顺序放置。例如[1 2 3 4] 的数组将变为[4 3 2 1]。在 2D 卷积中,您必须水平和垂直翻转。因此,您将获取矩阵的每一行并将每一行以相反的顺序放置,就像一维情况一样。在这里,您会将每一行视为一维信号并进行翻转。完成此操作后,您将获取此翻转结果,并将每个视为一维信号并再次进行翻转。

      现在,对于 3D,您必须水平、垂直和暂时翻转。这意味着您需要独立地对矩阵的每个切片执行 2D 翻转,然后以 3D 方式抓取单列并将其视为 1D 信号。在 MATLAB 语法中,您会得到 ker(1,1,:),将其视为一维信号,然后翻转。您可以对ker(1,2,:)ker(1,3,:) 等重复此操作,直到完成第一片。请记住,我们不会转到第二个切片或任何其他切片并重复我们刚刚所做的事情。因为您正在获取矩阵的 3D 部分,所以您本质上是对提取的每个 3D 列的所有切片进行操作。因此,只需查看矩阵的第一片,因此您需要在计算卷积之前对内核执行此操作:

      ker_flipped = flipdim(flipdim(flipdim(ker, 1), 2), 3);
      

      flipdim 在指定轴上执行翻转。在我们的例子中,我们是垂直执行,然后获取结果并水平执行,然后再次暂时执行。然后,您将在求和中使用ker_flipped。请注意,翻转的顺序无关紧要。 flipdim 对每个维度独立操作,所以只要记得翻转所有维度,输出都是一样的。


    为了演示,下面是convn 的输出:

    c =
    
        4.1837    4.1843    5.1187    6.1535
        4.5262    5.3253    5.5181    5.8375
        5.1311    4.7648    5.3608    7.1241
    

    现在,要手动确定 c(1,1) 是什么,您需要在 flipped 内核上进行计算:

    ker_flipped = flipdim(flipdim(flipdim(ker, 1), 2), 3);
    c11 = sum(sum(a(1:3,1:4,1).*ker_flipped(:,:,1)))+sum(sum(a(1:3,1:4,2).*ker_flipped(:,:,2)));
    

    我们得到的输出是:

    c11 =
    
        4.1837
    

    如您所见,这验证了我们在 MATLAB 中使用 convn 进行的计算,从而验证了我们手动得到的结果。如果您想比较更多位的精度,请使用format long 并同时比较它们:

    >> format long;
    >> disp(c11)
    
       4.183698205668000
    
    >> disp(c(1,1))
    
       4.183698205668001
    

    如您所见,所有数字都相同,除了最后一位。这归因于数字四舍五入。绝对确定:

    >> disp(abs(c11 - c(1,1)));
    
       8.881784197001252e-16
    

    ...我认为一个订单或 10-16 的差异足以让我证明它们是相等的,对吧?

    【讨论】:

    • 非常彻底的答案!
    • @mrgloom - 是的,这几乎解释了 2D 蒙版!请记住,对于 3D,您还需要进行时间反射。祝你好运!
    • 我试过 c=convn(a,reshape(ker(end:-1:1), size(ker)),'valid');对于 3-dim 矩阵,它可以工作。
    • @mrgloom - 这也有效。 reshape 部分将进行 2D 翻转。 end:-1:1 将执行时间翻转。这是一种非常好的方法!
    【解决方案2】:

    是的,你对卷积的理解是错误的。您的 c11 公式不是卷积:您只是将匹配索引相乘然后相加。它更像是一个点积运算(在修剪到相同大小的张量上)。我将尝试从 1 维开始解释。

    一维数组

    输入conv([4 5 6], [2 3]) 返回[8 22 27 18]。我发现从多项式乘法的角度来考虑这一点最容易:

    (4+5x+6x^2)*(2+3x) = 8+22x+27x^2+18x^3

    将每个数组的条目用作多项式的系数,将多项式相乘,收集相似项,然后从系数中读取结果。 x 的幂是用来跟踪乘法和加法的。请注意,x^n 的系数在第 (n+1) 个条目中找到,因为 x 的幂以 0 开头,而索引以 1 开头。

    二维数组

    输入conv2([2 3; 3 1], [4 5 6; 0 -1 1]) 返回矩阵

     8  22  27  18
    12  17  22   9
     0  -3   2   1
    

    同样,这可以解释为多项式的乘法,但现在我们需要两个变量:例如 x 和 y。 x^n y^m 的系数在 (m+1, n+1) 项中找到。上面的输出意味着

    (2+3x+3y+xy)*(4+5x+6x^2+0y-xy+x^2y) = 8+22x+27x^2+18x^3+12y+17xy+22x^2y +9x^3y-3xy^2+2x^2y^2+x^3y^2

    3 维数组

    同样的故事。您可以将条目视为变量 x,y,z 中多项式的系数。多项式相乘,乘积的系数是卷积的结果。

    '有效'参数

    这仅保留卷积的中心部分:第二个因素的所有项参与的那些系数。要使其为非空,第二个数组的维度应不大于第一个。 (这与默认设置不同,默认设置与卷积数组的顺序无关。)示例:

    conv([4 5 6], [2 3]) 返回[22 27](与上面的一维示例相比)。这对应于在

    (4+5x+6x^2)*(2+3x) = 8+22x+27x^2+18x^3

    粗体字得到了 2 和 3x 的贡献。

    【讨论】:

    • 有趣!我喜欢这种多项式解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 2014-11-21
    • 1970-01-01
    相关资源
    最近更新 更多