在 MATLAB 中有效计算加权距离

Posted

技术标签:

【中文标题】在 MATLAB 中有效计算加权距离【英文标题】:Efficiently calculating weighted distance in MATLAB 【发布时间】:2016-01-19 14:07:04 【问题描述】:

Severalpostsexist 关于在 MATLAB 中有效地计算成对距离。这些帖子往往涉及快速计算大量点之间的欧式距离。

我需要创建一个函数来快速计算少量点(通常少于 1000 对)之间的成对差异。在我正在编写的程序的宏大计划中,这个函数将被执行数千次,因此即使是效率上的微小提升也很重要。该功能需要在两个方面灵活:

    在任何给定呼叫中,距离度量可以是欧几里得或城市街区。 数据的维度是加权的。

据我所知,尚未发布针对此特定问题的解决方案。统计工具箱提供pdist 和pdist2,它们接受许多不同的距离函数,但不接受加权。我见过这些允许加权的函数的扩展,但是这些扩展不允许用户选择不同的距离函数。

理想情况下,我希望避免使用统计工具箱中的函数(我不确定该函数的用户是否可以访问这些工具箱)。

我写了两个函数来完成这个任务。第一个使用对 repmat 和 permute 的棘手调用,第二个简单地使用 for 循环。

function [D] = pairdist1(A, B, wts, distancemetric)

% get some information about the data
    numA = size(A,1);
    numB = size(B,1);

    if strcmp(distancemetric,'cityblock')
        r=1;
    elseif strcmp(distancemetric,'euclidean')
        r=2;
    else error('Function only accepts "cityblock" and "euclidean" distance')
    end

%   format weights for multiplication
    wts = repmat(wts,[numA,1,numB]);

%   get featural differences between A and B pairs
    A = repmat(A,[1 1 numB]);
    B = repmat(permute(B,[3,2,1]),[numA,1,1]);
    differences = abs(A-B).^r;

%   weigh difference values before combining them
    differences = differences.*wts;
    differences = differences.^(1/r);

%   combine features to get distance
    D = permute(sum(differences,2),[1,3,2]);
end

与:

function [D] = pairdist2(A, B, wts, distancemetric)

% get some information about the data
    numA = size(A,1);
    numB = size(B,1);

    if strcmp(distancemetric,'cityblock')
        r=1;
    elseif strcmp(distancemetric,'euclidean')
        r=2;
    else error('Function only accepts "cityblock" and "euclidean" distance')
    end

%   use for-loops to generate differences
    D = zeros(numA,numB);
    for i=1:numA
        for j=1:numB
            differences = abs(A(i,:) - B(j,:)).^(1/r);
            differences = differences.*wts;
            differences = differences.^(1/r);    
            D(i,j) = sum(differences,2);
        end
    end
end

以下是性能测试:

A = rand(10,3);
B = rand(80,3);
wts = [0.1 0.5 0.4];
distancemetric = 'cityblock';


tic
D1 = pairdist1(A,B,wts,distancemetric);
toc

tic
D2 = pairdist2(A,B,wts,distancemetric);
toc

Elapsed time is 0.000238 seconds.
Elapsed time is 0.005350 seconds.

很明显,repmat-and-permute 版本比双循环版本运行得更快,至少对于较小的数据集。但我也知道,调用 repmat 通常会减慢速度。所以我想知道 SO 社区中是否有人可以提供任何建议来提高任一功能的效率!

编辑

@Luis Mendo 使用 bsxfun 对 repmat-and-permute 函数进行了很好的清理。我在不同大小的数据集上将他的函数与我原来的函数进行了比较:

随着数据变大,bsxfun 版本成为明显的赢家!

编辑#2

我已经完成了函数的编写,它可以在 github [link] 上找到。我最终找到了一种非常好的矢量化方法来计算欧几里得距离 [link],所以我在欧几里得案例中使用了该方法,并且我将 @Divakar 的 advice 用于城市街区。它仍然不如 pdist2 快,但它必须比我在本文前面介绍的任何一种方法都快,并且很容易接受权重。

【问题讨论】:

鉴于上下文 Comparing BSXFUN and REPMAT 可能值得探索。 【参考方案1】:

您可以将repmat 替换为bsxfun。这样做可以避免显式重复,因此更节省内存,并且可能更快:

function D = pairdist1(A, B, wts, distancemetric)

    if strcmp(distancemetric,'cityblock')
        r=1;
    elseif strcmp(distancemetric,'euclidean')
        r=2;
    else
        error('Function only accepts "cityblock" and "euclidean" distance')
    end

    differences  = abs(bsxfun(@minus, A, permute(B, [3 2 1]))).^r;
    differences = bsxfun(@times, differences, wts).^(1/r);
    D = permute(sum(differences,2),[1,3,2]);

end

【讨论】:

很好的建议——我特别喜欢代码少了多少。我发现对于较小的数据集(如我的示例中的数据集),这两个函数是可比的。但随着数据规模的扩大,bsxfun 变得越来越快。【参考方案2】:

对于r = 1 ("cityblock" case),您可以使用bsxfun 进行元素减法,然后使用matrix-multiplication,这必须加快速度。实现看起来像这样 -

%// Calculate absolute elementiwse subtractions
absm = abs(bsxfun(@minus,permute(A,[1 3 2]),permute(B,[3 1 2])));

%// Perform matrix multiplications with the given weights and reshape
D = reshape(reshape(absm,[],size(A,2))*wts(:),size(A,1),[]);

【讨论】:

欧几里得函数在这里是必不可少的。你能轻松扩展它吗? 这必须更快。矩阵乘法总是胜过bsxfun @LuisMendo 猜猜看!您的通用方法就是要走的路! 我认为您需要根据矩阵大小在第二行更改 3 @LuisMendo 谢谢!已编辑。

以上是关于在 MATLAB 中有效计算加权距离的主要内容,如果未能解决你的问题,请参考以下文章

MATLAB计算数据各种距离矩阵(欧式距离加权欧式距离...)

MATLAB如何使计算结果保留4位有效数字

在红宝石中计算汉明距离的最有效方法?

matlab练习程序(局部加权线性回归)

如何在Oracle中有效地计算坐标之间的距离

knn算法中计算距离而不是欧几里得距离的替代有效方法