K-Means中K值的选取

news/2024/10/23 23:22:32/

K-Means中K值的选择

    • (1)拍脑袋法
    • (2)肘部法则(Elbow Method)
    • (3)间隔统计量(Gap Statistic)
    • (4)轮廓系数(Silhouette Coefficient)
    • (5)Canopy算法

K-Means是一个很简单的聚类方法,说它简单,主要原因是使用它时只需设置一个K值(设置需要将数据聚成几类)。但问题是,有时候我们拿到的数据根本不知道要分为几类,对于二维的数据,我们还能通过肉眼观察法进行确定,超过二维的数据怎么办?今天就一起来学习下。
在这里插入图片描述

(1)拍脑袋法

一个非常快速的,拍脑袋的方法是将样本量除以2再开方出来的值作为K值,具体公式为:
在这里插入图片描述

(2)肘部法则(Elbow Method)

Elbow Method :Elbow意思是手肘,如下图左所示,此种方法适用于 K 值相对较小的情况,当选择的k值小于真正的时,k每增加1,cost值就会大幅的减小;当选择的k值大于真正的K时, k每增加1,cost值的变化就不会那么明显。这样,正确的k值就会在这个转折点,类似elbow的地方。 如下图:
在这里插入图片描述
通过画K与cost function的关系曲线图,如左图所示,肘部的值(cost function开始时下降很快,在肘部开始平缓了)做为K值,K=3。并不是所有的问题都可以通过画肘部图来解决,有的问题如右边的那个图,肘点位置不明显(肘点可以是3,4,5),这时就无法确定K值了。故肘部图是可以尝试的一种方法,但是并不是对所有的问题都能画出如左边那么好的图来确定K值。

Elbow Method公式:
在这里插入图片描述

Python实现:

# clustering dataset
# determine k using elbow methodfrom sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import numpy as np
import matplotlib.pyplot as pltx1 = np.array([3, 1, 1, 2, 1, 6, 6, 6, 5, 6, 7, 8, 9, 8, 9, 9, 8])
x2 = np.array([5, 4, 5, 6, 5, 8, 6, 7, 6, 7, 1, 2, 1, 2, 3, 2, 3])plt.plot()
plt.xlim([0, 10])
plt.ylim([0, 10])
plt.title('Dataset')
plt.scatter(x1, x2)
plt.show()# create new plot and data
plt.plot()
X = np.array(list(zip(x1, x2))).reshape(len(x1), 2)
colors = ['b', 'g', 'r']
markers = ['o', 'v', 's']# k means determine k
distortions = []
K = range(1, 10)
for k in K:kmeanModel = KMeans(n_clusters=k).fit(X)kmeanModel.fit(X)distortions.append(sum(np.min(cdist(X, kmeanModel.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])# Plot the elbow
plt.plot(K, distortions, 'bx-')
plt.xlabel('k')
plt.ylabel('Distortion')
plt.title('The Elbow Method showing the optimal k')
plt.show()

(3)间隔统计量(Gap Statistic)

根据肘部法则选择最合适的K值有时并不是那么清晰,因此斯坦福大学的Robert等教授提出了Gap Statistic方法。
Gap Statistic的定义为:
在这里插入图片描述
这里E(logDk)指的是logDk的期望。这个数值通常通过蒙特卡洛模拟产生,我们在样本里所在的矩形区域中(高维的话就是立方体区域)按照均匀分布随机地产生和原始样本数一样多的随机样本,并对这个随机样本做K-Means,从而得到一个Dk。如此往复多次,通常20次,我们可以得到20个log Dk。对这20个数值求平均值,就得到了E(logDk)的近似值。最终可以计算Gap Statisitc。而Gap statistic取得最大值所对应的K就是最佳的K。

Gap Statistic的基本思路是:引入参考的测值,这个参考值可以有Monte Carlo采样的方法获得。
在这里插入图片描述
B是sampling的次数。为了修正MC带来的误差,我们计算sk也即标准差来矫正Gap Statistic。
在这里插入图片描述
选择满足
在这里插入图片描述
的最小的k作为最优的聚类个数。下图阐释了Gap Statistic的过程。
在这里插入图片描述
在这里插入图片描述
Python实现:

import scipy
from  scipy.spatial.distance import euclidean
from sklearn.cluster import KMeans as k_meansdst = euclideank_means_args_dict = {'n_clusters': 0,# drastically saves convergence time'init': 'k-means++','max_iter': 100,'n_init': 1,'verbose': False,# 'n_jobs':8
}def gap(data, refs=None, nrefs=20, ks=range(1, 11)):"""I: NumPy array, reference matrix, number of reference boxes, number of clusters to testO: Gaps NumPy array, Ks input listGive the list of k-values for which you want to compute the statistic in ks. By Gap Statisticfrom Tibshirani, Walther."""shape = data.shapeif not refs:tops = data.max(axis=0)bottoms = data.min(axis=0)dists = scipy.matrix(scipy.diag(tops - bottoms))rands = scipy.random.random_sample(size=(shape[0], shape[1], nrefs))for i in range(nrefs):rands[:, :, i] = rands[:, :, i] * dists + bottomselse:rands = refsgaps = scipy.zeros((len(ks),))for (i, k) in enumerate(ks):k_means_args_dict['n_clusters'] = kkmeans = k_means(**k_means_args_dict)kmeans.fit(data)(cluster_centers, point_labels) = kmeans.cluster_centers_, kmeans.labels_disp = sum([dst(data[current_row_index, :], cluster_centers[point_labels[current_row_index], :]) for current_row_indexin range(shape[0])])refdisps = scipy.zeros((rands.shape[2],))for j in range(rands.shape[2]):kmeans = k_means(**k_means_args_dict)kmeans.fit(rands[:, :, j])(cluster_centers, point_labels) = kmeans.cluster_centers_, kmeans.labels_refdisps[j] = sum([dst(rands[current_row_index, :, j], cluster_centers[point_labels[current_row_index], :]) forcurrent_row_index in range(shape[0])])# let k be the index of the array 'gaps'gaps[i] = scipy.mean(scipy.log(refdisps)) - scipy.log(disp)return ks, gaps

(4)轮廓系数(Silhouette Coefficient)

Silhouette method 会衡量对象和所属簇之间的相似度——即内聚性(cohesion)。当把它与其他簇做比较,就称为分离性(separation)。该对比通过 silhouette 值来实现,后者在 [-1, 1] 范围内。Silhouette 值接近 1,说明对象与所属簇之间有密切联系;反之则接近 -1。若某模型中的一个数据簇,生成的基本是比较高的 silhouette 值,说明该模型是合适、可接受的。

方法:

1)计算样本i到同簇其他样本的平均距离a(i)。a(i)越小,说明样本i越应该被聚类到该簇。将a(i)称为样本i的簇内不相似度。簇C中所有样本的a(i)均值称为簇C的簇不相似度。

2)计算样本i到其他某簇C(j)的所有样本的平均距离b(ij),称为样本i与簇C(j)的不相似度。定义为样本i的簇间不相似度:b(i) =min{bi1, bi2, …, bik},b(i)越大,说明样本i越不属于其他簇。

3)根据样本i的簇内不相似度a i 和簇间不相似度b i ,定义样本i的轮廓系数:

在这里插入图片描述
4)判断:

  • s(i)接近1,则说明样本i聚类合理
  • s(i)接近-1,则说明样本i更应该分类到另外的簇
  • 若s(i) 近似为0,则说明样本i在两个簇的边界上

所有样本的s(i )的均值称为聚类结果的轮廓系数,是该聚类是否合理、有效的度量。但是,其缺陷是计算复杂度为O(n^2),需要计算距离矩阵,那么当数据量上到百万,甚至千万级别时,计算开销会非常巨大。

Python实现:


import numpy as np
from sklearn.cluster import KMeans
from sklearn import metrics
import matplotlib.pyplot as pltplt.figure(figsize=(8, 10))
plt.subplot(3, 2, 1)
x1 = np.array([1, 2, 3, 1, 5, 6, 5, 5, 6, 7, 8, 9, 7, 9])
x2 = np.array([1, 3, 2, 2, 8, 6, 7, 6, 7, 1, 2, 1, 1, 3])
X = np.array(list(zip(x1, x2))).reshape(len(x1), 2)
plt.xlim([0, 10])
plt.ylim([0, 10])
plt.title('Sample')
plt.scatter(x1, x2)
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'b']
markers = ['o', 's', 'D', 'v', '^', 'p', '*', '+']
tests = [2, 3, 4, 5, 8]
subplot_counter = 1
for t in tests:subplot_counter += 1plt.subplot(3, 2, subplot_counter)kmeans_model = KMeans(n_clusters=t).fit(X)for i, l in enumerate(kmeans_model.labels_):plt.plot(x1[i], x2[i], color=colors[l], marker=markers[l],ls='None')plt.xlim([0, 10])plt.ylim([0, 10])plt.title('K = %s, Silhouette method = %.03f' % (t, metrics.silhouette_score(X, kmeans_model.labels_,metric='euclidean')))
plt.show()

(5)Canopy算法

肘部法则(Elbow Method)和轮廓系数(Silhouette Coefficient)来对k值进行最终的确定,但是这些方法都是属于“事后”判断的,而Canopy算法的作用就在于它是通过事先粗聚类的方式,为k-means算法确定初始聚类中心个数和聚类中心点。

与传统的聚类算法(比如K-Means)不同,Canopy聚类最大的特点是不需要事先指定k值(即clustering的个数),因此具有很大的实际应用价值。与其他聚类算法相比,Canopy聚类虽然精度较低,但其在速度上有很大优势,因此可以使用Canopy聚类先对数据进行“粗”聚类,得到k值,以及大致的k个中心点,再使用K-Means进行进一步“细”聚类。所以Canopy+K-Means这种形式聚类算法聚类效果良好。

在这里插入图片描述
Canopy算法解析:

1)原始数据集合List按照一定的规则进行排序(这个规则是任意的,但是一旦确定就不再更改),初始距离阈值为T1、T2,且T1>T2(T1、T2的设定可以根据用户的需要,或者使用交叉验证获得)。
2)在List中随机挑选一个数据向量A,使用一个粗糙距离计算方式计算A与List中其他样本数据向量之间的距离d。
3)根据第2步中的距离d,把d小于T1的样本数据向量划到一个canopy中,同时把d小于T2的样本数据向量从候选中心向量名单(这里可以理解为就是List)中移除。
4)重复第2、3步,直到候选中心向量名单为空,即List为空,算法结束。

算法原理比较简单,就是对数据进行不断遍历,T2<dis<T1的可以作为中心名单,dis<T2的认为与canopy太近了,以后不会作为中心点,从list中删除,这样的话一个点可能属于多个canopy。

canopy效果图如下:
在这里插入图片描述
Canopy算法优势:

  • Kmeans对噪声抗干扰较弱,通过Canopy对比较小的NumPoint的Cluster直接去掉 有利于抗干扰。
  • Canopy选择出来的每个Canopy的centerPoint作为Kmeans比较科学。
  • 只是针对每个Canopy的内容做Kmeans聚类,减少相似计算的数量。

Canopy算法缺点:

  • 算法中 T1、T2(T2 < T1) 的确定问题

Python实现:

# -*- coding: utf-8 -*-
import math
import random
import numpy as np
import matplotlib.pyplot as pltclass Canopy:def __init__(self, dataset):self.dataset = datasetself.t1 = 0self.t2 = 0# 设置初始阈值def setThreshold(self, t1, t2):if t1 > t2:self.t1 = t1self.t2 = t2else:print('t1 needs to be larger than t2!')# 使用欧式距离进行距离的计算def euclideanDistance(self, vec1, vec2):return math.sqrt(((vec1 - vec2)**2).sum())# 根据当前dataset的长度随机选择一个下标def getRandIndex(self):return random.randint(0, len(self.dataset) - 1)def clustering(self):if self.t1 == 0:print('Please set the threshold.')else:canopies = []  # 用于存放最终归类结果while len(self.dataset) != 0:rand_index = self.getRandIndex()current_center = self.dataset[rand_index]  # 随机获取一个中心点,定为P点current_center_list = []  # 初始化P点的canopy类容器delete_list = []  # 初始化P点的删除容器self.dataset = np.delete(self.dataset, rand_index, 0)  # 删除随机选择的中心点Pfor datum_j in range(len(self.dataset)):datum = self.dataset[datum_j]distance = self.euclideanDistance(current_center, datum)  # 计算选取的中心点P到每个点之间的距离if distance < self.t1:# 若距离小于t1,则将点归入P点的canopy类current_center_list.append(datum)if distance < self.t2:delete_list.append(datum_j)  # 若小于t2则归入删除容器# 根据删除容器的下标,将元素从数据集中删除self.dataset = np.delete(self.dataset, delete_list, 0)canopies.append((current_center, current_center_list))return canopiesdef showCanopy(canopies, dataset, t1, t2):fig = plt.figure()sc = fig.add_subplot(111)colors = ['brown', 'green', 'blue', 'y', 'r', 'tan', 'dodgerblue', 'deeppink', 'orangered', 'peru', 'blue', 'y', 'r','gold', 'dimgray', 'darkorange', 'peru', 'blue', 'y', 'r', 'cyan', 'tan', 'orchid', 'peru', 'blue', 'y', 'r', 'sienna']markers = ['*', 'h', 'H', '+', 'o', '1', '2', '3', ',', 'v', 'H', '+', '1', '2', '^','<', '>', '.', '4', 'H', '+', '1', '2', 's', 'p', 'x', 'D', 'd', '|', '_']for i in range(len(canopies)):canopy = canopies[i]center = canopy[0]components = canopy[1]sc.plot(center[0], center[1], marker=markers[i],color=colors[i], markersize=10)t1_circle = plt.Circle(xy=(center[0], center[1]), radius=t1, color='dodgerblue', fill=False)t2_circle = plt.Circle(xy=(center[0], center[1]), radius=t2, color='skyblue', alpha=0.2)sc.add_artist(t1_circle)sc.add_artist(t2_circle)for component in components:sc.plot(component[0], component[1],marker=markers[i], color=colors[i], markersize=1.5)maxvalue = np.amax(dataset)minvalue = np.amin(dataset)plt.xlim(minvalue - t1, maxvalue + t1)plt.ylim(minvalue - t1, maxvalue + t1)plt.show()if __name__ == "__main__":dataset = np.random.rand(500, 2)  # 随机生成500个二维[0,1)平面点t1 = 0.6t2 = 0.4gc = Canopy(dataset)gc.setThreshold(t1, t2)canopies = gc.clustering()print('Get %s initial centers.' % len(canopies))
showCanopy(canopies, dataset, t1, t2)

参考资料:

  • https://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set
  • https://www.quora.com/How-can-we-choose-a-good-K-for-K-means-clustering
  • https://stackoverflow.com/questions/1793532/how-do-i-determine-k-when-using-k-means-clustering

http://www.ppmy.cn/news/130834.html

相关文章

k近邻算法

本文来自的CSDN 博客 &#xff0c;全文地址请点击&#xff1a;https://blog.csdn.net/qq_35082030/article/details/60965320?utm_sourcecopy 0. 写在前面 在这一讲的讨论班中&#xff0c;我们将要讨论一下K近邻模型。可能有人会说&#xff0c;K近邻模型有什么好写的&#x…

华为k662c的虚拟服务器,华为k662c路由器怎么设置 | 华为k662c路由器设置_什么值得买...

WIFI6光猫(无线路由器)华为K662C使用设置 2021-01-09 18:50:50 39点赞 155收藏 64评论 华为K662C是一个三合一的设备,有3种工作模式。一种是光猫模式,另外一种是无线路由器模式,还有一种是光纤组网模式(设置较复杂,需要补全shell和更新固件程序,不推荐普通用户尝试,这里就…

K-D树算法的一些总结

在2016年ACM ICPC青岛站的比赛中&#xff0c;一道K-D树问题成为了金银牌的分界线&#xff0c;最后由我们队一个强力队友写出了该题&#xff0c;其实那题本来该由我负责的&#xff0c;都怪我学艺不精。 k-d树&#xff08;k-dimensional树的简称&#xff09;&#xff0c;是一种分…

不足20行 python 代码,高效实现 k-means 均值聚类算法

scikti-learn 将机器学习分为4个领域&#xff0c;分别是分类(classification)、聚类(clustering)、回归(regression)和降维(dimensionality reduction)。k-means均值算法虽然是聚类算法中比较简单的一种&#xff0c;却包含了丰富的思想内容&#xff0c;非常适合作为初学者的入门…

k-d tree树 近邻算法

k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据结构。主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索)。 应用背景 SIFT算法中做特征点匹配的时候就会利用到k-d树。而特征点匹配实际上就是一个通过距离函数在高维矢量之间进行相似性检索的问题。…

k-近邻算法

KNN 概述 k-近邻&#xff08;kNN, k-NearestNeighbor&#xff09;算法是一种基本分类与回归方法&#xff0c;我们这里只讨论分类问题中的 k-近邻算法。 一句话总结: 近朱者赤近墨者黑&#xff01; k 近邻算法的输入为实例的特征向量&#xff0c;对应于特征空间的点&#xff…

k-d tree算法

k-d树&#xff08;k-dimensional树的简称&#xff09;&#xff0c;是一种分割k维数据空间的数据结构。主要应用于多维空间关键数据的搜索&#xff08;如&#xff1a;范围搜索和最近邻搜索&#xff09;。 应用背景 SIFT算法中做特征点匹配的时候就会利用到k-d树。而特征点匹配实…

K-D Tree 算法详解及Python实现

K-D Tree 算法 k−d tree k − d t r e e 即 k−dimensional tree k − d i m e n s i o n a l t r e e &#xff0c;是一种分割k维数据空间的数据结构&#xff0c;常用来多维空间关键数据的搜索(如&#xff1a;范围搜素及近邻搜索)&#xff0c;是二叉空间划分树的一个特例。通…