【问题标题】:Vectorization and Nested Matrix Multiplication向量化和嵌套矩阵乘法
【发布时间】:2016-07-28 15:29:40
【问题描述】:

这里是原始代码:

K = zeros(N*N)
for a=1:N
    for i=1:I
        for j=1:J
            M = kron(X(:,:,a).',Y(:,:,a,i,j));

            %A function that essentially adds M to K. 
        end
    end
end

目标是向量化 kroniker 乘法调用。我的直觉是将 X 和 Y 视为矩阵的容器(作为参考,提供给 kron 的 X 和 Y 的切片是 7x7 阶的方阵)。在此容器方案下,X 显示为 1-D 容器,Y 显示为 3-D 容器。我的下一个猜测是将 Y 重塑为 2-D 容器,或者更好的是 1-D 容器,然后对 X 和 Y 进行元素乘法。问题是:如何以保留 M 和轨迹的方式进行这种重塑matlab 甚至可以在这个容器的想法中处理这个想法,还是容器需要进一步重塑以进一步暴露内部矩阵元素?

【问题讨论】:

    标签: matlab vectorization matrix-multiplication


    【解决方案1】:

    方法#1:矩阵乘法与6D permute

    % Get sizes
    [m1,m2,~] =  size(X);
    [n1,n2,N,n4,n5] =  size(Y);
    
    % Lose the third dim from X and Y with matrix-multiplication
    parte1 = reshape(permute(Y,[1,2,4,5,3]),[],N)*reshape(X,[],N).';
    
    % Rearrange the leftover dims to bring kron format
    parte2 = reshape(parte1,[n1,n2,I,J,m1,m2]);
    
    % Lose dims correspinding to last two dims coming in from Y corresponding
    % to the iterative summation as suggested in the question
    out = reshape(permute(sum(sum(parte2,3),4),[1,6,2,5,3,4]),m1*n1,m2*n2)
    

    方法 #2:简单的7D 置换

    % Get sizes
    [m1,m2,~] =  size(X);
    [n1,n2,N,n4,n5] =  size(Y);
    
    % Perform kron format elementwise multiplication betwen the first two dims
    % of X and Y, keeping the third dim aligned and "pushing out" leftover dims
    % from Y to the back
    mults = bsxfun(@times,permute(X,[4,2,5,1,3]),permute(Y,[1,6,2,7,3,4,5]));
    
    % Lose the two dims with summation reduction for final output
    out = sum(reshape(mults,m1*n1,m2*n2,[]),3);
    

    验证

    这是运行原始方法和建议方法的设置 -

    % Setup inputs
    X = rand(10,10,10);
    Y = rand(10,10,10,10,10);
    
    % Original approach
    [n1,n2,N,I,J] =  size(Y);
    K = zeros(100);
    for a=1:N
        for i=1:I
            for j=1:J
                M = kron(X(:,:,a).',Y(:,:,a,i,j));
                K = K + M;
            end
        end
    end
    
    % Approach #1
    [m1,m2,~] =  size(X);
    [n1,n2,N,n4,n5] =  size(Y);
    mults = bsxfun(@times,permute(X,[4,2,5,1,3]),permute(Y,[1,6,2,7,3,4,5]));
    out1 = sum(reshape(mults,m1*n1,m2*n2,[]),3);
    
    % Approach #2
    [m1,m2,~] =  size(X);
    [n1,n2,N,n4,n5] =  size(Y);
    parte1 = reshape(permute(Y,[1,2,4,5,3]),[],N)*reshape(X,[],N).';
    parte2 = reshape(parte1,[n1,n2,I,J,m1,m2]);
    out2 = reshape(permute(sum(sum(parte2,3),4),[1,6,2,5,3,4]),m1*n1,m2*n2);
    

    运行后,我们看到最大值。建议的方法与原始方法的绝对偏差 -

    >> error_app1 = max(abs(K(:)-out1(:)))
    error_app1 =
       1.1369e-12
    >> error_app2 = max(abs(K(:)-out2(:)))
    error_app2 =
       1.1937e-12
    

    价值观对我来说很好!


    基准测试

    使用与验证相同的大数据集对这三种方法进行计时,我们会得到这样的结果 -

    ----------------------------- With Loop
    Elapsed time is 1.541443 seconds.
    ----------------------------- With BSXFUN
    Elapsed time is 1.283935 seconds.
    ----------------------------- With MATRIX-MULTIPLICATION
    Elapsed time is 0.164312 seconds.
    

    似乎矩阵乘法对于这些大小的数据集表现得相当好!

    【讨论】:

    • 明确一点,在这两种新方法中,out1 和 out2 都替换了 K?如果是这样,在K=K+M更复杂的情况下,是否可以体现这一点?例如,在原始示例中,如果每个 M 都被一个随着循环的每次迭代而变化的常数归一化怎么办?第二个例子是,如果 M 在添加到 K 之前应用了一个布尔掩码。这些额外的步骤会破坏输出吗?
    • @MikeVandenberg 对于 ex1,作为预处理步骤使用:Y = bsxfun(@times,Y,permute(scale,[4,5,1,2,3]));,然后使用建议的代码,其中 scale 是大小为 (N,I,J) 的缩放矩阵。对于 ex2,您会为每次迭代使用不同的掩码还是相同的掩码?
    • 我想我不应该对这些步骤过于简单化,因为它们并非微不足道。这是构造 K 的特定函数:pr = real(trace(E*M)); K = K + H(i,j,a)*M/pr; 其中 H 是事先已知的矩阵。所以是的,在每次迭代中,我们都在提取 H 的不同切片并用不同的常数对 M 进行归一化,该常数是乘积 E*M 的迹,其中 E 是布尔掩码。
    • @MikeVandenberg 是的,这就是矢量化的问题,你不能指望一概而论。他们主要根据具体情况工作。祝你好运!
    • 嘿,过去几天我一直在与此作斗争,如果不完全破坏输出,似乎无法取得任何进展。我特别试图修改矩阵乘法方法。您会建议改用 bsxfun 方法吗?无论如何,我大部分时间都被公关部门困住了。我们如何在不实际迭代的情况下提取每个切片的轨迹?