寻找成对欧氏距离的快速算法(距离矩阵)

时间:2013-07-21 21:49:50

标签: matlab vectorization linear-algebra

我知道matlab有一个内置的pdist函数,可以计算成对距离。但是,我的矩阵非常大,60000乘300和matlab内存不足。

这个问题是对Matlab euclidean pairwise square distance function的跟进。

这种计算效率低下是否有任何解决方法?我尝试手动编码成对距离计算,通常需要一整天才能运行(有时需要6到7个小时)。

非常感谢任何帮助!

3 个答案:

答案 0 :(得分:6)

嗯,我无法抗拒玩耍。我创建了一个名为mex C file的Matlab pdistc,它实现了单精度和双精度的成对欧几里德距离。在使用Matlab R2012b和R2015a的机器上,对于大输入(例如,60,000乘300),它比pdist(以及基础pdistmex辅助函数)快20-25%。

正如已经指出的那样,这个问题从根本上受到记忆的限制,你要求的很多。我的mex C代码使用了超出输出所需内存的最小内存。在将其内存使用情况与pdist的内存使用情况进行比较时,看起来两者实际上是相同的。换句话说,pdist没有使用大量额外的内存。你的内存问题可能出现在调用pdist之前用完的内存中(你可以使用clear删除任何大型数组吗?)或仅仅是因为你试图解决小型硬件上的大计算问题。

因此,我的pdistc函数可能无法保存整体内存,但您可以使用我内置的其他功能。您可以计算整个成对距离向量的块。像这样:

m = 6e3;
n = 3e2;
X = rand(m,n);
sz = m*(m-1)/2;

for i = 1:m:sz-m
    D = pdistc(X', i, i+m); % mex C function, X is transposed relative to pdist
    ...                     % Process chunk of pairwise distances
end

这个速度相当慢(10次左右)并且我的C代码的这一部分没有很好地优化,但它将允许更少的内存使用 - 假设您不需要一次完整的数组。请注意,您可以使用pdist(或pdistc)更有效地执行相同操作,方法是创建一个直接传递X子集的循环,而不是全部传递。{/ p>

如果您有64位Intel Mac,则无需编译,因为我已包含.mexmaci64二进制文件,但您需要弄清楚如何编译机器的代码。我无法帮助你。您可能无法编译它,或者您可能需要通过自己编辑代码来解决兼容性问题。也有可能存在错误,代码将崩溃Matlab。另请注意,相对于pdist,您可能会得到略有不同的输出,两者之间的差异在机器epsilon(eps)的范围内。 pdist可能会或可能不会做出花哨的事情来避免大​​输入和其他数字问题的溢出,但要注意我的代码没有。

此外,我创建了一个简单的pure Matlab implementation。它比mex代码慢很多,但仍然比天真的实现或pdist中的代码更快。

所有文件can be found here。 ZIP存档包含所有文件。它是BSD许可的。随意优化(我在C代码中尝试了BLAS调用和OpenMP无济于事 - 也许一些指针魔法或GPU / OpenCL可以进一步加快速度)。我希望它对你或其他人有所帮助。

答案 1 :(得分:5)

在我的系统上,以下是最快的(甚至比@horchler的C代码pdistc更快):

function [ mD ] = CalcDistMtx ( mX )    
  vSsqX = sum(mX .^ 2);
  mD = sqrt(bsxfun(@plus, vSsqX.', vSsqX) - (2 * (mX.' * mX)));       
end

我认为你需要一个非常好的C代码才能击败它。

<强>更新
由于MATLAB R2016b MATLAB supports implicit broadcasting没有使用bsxfun()

因此可以编写代码:

function [ mD ] = CalcDistMtx ( mX )    
  vSsqX = sum(mX .^ 2, 1);
  mD = sqrt(vSsqX.'+ vSsqX - (2 * (mX.' * mX)));       
end

我的Calculate Distance Matrix project中提供了概括。

P上。 S.
使用MATLAB的pdist进行比较:squareform(pdist(mX.'))相当于CalcDistMtx(mX) 即输入应该被转置。

答案 2 :(得分:0)

计算机不是无限大,也不是无限快。人们认为他们有很多内存,快速的CPU,所以他们只是创造了越来越大的问题,然后最终想知道为什么他们的问题运行缓慢。事实是,这不是计算效率低下的问题。它只是一个过载的CPU。

正如Oli在评论中指出的那样,即使假设您只计算距离矩阵的上半部分或下半部分,也可以计算2e9值。 (6e4 ^ 2/2约为2e9。)这将需要大约16千兆字节的RAM来存储,假设在内存中只创建了一个数组的副本。如果您的代码很草率,那么您可能很容易将其加倍或加倍。一旦你进入虚拟内存,事情会变慢。

想要一个快速运行的大问题是不够的。为了真正帮助您,我们需要知道可用的RAM量。这是虚拟内存问题吗?您是否在可以处理所有所需RAM的CPU上使用64位MATLAB?