提高嵌套循环MATLAB的性能

时间:2013-10-21 22:26:39

标签: performance matlab vectorization nested-loops

我正在尝试分析大量数据,这些数据让我的程序运行缓慢。 我正在读取从.txt文件到单元格数组的数据集。 我正在使用一个单元格数组,以便对我的数据进行分类,这是两个属性的形式,我需要的是chars类。

我想使用最近的平均分类器找到重新替代错误。 我有一个主外环循环遍历我的数据集的每一行(数万)。依次移除每一行,每次迭代一行。在每次迭代时重新计算两个属性的平均值,并删除该行。主挂点似乎是我要为我的数据集中的每一行计算的下一部分:

  • 该行数据之间的欧几里德距离(2个属性值)和 每个班级的平均值。
  • 然后我想记录属性平均值最接近的类,这将是它的指定类。
  • 最后我想检查这个指定的类是否正确 类。

目前这个循环看起来像这样。

errorCount = 0;
for l = 1:20000
    closest = 100;
    class = 0;
    attribute1 = d{2}(l);
    attribute2 = d{3}(l);
    for m = 1:numel(classes)
        dist = sqrt((attribute1-meansattr1(m))*(attribute1-meansattr1(m)) + (attribute2-meansattr2(m))*(attribute2-meansattr2(m)));
        if dist < closest
            closest = dist;
            class = m;
        end
    end

    if strcmp(d{1}(l),classes(class))
        %correct
    else
        errorCount = errorCount + 1;
    end
end

d是我的单元格数组,d{2}是一个包含属性1值的列。我使用d{1}(1)为该列的第一行访问这些值。

classes是我的数据集中的唯一类,因此对于我的每个类,我都计算了它的欧几里德距离。

meansattr1meansattr2是包含每个属性的平均值的数组。当删除一行时,这些会在外循环的每次迭代中更新。

希望这可以帮助您理解我拥有的代码。非常感谢任何有关优化和加速这些计算的帮助。

3 个答案:

答案 0 :(得分:2)

最简单的速度提升是删除sqrt电话。找到最近的平方距离与最近的距离完全相同。

接下来,您可以向内循环进行矢量化。我做了任何MatLab已经很久了,所以我可能会得到以下代码错误,但我们的想法是将这两个属性变成长度为numel(classes)的向量。然后你可以直接计算差异并将它们平方。

这样的事情:

d1 = attribute1 - meansattr1;
d2 = attribute2 - meansattr2;
[closest, class] = min( d1 .* d1 + d2 .* d2 );

顺便说一下,使用class作为变量(如果你能做到的话)并不是一个好主意。这是一个保守的词。

答案 1 :(得分:1)

您实际上是在优化k-means算法的迭代部分,因此您可以参考my previous solution来获取矢量化的方法。但是,您可以按照以下方式处理问题和数据格式。

获取如下随机数据集

numClasses = 5;
numPoints = 20e3;
numDims = 2;

classes = strsplit(num2str(1:numClasses));

% generate random data (expected error rate of (numClasses-1)/numClasses)
d{1} = classes(randi(numClasses,numPoints,1));
d{2} = rand(numPoints,1);
d{3} = rand(numPoints,1);

% random initial class centers
meansattr1 = rand(5,1);
meansattr2 = rand(5,1);

您的代码,压缩并存储每个点的最接近的类ID以及与该类的距离变为:

closestDistance = zeros(numPoints,1);  nearestCluster = zeros(numPoints,1);
errorCount = 0;
for l = 1:numPoints
    closest = 100; iclass = 0;
    attribute1 = d{2}(l); attribute2 = d{3}(l);

    for m = 1:numel(classes)
        dist = sqrt((attribute1-meansattr1(m))*(attribute1-meansattr1(m)) + ...
            (attribute2-meansattr2(m))*(attribute2-meansattr2(m)));
        if dist < closest
            closest = dist; closestDistance(l) = closest;
            iclass = m; nearestCluster(l) = iclass;
        end
    end

    if ~strcmp(d{1}(l),classes(iclass))
        errorCount = errorCount + 1;
    end
end

以上的矢量化版本是:

data = [d{2}(:) d{3}(:)];
meansattr = [meansattr1(:) meansattr2(:)];

kdiffs = bsxfun(@minus,data,permute(meansattr,[3 2 1]));

allDistances = sqrt(sum(kdiffs.^2,2)); % no need to do sqrt
allDistances = squeeze(allDistances); % Nx1xK => NxK

[closestDistance,nearestCluster] = min(allDistances,[],2); % Nx1

correctClassIds = str2num(char(d{1}(:)));
errorCount = nnz(nearestCluster ~= correctClassIds);

errorCountclosestDistancenearestCluster中的结果与之前的解决方案相同。您可以删除sqrt并在errorCountnearestCluster中获得相同的结果,如代码注释所示。

假设您要执行更新meansattr1meansattr2的下一步:

% Calculate the NEW cluster centers (mean the data)
meansattr_new = zeros(numClasses,numDims);
clustersizes = zeros(numClasses,1);
for ii=1:numClasses,
    indk = nearestCluster==ii;
    clustersizes(ii) = nnz(indk);
    meansattr_new(ii,:) = mean(data(indk,:))';
end

meansattr1_next = meansattr_new(:,1);
meansattr2_next = meansattr_new(:,2);

将这一切全部放在while errorCount>THRESHfor jj = 1:MAXITER中,你应该拥有你想要的东西。

答案 2 :(得分:0)

我开始使用paddy的解决方案,简单地替换变量名称:

[closest, cl] = min( (d{2}(m) - meansattr1).^2 +(d{3}(m) - meansattr2).^
2);

因此我们有一个单行for循环,通用策略:创建它的函数并将其放入arrayfun:

f=@(x)min( (d{2}(x) - meansattr1).^2 +(d{3}(x) - meansattr2).^2)
[sqclosest,cl]=arrayfun(f,1:numel(d{2}));

%If necessary real distances could be calculated:
%closest=sqrt(sqclosest)

errorCount=sum(arrayfun(@(x,c)(1-strcmp(x,classes(c))),d{1},cl))

注意:请勿将“class”或任何其他保留字用于其他目的。