MATLAB:块矩阵乘法无循环

时间:2014-01-25 23:38:43

标签: matlab vectorization matrix-multiplication transpose scientific-computing

我有一个块矩阵[A B C...]和一个矩阵 D (全部是二维的)。 D 的维度为y-by-y,而 A,B,C 等均为z-by-y。基本上,我想要计算的是矩阵[D*(A'); D*(B'); D*(C');...],其中 X '指的是 X 的转置。但是,我希望在没有循环的情况下实现此目的以提高速度。

我一直在玩reshape命令几个小时了,我知道如何在其他情况下使用它,但这个用例与其他用例不同,我无法弄明白。如果可能的话,我也想避免使用多维矩阵。

3 个答案:

答案 0 :(得分:3)

老实说,循环可能是最好的方法。在我的图像处理工作中,我发现一个编写良好的循环,利用Matlab的JIT编译器通常更快比操纵数据的所有额外开销能够使用矢量化操作。像这样的循环:

[m n] = size(A);
T = zeros(m, n);
AT = A';
for ii=1:m:n
    T(:, ii:ii+m-1) = D * AT(ii:ii+m-1, :);
end

只包含内置运算符和最小的复制,并且考虑到JIT将很难被击败。即使您想要考虑解释器开销,它仍然只是一个没有要考虑的函数的语句。

带有额外faffing和内存复制的“无循环”版本是split the matrix并使用a hidden loop遍历块:

blksize = size(D, 1);
blkcnt = size(A, 2) / blksize;
blocks = mat2cell(A, blksize, repmat(blksize,1,blkcnt));
blocks = cellfun(@(x) D*x', blocks, 'UniformOutput', false);
T = cell2mat(blocks);

当然,如果您可以访问图像处理工具箱,还可以cheat horribly

T = blockproc(A, size(D), @(x) D*x.data');

答案 1 :(得分:1)

前瞻性方法&解决方案代码

假设:

  • M是块矩阵[A B C...],其中每个ABC等的大小为z x y。让这些矩阵的数量为num_mat,以便日后参考。 如果这些矩阵在列中连接,则M的大小为z x num_mat*y
  • D是要与每个矩阵ABC等相乘的矩阵,其大小为y x y

现在,正如问题中所述,您所追求的输出是[D*(A'); D*(B'); D*(C');...],即乘法结果沿着行连接。 如果您可以将这些乘法结果串联起来,即[D*(A') D*(B') D*(C') ...], 你可以通过一些重塑然后执行相同的方式来实现相同的目标 整数MD的矩阵乘法,因此具有矢量化无循环方法。因此,要获得这样的矩阵乘法结果,您可以 -

mults = D*reshape(permute(reshape(M,z,y,[]),[2 1 3]),y,[]);

但是,如果你必须得到一个输出,并且乘法结果沿rows连接,你需要做更多的重塑 -

out = reshape(permute(reshape(mults,y,z,[]),[1 3 2]),[],z);

基准

本节介绍基准代码,将建议的矢量化方法与天真的 JIT驱动的循环方法进行比较,以获得所需的输出。如前所述,根据输出数组必须如何保存乘法结果,您可以有两种情况。

案例I:沿着列连接的乘法结果

%// Define size paramters and then define random inputs with those
z = 500; y = 500; num_mat = 500;
M = rand(z,num_mat*y);
D = rand(y,y);

%// Warm up tic/toc.
for k = 1:100000
    tic(); elapsed = toc();
end

disp('---------------------------- With loopy approach')
tic
out1 = zeros(z,y*num_mat);
for k1 = 1:y:y*num_mat
    out1(:,k1:k1+y-1) = D*M(:,k1:k1+y-1).'; %//'
end
toc, clear out1 k1

disp('---------------------------- With proposed approach')
tic
mults = D*reshape(permute(reshape(M,z,y,[]),[2 1 3]),y,[]);
toc

案例II:沿着行连接的乘法结果

%// Define size paramters and then define random inputs with those
z = 500; y = 500; num_mat = 500;
M = rand(z,num_mat*y);
D = rand(y,y);

%// Warm up tic/toc.
for k = 1:100000
    tic(); elapsed = toc();
end

disp('---------------------------- With loopy approach')
tic
out1 = zeros(y*num_mat,z);
for k1 = 1:y:y*num_mat
    out1(k1:k1+y-1,:) = D*M(:,k1:k1+y-1).'; %//'
end
toc, clear out1 k1

disp('---------------------------- With proposed approach')
tic
mults = D*reshape(permute(reshape(M,z,y,[]),[2 1 3]),y,[]);
out2 = reshape(permute(reshape(mults,y,z,[]),[1 3 2]),[],z);
toc

<强>运行时

案例I:

---------------------------- With loopy approach
Elapsed time is 3.889852 seconds.
---------------------------- With proposed approach
Elapsed time is 3.051376 seconds.

案例II:

---------------------------- With loopy approach
Elapsed time is 3.798058 seconds.
---------------------------- With proposed approach
Elapsed time is 3.292559 seconds.

<强>结论

运行时通过提出的矢量化方法提出了一个很好的25% speedup!所以,希望这对你有用!

答案 2 :(得分:0)

如果你想从更大的矩阵得到A,B和C,你可以这样做,假设更大的矩阵被称为X:

A = X(:,1:y)
B = X(:,y+1:2*y)
C = X(:,2*y+1:3*y)

如果有N个这样的矩阵,最好的方法是使用重塑形式:

F =重塑(X,x,y,N)

然后使用循环生成一个新的矩阵我将其称为F1:

F1=[];
for n=1:N
   F1 = [F1 F(:,:,n)'];
end

然后将F2计算为:

F2 = D*F1;

最后得到你的结果:

R = reshape(F2,N*y,x)

注意:这个for循环不会减慢你的速度,因为它只是重新格式化矩阵,而乘法是以矩阵形式完成的。