Optimization 是否有可能优化此Matlab代码,以便从k-均值中使用质心进行矢量量化?

Optimization 是否有可能优化此Matlab代码,以便从k-均值中使用质心进行矢量量化?,optimization,matlab,vector,k-means,quantization,Optimization,Matlab,Vector,K Means,Quantization,我已经用大小为4000x300的k-means(4000个质心,每个质心有300个特征)创建了一个代码本。使用代码本,我想标记一个输入向量(为了以后的装箱)。输入向量的大小为Nx300,其中N是我接收的输入实例总数 为了计算标签,我计算每个输入向量的最近质心。为此,我将每个输入向量与所有质心进行比较,并选择距离最小的质心。然后,标签就是该质心的索引 我当前的Matlab代码如下所示: function labels = assign_labels(centroids, X) labels = z

我已经用大小为4000x300的k-means(4000个质心,每个质心有300个特征)创建了一个代码本。使用代码本,我想标记一个输入向量(为了以后的装箱)。输入向量的大小为Nx300,其中N是我接收的输入实例总数

为了计算标签,我计算每个输入向量的最近质心。为此,我将每个输入向量与所有质心进行比较,并选择距离最小的质心。然后,标签就是该质心的索引

我当前的Matlab代码如下所示:

function labels = assign_labels(centroids, X)
labels = zeros(size(X, 1), 1);

% for each X, calculate the distance from each centroid
for i = 1:size(X, 1)
    % distance of X_i from all j centroids is: sum((X_i - centroid_j)^2)
    % note: we leave off the sqrt as an optimization
    distances = sum(bsxfun(@minus, centroids, X(i, :)) .^ 2, 2);
    [value, label] = min(distances);
    labels(i) = label;
end     
然而,这段代码仍然相当慢(出于我的目的),我希望有一种方法可以进一步优化代码

一个明显的问题是存在for循环,这是Matlab良好性能的祸根。我一直在想办法摆脱它,但运气不好(我考虑过将arrayfun与bsxfun结合使用,但还没有成功)。或者,如果有人知道任何其他加快速度的方法,我将不胜感激

更新

在做了一些搜索之后,我无法使用Matlab找到一个很好的解决方案,因此我决定看看Python的scikits.learn包中使用的“欧几里得距离”(简称“euclidean_distance”):

它使用了欧几里德距离的二项式形式((x-y)^2->x^2+y^2-2xy),根据我所读的,它通常运行得更快。我完全未经测试的Matlab翻译是:

 XX = sum(data .* data, 2);
 YY = sum(center .^ 2, 2);
 [val, ~] = max(XX + YY - 2*data*center');

您可以通过转换为单元格并使用以下命令将其矢量化:

说明:

  • 我们将
    X
    的每一行分配给第二行中自己的单元格
  • 这段
    @(x)(sum(bsxfun(@减号,质心,x)。^2,2))
    是一个匿名函数,它与
    距离=…
    行相同,我们使用
    cell2mat
    ,将其应用于
    x
    的每一行
  • 然后,标签是沿每列的最小行的索引

  • 对于一个真正的矩阵实现,您可以考虑沿着以下的方式尝试:

      P2 = kron(centroids, ones(size(X,1),1));
      Q2 = kron(ones(size(centroids,1),1), X);
    
      distances = reshape(sum((Q2-P2).^2,2), size(X,1), size(centroids,1));
    
    注意
    这假设数据组织为[x1 y1…;x2 y2…;…]

    使用以下函数计算距离。你会看到一个数量级的加速

    两个矩阵A和B的列作为二元数,行作为每个点。 A是质心矩阵。B是数据点矩阵

    function D=getSim(A,B)
        Qa=repmat(dot(A,A,2),1,size(B,1));
        Qb=repmat(dot(B,B,2),1,size(A,1));
        D=Qa+Qb'-2*A*B';
    

    对于最近邻搜索,您可以使用比暴力更有效的算法。 最流行的方法是Kd树。O(log(n))平均查询时间,而不是O(n)蛮力复杂性。
    关于Kd树的Maltab实现,您可以看一看

    它看起来像num2cell(X)将X的每个元素转化为它自己的向量。然而,对于(x-质心),我们想要减去x的每一行与每个质心。那么应该改为:XCell=num2cell(X,2)?@Abe,你说得对。虽然它肯定会返回相同的答案,但将其应用于每个元素时会产生不必要的函数调用开销。我已经修好了。但是请记住,
    bsxfun
    cellfun
    通常只是编写循环的简单方法,不一定要更快(有时是,但并不总是)。对你的循环和cellfun代码进行计时,使其与你的矩阵具有相同的维度,它们在93秒左右都相当不错,只是相差十分之一。太好了,谢谢!是的,我希望Matlab的内部循环可能比一般的for循环更好,但我也没有看到很大的改进。此外,for循环允许使用“parfor”。最初希望使用GPU,但bsxfun不允许使用非对称函数,因此无法通过“质心”。我确实发现了一个改进,那就是其他人的帖子,这样就可以绕过质心而不是数据。我的数据中有4000个质心和从1e5到1e6的任何特征向量。所以通过在质心上循环,我可以用矩阵数学加快速度。我在之前的评论中没有空间,但我使用了这里的标签分配代码:这使用了大量内存-就像任何真正的矢量化版本一样。@yoda已经提供了(较慢的)替代方案。你也可以用repmat代替kron——这只是我在自己的一个项目中使用过的一个版本。我要试试这个,因为我目前的方法需要4天以上的时间。不过,我不熟悉kron,所以我将尝试使用上面的repmats重写它。如果你有机会,你介意核实一下吗?没关系。在纸上,如果我把两个矩阵都做成3维(重复数据矩阵的大小(质心,1)次,然后重复每个质心的大小(数据,1)对于每个质心的时间,我只需减去、平方、求和,然后将它们最小化。然而,我无法找到一个好方法来确定第二个矩阵。当然-你能告诉我
    N
    将有多大吗?正如我提到的,这是内存使用会很快变大-我认为它处理N的数量不会超过给定的最大值几百个左右300-d测量空间。好的,很高兴知道。我现在在读克朗,但我以前没有见过这样使用它。至于N,它相当大,所以这可能是一个问题。我的数据通常是100000,我的质心大约是4000。我可以更改数据的大小,因为我正在分页它,但如果它太小,我担心会引起错误成本很高。+1对你来说。这确实更快。回想起来,我不知道为什么我没有这样做,而不是使用
    cellfun
    。我已经使用
    cellfun
    太晚了,应该从我的工具包中退出,至少暂时:)相关:
      P2 = kron(centroids, ones(size(X,1),1));
      Q2 = kron(ones(size(centroids,1),1), X);
    
      distances = reshape(sum((Q2-P2).^2,2), size(X,1), size(centroids,1));
    
    function D=getSim(A,B)
        Qa=repmat(dot(A,A,2),1,size(B,1));
        Qb=repmat(dot(B,B,2),1,size(A,1));
        D=Qa+Qb'-2*A*B';