【问题标题】:Optimize/ Vectorize Mahalanobis distance calculations in MATLAB在 MATLAB 中优化/矢量化马氏距离计算
【发布时间】:2015-03-22 13:53:25
【问题描述】:

我有以下一段 Matlab 代码,它通过多次迭代在向量和矩阵之间计算 Mahalanobis distances。我正在尝试通过矢量化找到一种更快的方法,但没有成功。

S.data=0+(20-0).*rand(15000,3);
S.a=0+(20-0).*rand(2500,3);

S.resultat=ones(length(S.data),length(S.a))*nan;
S.b=ones(length(S.a),3,length(S.a))*nan;

for i=1:length(S.data)
    for j=1:length(S.a)
         S.a2=S.a;
         S.a2(j,:)=S.data(i,:);
         S.b(:,:,j)=S.a2;
           if j==length(S.a)
              for k=1:length(S.a);
                   S.resultat(i,k)=mahal(S.a(k,:),S.b(:,:,k));
              end
           end    
    end   
end

我现在已经修改了代码并避免了其中一个循环。但它仍然很长。如果有人有想法,我将非常感激!

S.data=0+(20-0).*rand(15000,3);
S.a=0+(20-0).*rand(2500,3);

S.resultat=ones(length(S.data),length(S.a))*nan;
   for i=1:length(S.data)
       for j=1:length(S.a)
       S.a2=S.a;
       S.a2(j,:)=S.data(i,:);
       S.resultat(i,j)=mahal(S.a(j,:),S.a2);    
       end   
   end

【问题讨论】:

  • 那个函数mahal是什么?
  • 如果不知道mahal 函数的作用,将很难提供任何帮助。 mahal 可以对数组进行操作吗?另外,S.bS.a 的大小是多少?
  • 您是否正在计算一系列不同向量 a 和矩阵 B 的马氏距离 a'*inv(B)*a?
  • 另外,您的结果似乎并不依赖于循环变量 i
  • S.b 如何依赖于idata的作用是什么?

标签: performance matlab optimization matrix vectorization


【解决方案1】:

介绍及解决方案代码

您可以将使用 mahal 的最内层循环替换为 bit 矢量化的内容,因为它使用了一些预先计算的值(借助 @ 987654323@) 在mahal 的循环缩短和破解版本中。

基本上你有一个2D 数组,我们称它为A 以方便参考和一个3D 数组,我们称它为B。让输出存储到变量out 中。因此,可以提取最里面的代码 sn-p 并基于假定的变量名称。

原始循环代码

for k=1:size(A,1)
    out(k)=mahal(A(k,:),B(:,:,k));
end

所以,我所做的是侵入mahal.m 并寻找当输入为2D3D 时可以向量化的部分。现在,mahal 在里面使用了qr,它不能被矢量化。因此,我们最终得到了一个被黑代码。

被黑代码

%// Pre-calculate certain values that could be avoided than using into loop
meanB = mean(B,1); %// mean of B along dim-1
B_meanB = bsxfun(@minus,B,meanB); %// B minus mean values of B
A_B_meanB = A' - reshape(meanB,size(B,2),[]); %//'# A minus B_meanB 

%// QR calculations in a for-loop starts until the output is obtained
for k = 1:size(A,1)
    [~,R] = qr(B_meanB(:,:,k),0);
    out2(k) = sum((R'\A_B_meanB(:,k)).^2)*(size(A,1)-1);
end

现在,要将这个 hack 解决方案扩展到问题代码,可以引入更多的调整来预先计算那些嵌套循环使用的更多值。

最终解决方案代码

A = S.a; %// Get data from S
[rx,cx] = size(A); %// Get size parameters
Atr = A'; %//'# Pre-calculate transpose of A

%// Pre-calculate replicated B and the indices to be modified at each iteration
B_rep = repmat(S.a,1,1,rx);
B_idx = bsxfun(@plus,[(0:cx-1)*rx + 1]',[0:rx-1]*(rx*cx+1)); %//'

out = zeros(size(S.data,1),rx); %// initialize output array
for i=1:length(S.data)

    B = B_rep;
    B(B_idx) = repmat(S.data(i,:)',1,rx); %//'
    meanB = mean(B,1); %// mean of B along dim-1

    B_meanB = bsxfun(@minus,B,meanB); %// B minus mean values of B
    A_B_meanB = Atr - reshape(meanB,3,[]); %// A minus B_meanB
    for jj = 1:rx
        [~,R] = qr(B_meanB(:,:,jj),0);
        out(i,jj) = sum((R'\A_B_meanB(:,jj)).^2)*(rx-1); %//'
    end

end
S.resultat = out;

基准测试

这是用于将建议的解决方案与问题中列出的代码进行比较的基准测试代码 -

%// Random inputs
S.data=0+(20-0).*rand(1500,3); %(size 10x reduced for a quicker runtime test)
S.a=0+(20-0).*rand(250,3);

S.resultat=ones(length(S.data),length(S.a))*nan;
disp('----------------------------- With original code')
tic

S.b=ones(length(S.a),3,length(S.a))*nan;
for i=1:length(S.data)
    for j=1:length(S.a)
        S.a2=S.a;
        S.a2(j,:)=S.data(i,:);
        S.b(:,:,j)=S.a2;
        if j==length(S.a)
            for k=1:length(S.a);
                S.resultat(i,k)=mahal(S.a(k,:),S.b(:,:,k));
            end
        end
    end
end

toc, clear i j S.a2 k S.resultat

S.resultat=ones(length(S.data),length(S.a))*nan;
disp('----------------------------- With proposed solution code')
tic

[ ... Proposed solution code ...]

toc

运行时 -

----------------------------- With original code
Elapsed time is 17.734394 seconds.
----------------------------- With proposed solution code
Elapsed time is 6.602860 seconds.

因此,我们可能会通过建议的方法和一些调整来解决 2.7x 加速!

【讨论】:

  • @Divakar - 感谢您的建议。我现在试试看!
  • @Divakar - 是的!即使我使用修改后的代码,我也有与您相同的运行时结果。您的解决方案运行得非常快!更长的代码,更多的循环,但更快……有时 Matlab 很奇怪……矢量化是个好主意!
  • @Stéphane 今天听到的最甜蜜的事!那么函数内联是加速 MATLAB 中的事情的工具之一,正如这里所做的那样。如果你开始玩它,它看起来不再奇怪了:)
  • @Stéphane 做了一些调整!看看吧。
  • @Divakar - 很抱歉我的回复晚了,直到现在我都没有连接。非常感谢您的帮助,我运行了您之前的修改并且效果很好(加速非常令人印象深刻)。我刚刚尝试了你的新方法,但我在执行它时遇到了一些困难(肯定是因为我是函数内联的新手)。当我应用您的代码时,Matlab 警告我 repmat 存在问题(输入参数过多)来创建变量 'B_rep' .... 对不起,我觉得白痴...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-16
  • 1970-01-01
  • 2016-04-21
相关资源
最近更新 更多