【问题标题】:K-Means centroids getting marginalized to having no data points [Matlab]K-Means 质心被边缘化到没有数据点 [Matlab]
【发布时间】:2023-04-09 05:43:02
【问题描述】:

所以我有一个奇怪的问题。我有一个包含 240 个点的数据集,我正在尝试使用 k-means 将其聚类为 100 个聚类。我正在使用 Matlab,但我无法访问统计工具箱,所以我不得不编写自己的 k-means 函数。这很简单,所以应该不会太难,对吧?好吧,我的代码似乎有问题:

function result=Kmeans(X,c)
[N,n]=size(X);
index=randperm(N);
ctrs = X(index(1:c),:);
old_label = zeros(1,N);
label = ones(1,N);

iter = 0;
while ~isequal(old_label, label)
    old_label = label;
    label = assign_labels(X, ctrs);

    for i = 1:c
        ctrs(i,:) = mean(X(label == i,:));
        if sum(isnan(ctrs(i,:))) ~= 0
            ctrs(i,:) = zeros(1,n);
        end
    end
    iter = iter + 1;
end

result = ctrs;

function label = assign_labels(X, ctrs)
[N,~]=size(X);
[c,~]=size(ctrs);
dist = zeros(N,c);
for i = 1:c
    dist(:,i) = sum((X - repmat(ctrs(i,:),[N,1])).^2,2);
end

[~,label] = min(dist,[],2);

似乎发生的情况是,当我去重新计算质心时,一些质心没有分配给它们的数据点,所以我不确定该怎么做。在对此进行了一些研究之后,我发现如果您提供任意初始质心,则可能会发生这种情况,但在这种情况下,初始质心是从数据点本身获取的,因此这没有任何意义。我尝试将这些质心重新分配给随机数据点,但这会导致代码不收敛(或者至少在让它运行一整夜之后,代码从未收敛)。基本上它们会被重新分配,但这会导致其他质心被边缘化并重复。我不太确定我的代码出了什么问题,但是我通过 R 的 k-means 函数运行了相同的数据集,k=100 进行了 1000 次迭代,并且它设法收敛。有谁知道我在这里搞砸了什么?谢谢。

【问题讨论】:

  • 我很好奇,如果集群质心的任何维度为NaN,为什么您将集群的中心设为全零。你有理由解释你为什么这样做吗?无论如何,我将检查您的代码并找出问题所在。 Kmeans 我非常了解。
  • NaN 只有在没有分配给质心的点时才会发生。当这种情况发生时,我真的不知道该怎么办。我尝试随机选择一个数据点并将质心移动到那里,但这最终会导致无限循环(移动的质心会导致其他质心没有任何数据点,然后重复)。我只是选择将质心全为零作为默认值,因为我不确定在这里做什么正确。
  • 好的。我在帖子中写的确保所有集群质心都是唯一的建议应该有望解决您遇到的这个问题。试试看,看看效果如何!

标签: matlab machine-learning cluster-analysis k-means


【解决方案1】:

让我们一次一步地浏览您的代码,并根据我对k-means 算法的了解讨论您正在做的事情。

function result=Kmeans(X,c)
[N,n]=size(X);
index=randperm(N);
ctrs = X(index(1:c),:);
old_label = zeros(1,N);
label = ones(1,N);

这看起来像一个函数,它接收大小为N x n 的数据矩阵,其中N 是数据集中的点数,而n 是数据集中点的维度。此函数还接受c:所需的输出簇数。index 提供1 与您拥有的尽可能多的数据点之间的随机排列,然后我们从该排列中随机选择c 点你已经习惯了初始化你的集群中心。


iter = 0;
while ~isequal(old_label, label)
    old_label = label;
    label = assign_labels(X, ctrs);

    for i = 1:c
        ctrs(i,:) = mean(X(label == i,:));
        if sum(isnan(ctrs(i,:))) ~= 0
            ctrs(i,:) = zeros(1,n);
        end
    end
    iter = iter + 1;
end

result = ctrs;

对于k-means,我们基本上会继续迭代,直到前一次迭代中每个点的集群成员与当前迭代匹配,这就是您在while 循环中所做的。现在,label 确定数据集中每个点的集群成员。现在,对于存在的每个集群,您确定平均数据点是什么,然后将此平均数据点分配为每个集群的新集群中心。出于某种原因,如果您在集群中心的任何维度上遇到任何NaN,则将新的集群中心设置为全零。 这对我来说看起来很不正常,我稍后会提供建议。 编辑:现在我明白你为什么这样做了。这是因为如果您有任何空集群,您只需将该集群中心设为全零,因为您将无法找到空集群的平均值。这可以通过我在本文末尾提出的重复初始集群的建议来解决。


function label = assign_labels(X, ctrs)
[N,~]=size(X);
[c,~]=size(ctrs);
dist = zeros(N,c);
for i = 1:c
    dist(:,i) = sum((X - repmat(ctrs(i,:),[N,1])).^2,2);
end

[~,label] = min(dist,[],2);

这个函数接受一个数据集X和当前的集群中心进行迭代,它应该返回每个点属于每个集群的标签列表。这看起来也是正确的,因为对于dist 的每一列,您正在计算每个点到每个簇之间的距离,其中这些距离位于第 ith 列中的第 ith支持>集群。我将使用的一种优化技巧是避免在此处使用repmat,而使用在内部处理复制的bsxfun。因此,改为这样做:

function label = assign_labels(X, ctrs)
[N,~]=size(X);
[c,~]=size(ctrs);
dist = zeros(N,c);
for i = 1:c
    dist(:,i) = sum(bsxfun(@minus, X, ctrs(i,:)).^2, 2);
end

[~,label] = min(dist,[],2);

现在,这一切看起来都是正确的。我自己也进行了一些测试,如果初始集群中心是唯一的,一切似乎都可以解决。 k-means 的一个小问题是我们隐含地假设所有聚类中心都是唯一的。如果它们不是唯一的,那么您会遇到两个(或更多)集群具有完全相同的初始集群中心的问题......那么数据点应该分配到哪个集群?当您在 assign_labels 函数中执行 min 时,如果您有两个相同的聚类中心,则分配该点的聚类标签将是这两个数字中的最小值。这就是为什么你会有一个没有点的集群,因为所有应该分配给这个集群的点都会被分配给另一个。

因此,您可能有两个(或更多)初始聚类中心在随机化时相同。即使要选择的索引的排列是唯一的,实际的数据点本身在选择时可能不是是唯一的。我可以强加的一件事是循环排列直到你得到一组独特的初始集群,没有重复。因此,请尝试在代码开头执行此操作。

[N,n]=size(X);
index=randperm(N);
ctrs = X(index(1:c),:);

while size(unique(ctrs, 'rows'), 1) ~= c
    index=randperm(N);
    ctrs = X(index(1:c),:);
end

old_label = zeros(1,N);
label = ones(1,N);

iter = 0;

%// While loop appears here

这将确保您在继续编写代码之前拥有一组唯一的初始集群。现在,回到for 循环中的NaN 内容。 老实说,如果您的数据没有任何 NaN 开头,那么在您计算平均值后,我看不出任何维度如何导致 NaN。我建议你在你的代码中去掉这个(对我来说)它看起来不是很有用。 编辑:你现在可以删除 NaN 检查作为初始集群中心现在应该是唯一的。


这有望解决您遇到的问题。祝你好运!

【讨论】:

  • 是的!谢谢!这正是它。显然我的数据有几个相同的数据点,当我添加你推荐的检查时,它修复了所有问题。为了更好地衡量,我还在此之前添加了一个断言语句,以便在整体上没有 c 唯一数据点的情况下,代码不会陷入无限循环。
  • @Rae_III - 啊哈。我们去大声笑。听起来你的矩阵中有重复的数据点,所以这是我在你运行算法之前使用的一件事。我以前也经历过同样的异常。我还编辑了我的帖子,以解释为什么您需要 NaN 以供未来的读者查看。很高兴我能提供帮助,并且在 assert 声明中做得很好!
【解决方案2】:

由于 k-means 的性质,“丢失”一个簇并不像人们想象的那么特别。

考虑重复。让我们假设你所有的前 k 点都是相同的,你的代码会发生什么?您需要谨慎处理这种情况是有原因的。最简单的解决方案是让质心保持原样,并使用退化的集群。

鉴于您只有 240 分,但想使用 k=100,不要期望太好的结果。大多数对象都是独立的……选择太大的 k 可能是您经常看到这种退化效应的原因。让我们假设在这 240 个中,只有不到 100 个是唯一的......那么你不能有 100 个非空集群......另外,无论如何,我会认为这种结果“过度拟合”。

如果您在 Matlab 中没有所需的工具箱,也许您应该转向免费软件。 Octave、R、Weka、ELKI……有很多软件,其中一些在聚类方面比纯 Matlab 更强大(特别是,如果你没有工具箱的话)。

也是基准。您会对性能差异感到惊讶。

【讨论】:

    猜你喜欢
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 2014-10-26
    • 2018-09-19
    • 1970-01-01
    • 2018-07-02
    • 2015-01-19
    相关资源
    最近更新 更多