使用k均值聚类时如何确定k?


142

我一直在研究k均值聚类,尚不清楚的一件事是如何选择k的值。这仅仅是试错法,还是更多?


34
啊啊...这真的(关于k均值)的问题。
mjv

可以共享函数L(对数似然)的代码吗?给定X,Y的中心并指向(x(i = 1,2,3,4,...,n),y(i = 1,2,3,4,..,n)),如何我得到L吗?

7
有关该主题的Wikipedia文章的链接:en.wikipedia.org/wiki/…–
Amro,

11
我已经回答了类似的Q随半打的方法(用Rstackoverflow.com/a/15376462/1036500:)在这里

Answers:


142

您可以最大化贝叶斯信息准则(BIC):

BIC(C | X) = L(X | C) - (p / 2) * log n

其中L(X | C)X根据模型的数据集的对数似然Cp是模型中的参数数C,并且n是数据集中的点数。请参阅Dan Pelleg和Andrew Moore在ICML 2000中的“ X均值:通过有效估计簇数来扩展K均值”

另一种方法是从开始使用一个较大的值,k并保持删除质心(减小k),直到不再减小描述长度为止。请参阅模式分析和应用》第1卷中的Horst Bischof,Ales Leonardis和Alexander Selb 撰写的“鲁棒矢量量化的MDL原理”。2,第 59-72,1999年。

最后,您可以从一个群集开始,然后继续拆分群集,直到分配给每个群集的点具有高斯分布。Greg Hamerly和Charles Elkan 在“学习k中的k均值(NIPS 2003)中显示了一些证据,证明这比BIC更好,并且BIC并没有足够严厉地惩罚模型的复杂性。


好答案!对于X均值,您是否知道总体BIC分数n:= k * 2(k个聚类,每个聚类由具有平均/方差参数的高斯建模)。另外,如果确定“父级” BIC>“ 2个子级” BIC,您是否会在下一次迭代中再次拆分该群集?
Budric

2
@Budric,这些可能应该是单独的问题,并且可能在stats.stackexchange.com上。
Vebjorn Ljosa 2011年

37

基本上,您希望在两个变量之间找到平衡:聚类数(k)和聚类的平均方差。您要最小化前者,同时还要最小化后者。当然,随着聚类数的增加,平均方差会减少(直到k = n且方差= 0 的平凡情况)。

像在数据分析中一样,没有一种真正的方法可以在所有情况下都比其他方法更好。最后,您必须使用自己的最佳判断。为此,它有助于根据平均方差绘制聚类数(假设您已经针对k的多个值运行了算法)。然后,您可以使用曲线拐点处的簇数。


24

是的,您可以使用Elbow方法找到最佳数量的聚类,但是我发现使用脚本从肘形图中查找聚类的值很麻烦。您可以观察弯头图并自己找到弯头点,但是从脚本中找到弯头点是很多工作。

因此,另一种选择是使用“ 剪影方法”来找到它。Silhouette的结果与R中的Elbow方法的结果完全一致。

这是我所做的。

#Dataset for Clustering
n = 150
g = 6 
set.seed(g)
d <- data.frame(x = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2))), 
                y = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2))))
mydata<-d
#Plot 3X2 plots
attach(mtcars)
par(mfrow=c(3,2))

#Plot the original dataset
plot(mydata$x,mydata$y,main="Original Dataset")

#Scree plot to deterine the number of clusters
wss <- (nrow(mydata)-1)*sum(apply(mydata,2,var))
  for (i in 2:15) {
    wss[i] <- sum(kmeans(mydata,centers=i)$withinss)
}   
plot(1:15, wss, type="b", xlab="Number of Clusters",ylab="Within groups sum of squares")

# Ward Hierarchical Clustering
d <- dist(mydata, method = "euclidean") # distance matrix
fit <- hclust(d, method="ward") 
plot(fit) # display dendogram
groups <- cutree(fit, k=5) # cut tree into 5 clusters
# draw dendogram with red borders around the 5 clusters 
rect.hclust(fit, k=5, border="red")

#Silhouette analysis for determining the number of clusters
library(fpc)
asw <- numeric(20)
for (k in 2:20)
  asw[[k]] <- pam(mydata, k) $ silinfo $ avg.width
k.best <- which.max(asw)

cat("silhouette-optimal number of clusters:", k.best, "\n")
plot(pam(d, k.best))

# K-Means Cluster Analysis
fit <- kmeans(mydata,k.best)
mydata 
# get cluster means 
aggregate(mydata,by=list(fit$cluster),FUN=mean)
# append cluster assignment
mydata <- data.frame(mydata, clusterid=fit$cluster)
plot(mydata$x,mydata$y, col = fit$cluster, main="K-means Clustering results")

希望能帮助到你!!


2
只要添加一个链接剪影分析教程Python用户scikit-learn.org/stable/auto_examples/cluster/...
切塔尼亚Shivade

10

可能是像我这样的初学者在寻找代码示例。Silhouette_score的信息 可在此处获得。

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

range_n_clusters = [2, 3, 4]            # clusters range you want to select
dataToFit = [[12,23],[112,46],[45,23]]  # sample data
best_clusters = 0                       # best cluster number which you will get
previous_silh_avg = 0.0

for n_clusters in range_n_clusters:
    clusterer = KMeans(n_clusters=n_clusters)
    cluster_labels = clusterer.fit_predict(dataToFit)
    silhouette_avg = silhouette_score(dataToFit, cluster_labels)
    if silhouette_avg > previous_silh_avg:
        previous_silh_avg = silhouette_avg
        best_clusters = n_clusters

# Final Kmeans for best_clusters
kmeans = KMeans(n_clusters=best_clusters, random_state=0).fit(dataToFit)

9

看看这个文件,由Greg Hamerly,查尔斯·埃尔肯“学在k手段K”。它使用高斯检验来确定正确的群集数。同样,作者声称这种方法比公认的答案中提到的BIC更好。


7

有一种所谓的经验法则。它说可以通过

k = (n/2)^0.5

其中n是样本中元素的总数。您可以在以下纸张上检查此信息的准确性:

http://www.ijarcsms.com/docs/paper/volume1/issue6/V1I6-0015.pdf

还有另一种称为G均值的方法,您的分布遵循高斯分布或正态分布。它包括增加k直到所有k组都遵循高斯分布。它需要大量统计信息,但可以完成。来源如下:

http://papers.nips.cc/paper/2526-learning-the-k-in-k-means.pdf

我希望这有帮助!



3

我很惊讶没有人提到这篇出色的文章:http : //www.ee.columbia.edu/~dpwe/papers/PhamDN05-kmeans.pdf

在遵循了其他一些建议之后,我终于在阅读此博客时遇到了这篇文章:https : //datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/

之后,我在Scala中实现了它,该实现对于我的用例提供了非常好的结果。这是代码:

import breeze.linalg.DenseVector
import Kmeans.{Features, _}
import nak.cluster.{Kmeans => NakKmeans}

import scala.collection.immutable.IndexedSeq
import scala.collection.mutable.ListBuffer

/*
https://datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/
 */
class Kmeans(features: Features) {
  def fkAlphaDispersionCentroids(k: Int, dispersionOfKMinus1: Double = 0d, alphaOfKMinus1: Double = 1d): (Double, Double, Double, Features) = {
    if (1 == k || 0d == dispersionOfKMinus1) (1d, 1d, 1d, Vector.empty)
    else {
      val featureDimensions = features.headOption.map(_.size).getOrElse(1)
      val (dispersion, centroids: Features) = new NakKmeans[DenseVector[Double]](features).run(k)
      val alpha =
        if (2 == k) 1d - 3d / (4d * featureDimensions)
        else alphaOfKMinus1 + (1d - alphaOfKMinus1) / 6d
      val fk = dispersion / (alpha * dispersionOfKMinus1)
      (fk, alpha, dispersion, centroids)
    }
  }

  def fks(maxK: Int = maxK): List[(Double, Double, Double, Features)] = {
    val fadcs = ListBuffer[(Double, Double, Double, Features)](fkAlphaDispersionCentroids(1))
    var k = 2
    while (k <= maxK) {
      val (fk, alpha, dispersion, features) = fadcs(k - 2)
      fadcs += fkAlphaDispersionCentroids(k, dispersion, alpha)
      k += 1
    }
    fadcs.toList
  }

  def detK: (Double, Features) = {
    val vals = fks().minBy(_._1)
    (vals._3, vals._4)
  }
}

object Kmeans {
  val maxK = 10
  type Features = IndexedSeq[DenseVector[Double]]
}

在scala 2.11.7中使用微风0.12和nak 1.3进行了
放大

嗨@eirirlar,我正在尝试使用Python实现相同的代码-但我无法遵循网站上的代码。见我的文章:stackoverflow.com/questions/36729826/python-k-means-clustering
piccolo

@ImranRashid对不起,我仅用2维进行了测试,我不是Python专家。
eirirlar '16

3

如果您使用的是MATLAB(2013b以后的任何版本),则可以利用该函数evalclusters找出k给定数据集的最佳值。

该功能可让您从当中3聚类算法选择- kmeanslinkagegmdistribution

它也可以让你在4个聚类评价标准中进行选择- ,,CalinskiHarabasz 和。DaviesBouldingapsilhouette


3

如果您不知道作为参数提供给k-means的群集k的数目,那么有四种方法可以自动找到它:

  • G均值算法:它使用统计检验来自动确定群集数,以决定是否将k均值中心一分为二。该算法采用分层方法来检测聚类的数量,基于对数据子集遵循高斯分布(近似于事件的精确二项式分布的连续函数)的假设的统计检验,如果没有,它将对聚类进行拆分。它以少数几个中心开始,例如仅说一个集群(k = 1),然后该算法将其拆分为两个中心(k = 2),然后再次将这两个中心分别拆分为(k = 4),其中有四个中心总。如果G-means不接受这四个中心,则答案是上一步:在这种情况下为两个中心(k = 2)。这是您的数据集将被划分的集群数量。当您没有对实例分组后将获得的群集数量的估计时,G均值非常有用。请注意,对“ k”参数的不便选择可能会给您带来错误的结果。平行版本的g-means被称为p型装置。G均值来源: 来源1 来源2 来源3

  • x-means:一种新的算法,可以有效地搜索聚类位置的空间和聚类数量,以优化贝叶斯信息准则(AIC)或贝叶斯信息准则(AIC)度量。此版本的k均值可找到数字k并加快k均值。

  • 在线k均值或流式k均值:它允许通过扫描整个数据一次来执行k均值,并自动找到k的最佳数量。Spark实现了它。

  • MeanShift算法:这是一种非参数聚类技术,不需要先验聚类数量的知识,也不会限制聚类的形状。均值漂移聚类旨在发现平滑密度的样本中的“斑点”。这是基于质心的算法,通过将质心的候选对象更新为给定区域内点的均值来工作。然后在后处理阶段对这些候选对象进行过滤,以消除几乎重复的部分,从而形成最终的形心集。来源:来源1源2source3


2

我使用了在这里找到的解决方案:http : //efavdb.com/mean-shift/,它对我来说非常有效:

import numpy as np
from sklearn.cluster import MeanShift, estimate_bandwidth
from sklearn.datasets.samples_generator import make_blobs
import matplotlib.pyplot as plt
from itertools import cycle
from PIL import Image

#%% Generate sample data
centers = [[1, 1], [-.75, -1], [1, -1], [-3, 2]]
X, _ = make_blobs(n_samples=10000, centers=centers, cluster_std=0.6)

#%% Compute clustering with MeanShift

# The bandwidth can be automatically estimated
bandwidth = estimate_bandwidth(X, quantile=.1,
                               n_samples=500)
ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)
ms.fit(X)
labels = ms.labels_
cluster_centers = ms.cluster_centers_

n_clusters_ = labels.max()+1

#%% Plot result
plt.figure(1)
plt.clf()

colors = cycle('bgrcmykbgrcmykbgrcmykbgrcmyk')
for k, col in zip(range(n_clusters_), colors):
    my_members = labels == k
    cluster_center = cluster_centers[k]
    plt.plot(X[my_members, 0], X[my_members, 1], col + '.')
    plt.plot(cluster_center[0], cluster_center[1],
             'o', markerfacecolor=col,
             markeredgecolor='k', markersize=14)
plt.title('Estimated number of clusters: %d' % n_clusters_)
plt.show()

在此处输入图片说明



1

假设您有一个名为的数据矩阵DATA,则可以通过估计聚类数(通过轮廓分析)在类聚体周围执行分区,如下所示:

library(fpc)
maxk <- 20  # arbitrary here, you can set this to whatever you like
estimatedK <- pamk(dist(DATA), krange=1:maxk)$nc

1

一种可能的答案是使用元启发式算法(例如遗传算法)来找到k。很简单 您可以使用随机K(在一定范围内)并通过一些测量(例如,“轮廓”和“基于拟合函数查找最佳K”)来评估遗传算法的拟合函数。

https://zh.wikipedia.org/wiki/剪影_(群集)


1
km=[]
for i in range(num_data.shape[1]):
    kmeans = KMeans(n_clusters=ncluster[i])#we take number of cluster bandwidth theory
    ndata=num_data[[i]].dropna()
    ndata['labels']=kmeans.fit_predict(ndata.values)
    cluster=ndata
    co=cluster.groupby(['labels'])[cluster.columns[0]].count()#count for frequency
    me=cluster.groupby(['labels'])[cluster.columns[0]].median()#median
    ma=cluster.groupby(['labels'])[cluster.columns[0]].max()#Maximum
    mi=cluster.groupby(['labels'])[cluster.columns[0]].min()#Minimum
    stat=pd.concat([mi,ma,me,co],axis=1)#Add all column
    stat['variable']=stat.columns[1]#Column name change
    stat.columns=['Minimum','Maximum','Median','count','variable']
    l=[]
    for j in range(ncluster[i]):
        n=[mi.loc[j],ma.loc[j]] 
        l.append(n)

    stat['Class']=l
    stat=stat.sort(['Minimum'])
    stat=stat[['variable','Class','Minimum','Maximum','Median','count']]
    if missing_num.iloc[i]>0:
        stat.loc[ncluster[i]]=0
        if stat.iloc[ncluster[i],5]==0:
            stat.iloc[ncluster[i],5]=missing_num.iloc[i]
            stat.iloc[ncluster[i],0]=stat.iloc[0,0]
    stat['Percentage']=(stat[[5]])*100/count_row#Freq PERCENTAGE
    stat['Cumulative Percentage']=stat['Percentage'].cumsum()
    km.append(stat)
cluster=pd.concat(km,axis=0)## see documentation for more info
cluster=cluster.round({'Minimum': 2, 'Maximum': 2,'Median':2,'Percentage':2,'Cumulative Percentage':2})

您选择数据并添加库,然后将km = []复制到Percentage':2}),然后运行python并看到
sum

欢迎使用Stack Overflow!尽管此代码可能有助于解决问题,但并未解释为什么和/或如何回答问题。提供这种额外的环境将大大提高其长期教育价值。请编辑您的答案以添加解释,包括适用的限制和假设。
Toby Speight

1

另一种方法是使用自组织映射(SOP)查找最佳数量的群集。SOM(自组织映射)是一种无监督的神经网络方法,只需输入即可用于聚类以解决问题。在有关客户细分的论文中使用了这种方法。

本文的参考文献是

Abdellah Amine等人,《使用聚类技术和LRFM模型的电子商务中的客户细分模型:摩洛哥的在线商店案例》,世界科学,工程和技术学院,《国际计算机和信息工程学报》,第9卷,第8期: ,2015年,1999年-2010年


0

嗨,我将简单明了地进行解释,我想使用“ NbClust”库确定集群。

现在,如何使用'NbClust'函数确定正确的簇数:您可以使用实际数据和簇检查Github中的实际项目-该'kmeans'算法的扩展也使用了正确数目的'centers'。

Github项目链接:https : //github.com/RutvijBhutaiya/Thailand-Customer-Engagement-Facebook


除了添加github链接,您是否可以添加几行关键代码,即使您的代码无法访问,它们也可以帮助其他人?
朱利奥·卡钦

0

您可以通过目视检查数据点来选择群集的数量,但是您很快就会意识到,除了最简单的数据集之外,所有其他过程在此过程中都存在很多歧义。这并不总是不好的,因为您正在做无监督学习,并且在标记过程中存在一些固有的主观性。在这里,具有该特定问题或类似问题的先前经验将帮助您选择正确的价值。

如果需要有关应使用的群集数量的一些提示,则可以应用Elbow方法:

首先,为某些k值(例如2、4、6、8等)计算平方误差总和(SSE)。SSE定义为群集的每个成员与其质心之间的平方距离的总和。数学上:

SSE = ∑Ki = 1∑x∈cidist(x,ci)2

如果将k相对于SSE绘制,您会发现随着k变大,误差减小;反之,k变大。这是因为当簇的数量增加时,它们应该较小,因此失真也较小。弯头法的想法是选择SSE突然减小的k。如下面的图片所示,这会在图形中产生“肘部效果”:

在此处输入图片说明

在这种情况下,k = 6是Elbow方法选择的值。考虑到Elbow方法是一种启发式方法,因此,在您的特定情况下它可能会或可能不会很好地起作用。有时,肘部不止一个,甚至根本没有。在这些情况下,您通常最终会通过评估k均值在您要解决的特定聚类问题的情况下的表现来计算最佳k。


0

我研究了跪着的Python软件包(跪着算法)。它动态地找到簇编号作为曲线开始变平的点。给定一组x和y值,knee将返回函数的拐点。拐点是最大曲率点,此处是示例代码。

Y = [7342.1301373073857,6881.7109460930769,6531.1657905495022,
6356.2255554679778,6209.8382535595829,6094.9052166741121,5980.0191582610196,5880.1869867848218,5779.8957906367368,5691.1879324562778,5617.5153566271356,5532.2613232619951,5467.352265375117,5395.4493783888756,5345.3459908298091,5290.6769823693812,5243.5271656371888,5207.2501206569532,5164.9617535255456]

x =范围(1,len(y)+1)

从膝盖导入KneeLocator kn = KneeLocator(x,y,curve ='convex',direction ='decreasing')

打印(膝盖)


请为您的答案添加一些解释,以便其他人可以从中学习
Nico Haase
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.