当前位置:   article > 正文

数学建模笔记——K-means聚类(python实现)_python k-means聚类

python k-means聚类

一、K-means简介

K-means是机器学习中常见的一种非监督学习分类算法,主要是对一个不带标签的数据集进行相似性分析,进而将其分成若干类。

二、一些基本概念

  1. “距离”:我们通常是使用欧式距离来衡量两个样本间的相似度,其计算公式为:gif.latex?d_%7Bi%2C%20j%7D%3D%5Csqrt%7B%5Csum_%7Bk%3D1%7D%5E%7Bm%7D%5Cleft%20%28%20X_%7Bik%7D-X_%7Bjk%7D%20%5Cright%20%29%5E%7B2%7D%7D, 其中,dij表示样本i和样本j的距离,m是特征数。
  2. 簇:分出的每个类称为一个簇,其中簇的数量人为确定,一般用K表示,是一个超参数。
  3. 簇中心:簇中心是一个类整体特征的代表,其一般是整个类别中的所有样本取平均,其计算公式为:gif.latex?C_%7Bk%7D%3D%20%5Cfrac%7B1%7D%7Bc%7D%5Csum_%7Bi%3D1%7D%5E%7Bc%7D%7BX_%7Bik%7D%7D%2C%5Cleft%20%28%20k%3D1%2C2%2C...%2Cm%20%5Cright%20%29,其中Ck表示簇中心第k个特征的值,c为该簇总样本数。

三、算法基本步骤

  1. 从原始数据中随机选取K个样本作为初始簇中心
  2. 计算各样本与各簇中心的距离,并将其分到距离最近的簇
  3. 重新计算簇中心
  4. 重复步骤2,3直至簇中心不在发生变化

 四、聚类结果评估

聚类完成之后,我们通常用轮廓系数来评估聚类结果的好坏,其计算公式为:gif.latex?S_%7Bi%7D%3D%5Cfrac%7Bb_%7Bi%7D-a_%7Bi%7D%7D%7Bmax%5Cleft%20%28%20a_%7Bi%7D%2Cb_%7Bi%7D%20%5Cright%20%29%7D,其中ai,bi分别表示第i个样本的凝聚度和分散度。凝聚度用于衡量簇内样本的密集程度,分散度则用于衡量簇间的分散程度。ai的计算公式为:gif.latex?a_%7Bi%7D%3D%5Cfrac%7B1%7D%7Bc-1%7D%5Csum_%7Bj%5Cneq%20i%7D%5E%7B%7Dd_%7Bi%2Cj%7D,其中dij表示同一簇内第i个样本与第j个样本的距离,c为该簇总样本量。而分散度计算方法与ai类似,将同一个簇改为不同的簇分别计算距离平均值取最小即可。最终结果取所有样本轮廓系数的平均值来衡量分类结果的好坏,其大小一般属于[-1,1]且越靠近1分类效果越好,越靠近-1分类效果越差。

五、python实现 

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. class Kmeans:
  4. def __init__(self):
  5. # 原始数据
  6. self.dataset = None
  7. # 分类的簇数
  8. self.K = None
  9. # 分类结果
  10. self.belongs = None
  11. # 分类簇
  12. self.piles = None
  13. # 簇中心
  14. self.centres = None
  15. # 轮廓系数
  16. self.sils = None
  17. # 计算欧式距离
  18. @staticmethod
  19. def cal_Eu_dis(sample1, sample2):
  20. sample1 = np.array(sample1, dtype=np.float32)
  21. sample2 = np.array(sample2, dtype=np.float32)
  22. assert sample1.shape == sample2.shape
  23. dis = np.sqrt(np.sum((sample1 - sample2) ** 2))
  24. return dis
  25. # 计算单个簇的中心
  26. @staticmethod
  27. def cal_centre(category_data):
  28. category_data = np.array(category_data, dtype=np.float32)
  29. return np.mean(category_data, axis=0).reshape(1, -1)
  30. # 计算每个簇的中心
  31. def cal_centres(self):
  32. self.piles = self.split_dataset()
  33. self.centres = self.cal_centre(self.piles['pile0'])
  34. for k in range(1, self.K):
  35. category_data = self.piles['pile' + str(k)]
  36. self.centres = np.concatenate((self.centres, self.cal_centre(category_data)), axis=0)
  37. return self.centres
  38. # 计算单个样本与各簇中心的距离
  39. def cal_sample_centre(self, sample):
  40. assert len(self.centres.shape) == 2
  41. dis = []
  42. for centre in self.centres:
  43. dis.append(self.cal_Eu_dis(sample, centre))
  44. return np.array(dis).reshape(1, -1)
  45. # 计算整个个数据集与各簇中心的距离
  46. def cal_dataset_centre(self):
  47. sample = next(iter(self.dataset))
  48. dis = self.cal_sample_centre(sample)
  49. for sample in self.dataset[1:]:
  50. dis = np.concatenate((dis, self.cal_sample_centre(sample)), axis=0)
  51. return dis
  52. # 计算单个样本的凝聚度
  53. @staticmethod
  54. def cal_sample_condensation(sample, pile):
  55. sample = np.tile(sample, (len(pile), 1))
  56. assert sample.shape == pile.shape
  57. C = np.sum(np.sqrt(np.sum((sample - pile) ** 2, axis=1)))
  58. return C / (len(pile) - 1)
  59. # 计算整个数据集的凝聚度
  60. def cal_dataset_condensation(self):
  61. a = np.zeros(len(self.dataset), dtype=np.float32)
  62. for k, data in enumerate(self.dataset):
  63. pile = self.piles['pile' + str(int(self.belongs[k]))]
  64. a[k] = self.cal_sample_condensation(data, pile)
  65. return a
  66. # 计算分离度
  67. @staticmethod
  68. def cal_sample_separation(sample, other_piles):
  69. S = []
  70. for pile in other_piles:
  71. sample = np.tile(sample, (pile.shape[0], 1))
  72. assert sample.shape == pile.shape
  73. D = np.sum(np.sqrt(np.sum((sample - pile) ** 2, axis=1)))
  74. S.append(D / (len(pile)))
  75. sample = sample[0, :]
  76. return np.min(S)
  77. # 计算整个数据集的分离度
  78. def cal_dataset_separation(self):
  79. b = np.zeros(len(self.dataset), dtype=np.float32)
  80. for k, data in enumerate(self.dataset):
  81. # 起点---生成other_piles---起点
  82. other_piles = []
  83. for l in range(len(self.piles)):
  84. if not np.equal(l, int(self.belongs[k])):
  85. other_piles.append(self.piles['pile' + str(l)])
  86. # 结束------结束
  87. b[k] = self.cal_sample_separation(data, other_piles)
  88. return b
  89. def cal_dataset_silhouette_score(self):
  90. a = self.cal_dataset_condensation()
  91. b = self.cal_dataset_separation()
  92. c = np.concatenate((a.reshape(-1, 1), b.reshape(-1, 1)), axis=1)
  93. r = (b - a) / np.max(c, axis=1)
  94. return np.sum(r) / len(r)
  95. # 将整体数据集按簇划分
  96. def split_dataset(self):
  97. self.piles = {}
  98. for k in range(self.K):
  99. self.piles['pile' + str(k)] = self.dataset[np.where(self.belongs == k)[0]]
  100. return self.piles
  101. # 若是二维数据则可画图
  102. def plot(self):
  103. for i, category_data in enumerate(self.piles.values()):
  104. plt.scatter(category_data[:, 0], category_data[:, 1], marker='o', s=100, alpha=0.8)
  105. plt.scatter(self.centres[i, 0], self.centres[i, 1], marker='x', s=20, alpha=1)
  106. centre = np.tile(self.centres[i], [len(category_data), 1])
  107. plt.plot([centre[:, 0], category_data[:, 0]], [centre[:, 1], category_data[:, 1]], c='grey')
  108. plt.show()
  109. def __call__(self, dataset, K):
  110. # 原始数据
  111. self.dataset = dataset
  112. # 分类的簇数
  113. self.K = K
  114. # 随机选取K个簇中心
  115. self.centres = self.dataset[np.random.choice(self.dataset.shape[0], self.K, replace=False)]
  116. pre_centres = np.zeros((self.K, self.dataset.shape[1]))
  117. self.belongs = np.zeros((self.dataset.shape[0], 1))
  118. while not np.equal(pre_centres, self.centres).all():
  119. # 计算一个数据集中所有样本到簇中心的距离
  120. distance = self.cal_dataset_centre()
  121. # 更新样本所属簇
  122. self.belongs = np.argmin(distance, axis=1).reshape(-1, 1)
  123. # 保存原有簇中心
  124. pre_centres = self.centres
  125. # 重新计算簇中心
  126. self.centres = self.cal_centres()
  127. self.piles = self.split_dataset()
  128. self.sils = self.cal_dataset_silhouette_score()
  129. if dataset.shape[1] == 2:
  130. self.plot()
  131. return np.concatenate((self.dataset, self.belongs), axis=1)

主要的属性有分类结果belongs、分类簇piles、簇中心centres以及轮廓系数sils。需要传入的参数有原始数据dataset(数据结构为ndarray)和簇的数量K。

六、模型使用

我们可以随机生成100个样本,每个样本两个特征。接着实例化Kmeans类,生成结果result。(运行下列代码时记得导入前面的Kmeans类哟~)

  1. def load_data():
  2. dataset = np.random.rand(100, 2)
  3. K = 4
  4. return dataset, K
  5. dataset, K = load_data()
  6. k = Kmeans()
  7. result = k(dataset, K)

结果图069aa362247f4202a17cb742d7437000.png

 其他还有些细节我就不详细介绍了,大家可以自己去跑一些代码,实践出真知嘛。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/72904
推荐阅读
相关标签
  

闽ICP备14008679号