Matlab 求两两欧氏距离(距离矩阵)的快速算法

Matlab 求两两欧氏距离(距离矩阵)的快速算法,matlab,vectorization,linear-algebra,Matlab,Vectorization,Linear Algebra,我知道matlab有一个内置的pdist函数,可以计算成对距离。然而,我的矩阵太大了,它的大小是60000乘300,matlab的内存不足 这是一个后续问题 有没有解决这种计算效率低下的办法。我尝试手动编码成对距离计算,通常需要一整天的时间(有时6到7个小时) 非常感谢您的帮助 计算机不是无限大,也不是无限快。人们认为他们有很多内存,一个快速的CPU,所以他们只是制造越来越大的问题,然后最终想知道为什么他们的问题运行缓慢。事实上,这不是计算效率低下。它只是一个过载的CPU 正如奥利在一篇评论中指

我知道matlab有一个内置的pdist函数,可以计算成对距离。然而,我的矩阵太大了,它的大小是60000乘300,matlab的内存不足

这是一个后续问题

有没有解决这种计算效率低下的办法。我尝试手动编码成对距离计算,通常需要一整天的时间(有时6到7个小时)


非常感谢您的帮助

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

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


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

嗯,我忍不住要到处玩。我创建了一个名为Matlab的程序,它实现了单精度和双精度的成对欧氏距离。在我使用Matlab R2012b和R2015a的机器上,对于大输入(例如60000×300),它比(以及基本的
pdistmex
helper函数)快20–25%

正如已经指出的,这个问题基本上是由内存限制的,你需要很多内存。我的MEXC代码使用了最小的内存,超出了输出所需的内存。将它的内存使用情况与pdist的内存使用情况进行比较,看起来两者几乎相同。换句话说,
pdist
没有使用大量额外内存。您的内存问题可能是在调用
pdist
之前内存耗尽(您可以使用
清除
删除任何大型数组吗?)或者仅仅是因为您试图在小型硬件上解决一个大型计算问题

因此,我的
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位Intel Mac,则不需要编译,因为我已经包含了
.mexmaci64
二进制文件,否则您需要了解如何为您的机器编译代码。我帮不了你。您可能无法编译它,或者需要通过自己编辑代码来解决兼容性问题。也有可能存在bug,代码会使Matlab崩溃。另外,请注意,相对于
pdist
,您可能会得到稍微不同的输出,两者之间的差异在机器epsilon()范围内
pdist
可能会也可能不会做一些花哨的事情来避免大输入和其他数字问题的溢出,但请注意,我的代码不会

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


所有的文件。ZIP存档包括所有文件。它是BSD授权的。请随意优化(我在C代码中尝试了BLAS调用和OpenMP,但都没有用——也许一些指针魔法或GPU/OpenCL可以进一步加速)。我希望它能对你或其他人有所帮助

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

我认为,你需要一个非常好的C代码来克服这个问题

更新
因为没有使用

因此,可以编写代码:

function [ mD ] = CalcDistMtx ( mX )    
  vSsqX = sum(mX .^ 2, 1);
  mD = sqrt(vSsqX.'+ vSsqX - (2 * (mX.' * mX)));       
end
我的报告中给出了一个概括

附言
使用MATLAB的
pdist
进行比较:
squareform(pdist(mX.)
相当于
CalcDistMtx(mX)


也就是说,输入应该被转置。

这永远不会很快。您需要计算~2e9个结果,每个结果需要300次乘法和600次加法/减法。所以总共大约有2e12个操作。也就是说,如果代码经过充分优化,它应该可以比6-7个小时做得更好。@OliCharlesworth-了解这一点的唯一方法是更多地了解正在使用的计算机。多少公羊?@woodchips:这是一个公平的观点;输出阵列约为16GB(我想我习惯于在具有96GB RAM的机器上工作)。但是,即使输出直接写入磁盘,50MB/s的典型写入速度只需要5分钟左右……对您的时间和精力进行更好的投资可能是找到一台运行64位Matlab的具有大量RAM的像样的64位机器,并在其上(本地或远程)运行您的代码。你有Mac和Matlab的朋友吗?运行
d=pdist(rand(6e4,3e2)),只需8分45秒(143997600字节内存)。注意此处的误导性措辞;一个人不“进入虚拟内存”,他总是在使用虚拟内存…;)没错,现代计算机是
function [ mD ] = CalcDistMtx ( mX )    
  vSsqX = sum(mX .^ 2, 1);
  mD = sqrt(vSsqX.'+ vSsqX - (2 * (mX.' * mX)));       
end