使用 k-means 聚类时如何确定 k?
Posted
技术标签:
【中文标题】使用 k-means 聚类时如何确定 k?【英文标题】:How do I determine k when using k-means clustering? 【发布时间】:2010-12-20 02:17:01 【问题描述】:我一直在研究k-means clustering,不清楚的一件事是你如何选择k的值。这只是一个反复试验的问题,还是有更多的问题?
【问题讨论】:
啊啊...这真的是 the 问题(关于 k-mean)。 你能分享函数 L(对数似然)的代码吗?给定 X,Y 的中心和 (x(i=1,2,3,4,...,n),y(i=1,2,3,4,..,n)) 的点,如何我会得到 L 吗? 关于该主题的***文章链接:en.wikipedia.org/wiki/… 我在这里用六种方法(使用R
)回答了一个类似的问题:***.com/a/15376462/1036500
【参考方案1】:
基本上,您希望在两个变量之间找到平衡点:聚类数 (k) 和聚类的平均方差。你想最小化前者,同时也最小化后者。当然,随着聚类数量的增加,平均方差会减小(直到 k=n 和方差=0 的平凡情况。
与数据分析一样,没有一种方法在所有情况下都比其他方法效果更好。最后,您必须使用自己的最佳判断。为此,它有助于根据平均方差(假设您已经针对 k 的多个值运行算法)绘制聚类数。然后您可以使用曲线拐点处的簇数。
【讨论】:
【参考方案2】:您可以最大化贝叶斯信息准则 (BIC):
BIC(C | X) = L(X | C) - (p / 2) * log n
其中L(X | C)
是数据集X
根据模型C
的对数似然,p
是模型C
中的参数数量,n
是模型中的点数数据集。
请参阅 ICML 2000 中 Dan Pelleg 和 Andrew Moore 的 "X-means: extending K-means with efficient estimation of the number of clusters"。
另一种方法是从k
的较大值开始,并继续删除质心(减少 k),直到不再减少描述长度。请参阅 Horst Bischof、Ales Leonardis 和 Alexander Selb 在模式分析和应用卷中的"MDL principle for robust vector quantisation"。 2,第1999 年 59 至 72 日。
最后,您可以从一个集群开始,然后继续拆分集群,直到分配给每个集群的点具有高斯分布。在"Learning the k in k-means" (NIPS 2003) 中,Greg Hamerly 和 Charles Elkan 展示了一些证据,证明这比 BIC 更有效,并且 BIC 对模型复杂性的惩罚不够强烈。
【讨论】:
很好的答案!对于 X-Means,您是否知道整体 BIC 得分 n := k*2(k 个集群,每个集群由高斯建模,具有均值/方差参数)。此外,如果您确定“父”BIC >“2 个子”BIC,您是否会在下一次迭代中再次拆分该集群? @Budric,这些应该是单独的问题,可能在 stats.stackexchange.com 上。【参考方案3】:首先为您的数据构建一个minimum spanning tree。 移除 K-1 个最昂贵的边会将树分成 K 个簇, 因此您可以构建一次 MST,查看各种 K 的簇间距/指标, 并取曲线的膝盖。
这仅适用于Single-linkage_clustering, 但为此它又快又容易。此外,MST 可以提供出色的视觉效果。 例如,参见下面的 MST 图 stats.stackexchange visualization software for clustering.
【讨论】:
【参考方案4】:查看 this 论文,“Learning the k in k-means”,作者是 Greg Hamerly,Charles Elkan。它使用高斯检验来确定正确的集群数量。此外,作者声称此方法优于已接受答案中提到的 BIC。
【讨论】:
【参考方案5】:是的,您可以使用 Elbow 方法找到最佳聚类数,但我发现使用脚本从肘形图中找到聚类值很麻烦。您可以观察肘部图并自己找到肘部点,但是从脚本中找到它需要做很多工作。
所以另一种选择是使用Silhouette Method 来查找它。 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")
希望对你有帮助!
【讨论】:
只是为python用户添加了剪影分析教程的链接scikit-learn.org/stable/auto_examples/cluster/… 另外,绘图见黄砖scikit-yb.org/en/latest/api/cluster/silhouette.html 他们也有肘法【参考方案6】:我的想法是使用Silhouette Coefficient 来找到最佳簇数(K)。详细解释是here。
【讨论】:
【参考方案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
我希望这会有所帮助!
【讨论】:
【参考方案8】:假设您有一个名为 DATA
的数据矩阵,您可以通过估计聚类数量(通过轮廓分析)围绕中心点执行分区,如下所示:
library(fpc)
maxk <- 20 # arbitrary here, you can set this to whatever you like
estimatedK <- pamk(dist(DATA), krange=1:maxk)$nc
【讨论】:
【参考方案9】:我很惊讶没有人提到这篇优秀的文章: 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 实现相同的代码 - 但我无法遵循网站中的代码。见我的帖子:***.com/questions/36729826/python-k-means-clustering @ImranRashid 抱歉,我只测试了二维,我不是 Python 专家。【参考方案10】:如果您使用 MATLAB(自 2013b 以来的任何版本),您可以使用函数 evalclusters
找出给定数据集的最佳 k
应该是什么。
此功能可让您从 3 种聚类算法中进行选择 - kmeans
、linkage
和 gmdistribution
。
它还允许您从 4 个聚类评估标准中进行选择 - CalinskiHarabasz
、DaviesBouldin
、gap
和 silhouette
。
【讨论】:
【参考方案11】:一个可能的答案是使用像遗传算法这样的元启发式算法来找到 k。 这很简单。您可以使用随机 K(在某个范围内)并使用诸如剪影之类的测量来评估遗传算法的拟合函数 并根据拟合函数找到最佳 K。
https://en.wikipedia.org/wiki/Silhouette_(clustering)
【讨论】:
【参考方案12】: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 并查看 欢迎来到 Stack Overflow!尽管此代码可能有助于解决问题,但它并没有解释 why 和/或 如何 回答问题。提供这种额外的背景将显着提高其长期教育价值。请edit您的答案添加解释,包括适用的限制和假设。【参考方案13】:可能是像我这样寻找代码示例的初学者。 silhouette_score 的信息 可用here.
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)
【讨论】:
示例不适用于 scikit-learn 版本:0.24.2。 Silhouette_score(dataToFit、cluster_labels)上的错误。 “发生异常:ValueError 标签数为 3。有效值为 2 到 n_samples - 1(含)” 你应该看看这个:***.com/questions/51382250/…【参考方案14】:我使用了我在这里找到的解决方案: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()
【讨论】:
【参考方案15】:另一种方法是使用自组织图 (SOP) 来查找最佳集群数量。 SOM(自组织图)是一种无监督的神经网络 网络方法,它只需要输入用于 聚类解决问题。这种方法在一篇关于客户细分的论文中使用。
论文的参考文献是
Abdellah Amine 等人,电子商务中的客户细分模型 聚类技术和 LRFM 模型:案例 摩洛哥在线商店,世界科学、工程和技术学院 国际计算机与信息工程杂志 Vol:9, No:8, 2015, 1999 - 2010
【讨论】:
【参考方案16】:如果您不知道作为参数提供给 k-means 的聚类 k 的数量,那么有四种方法可以自动找到它:
G-means 算法:它使用统计测试自动发现集群的数量,以决定是否将 k-means 中心一分为二。该算法采用分层方法来检测集群的数量,基于对数据子集遵循高斯分布(近似于事件的精确二项式分布的连续函数)的假设的统计检验,如果不是,则拆分集群.它从少量中心开始,比如只有一个集群 (k=1),然后算法将其拆分为两个中心 (k=2) 并再次拆分这两个中心中的每一个 (k=4),其中有四个中心全部的。如果 G-means 不接受这四个中心,那么答案是上一步:在这种情况下是两个中心 (k=2)。这是您的数据集将分成的集群数量。当您无法估计分组实例后将获得的集群数量时,G-means 非常有用。请注意,“k”参数的不方便选择可能会给您错误的结果。 g-means 的并行版本称为p-means。 G-均值来源: source 1 source 2 source 3
x-means:一种新算法,可有效搜索集群位置空间和集群数量,以优化贝叶斯信息准则 (BIC) 或 Akaike 信息准则 (AIC) 度量。这个版本的 k-means 找到数字 k 并且还加速了 k-means。
在线 k-means 或 Streaming k-means:它允许通过扫描整个数据一次来执行 k-means,并自动找到最佳 k 数。 Spark 实现了它。
MeanShift algorithm:它是一种非参数聚类技术,不需要事先知道聚类的数量,也不限制聚类的形状。均值偏移聚类旨在发现平滑密度样本中的“斑点”。它是一种基于质心的算法,它通过将候选质心更新为给定区域内点的平均值来工作。然后在后处理阶段对这些候选对象进行过滤,以消除近似重复以形成最终的质心集。来源:source1、source2、source3
【讨论】:
【参考方案17】:您好,我会简单明了地解释一下,我喜欢使用“NbClust”库来确定集群。
现在,如何使用“NbClust”函数来确定正确的集群数量:您可以使用实际数据和集群检查 Github 中的实际项目 - 对这个“kmeans”算法的扩展也使用正确数量的“中心”。
Github 项目链接:https://github.com/RutvijBhutaiya/Thailand-Customer-Engagement-Facebook
【讨论】:
除了添加 github 链接,您能否添加几行关键代码,即使您的代码无法访问也可以帮助其他人?【参考方案18】:您可以通过直观地检查数据点来选择集群的数量,但您很快就会意识到,除了最简单的数据集之外,此过程中存在很多歧义。这并不总是很糟糕,因为您正在进行无监督学习,并且在标记过程中存在一些固有的主观性。在这里,具有该特定问题或类似问题的先前经验将帮助您选择正确的值。
如果您想了解应该使用的集群数量,可以应用 Elbow 方法:
首先,计算某些 k 值(例如 2、4、6、8 等)的误差平方和 (SSE)。 SSE 定义为集群的每个成员与其质心之间的平方距离之和。数学上:
SSE=∑Ki=1∑x∈cidist(x,ci)2
如果你将 k 与 SSE 作图,你会看到误差随着 k 变大而减小;这是因为当簇的数量增加时,它们应该更小,因此失真也更小。肘部法的思想是选择 SSE 突然下降的 k。这会在图中产生“肘部效应”,如下图所示:
在这种情况下,k=6 是 Elbow 方法选择的值。考虑到 Elbow 方法是一种启发式方法,因此,它在您的特定情况下可能会或可能不会很好地工作。有时,有不止一个肘部,或者根本没有肘部。在这些情况下,您通常最终通过评估 k-means 在您尝试解决的特定聚类问题的上下文中的执行情况来计算最佳 k。
【讨论】:
【参考方案19】:我研究了一个 Python 包膝盖(Kneedle 算法)。它动态地找到簇数作为曲线开始变平的点。给定一组 x 和 y 值,kneed 将返回函数的拐点。膝关节是曲率最大的点。这是示例代码。
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 = range(1, len(y)+1)
from kneed import KneeLocator
kn = KneeLocator(x, y, curve='convex', direction='decreasing')
print(kn.knee)
【讨论】:
请在您的答案中添加一些解释,以便其他人可以从中学习【参考方案20】:在这里留下一个来自 Codecademy 课程的非常酷的 gif:
K-Means 算法:
-
为初始集群放置 k 个随机质心。
将数据样本分配到最近的质心。
根据上述分配的数据样本更新质心。
顺便说一句,它不是对完整算法的解释,它只是有用的可视化
【讨论】:
以上是关于使用 k-means 聚类时如何确定 k?的主要内容,如果未能解决你的问题,请参考以下文章
使用带有 Silhouette 函数的 k-means 聚类时如何选择 k?