用于查找成对欧几里得距离(距离矩阵)的快速算法



我知道matlab有一个内置的pdist函数,可以计算成对距离。但是,我的矩阵太大了,以至于它的 60000 x 300 和 matlab 内存不足。

这个问题是 Matlab 欧几里得成对平方距离函数的后续问题。

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

任何帮助将不胜感激!

好吧,我忍不住玩了。我创建了一个名为 pdistc 的 Matlab mex C 文件,该文件实现了单精度和双精度的成对欧几里得距离。在我使用 Matlab R2012b 和 R2015a 的机器上,对于大输入(例如,60,000 x 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子集,而不是全部。

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

此外,我还创建了一个简单的纯 Matlab 实现。它比 mex 代码慢得多,但仍然比朴素的实现或 pdist 中的代码更快。

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

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

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

我认为,你需要一个经过良好调整的 C 代码来解决这个问题。

更新
由于 MATLAB R2016b MATLAB 支持隐式广播,无需使用bsxfun()

因此,可以编写代码:

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

在我的计算距离矩阵项目中给出了一个概括。


附言使用 MATLAB 的pdist进行比较:squareform(pdist(mX.')) 等同于 CalcDistMtx(mX)
即输入应该被转置。

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

正如 Oli 在评论中指出的那样,即使假设您只计算距离矩阵的上半部分或下半部分,也有大约 2e9 的值需要计算。(6e4^2/2 大约是 2e9。这将需要大约 16 GB 的 RAM 来存储,假设在内存中只创建了阵列的一个副本。如果你的代码很草率,你可以很容易地将其翻倍或三倍。一旦进入虚拟内存,事情就会变慢得多。

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

相关内容

  • 没有找到相关文章

最新更新