赞
踩
其实Kmeans聚类算法在YOLOv2(【YOLO系列】YOLOv2论文超详细解读(翻译 +学习笔记))中我们就见到了,那时候只是简单地了解了一下。后来在这学期的数据挖掘课程的期末汇报中,我又抽中了这个算法,于是又重新学习了一遍。另外最近在看一些改进的论文,很多摘要中也都提到将Kmeans改为Kmeans++作为创新点(主要是YOLO中对anchor做改进,叫作多尺度自适应锚框初始化)。下面就让我们具体了解一下Kmeans和Kmeans++算法吧!
目录
聚类(Clustering) 是按照某个特定标准(如距离)把一个数据集分割成不同的类或簇,使得同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇中的数据对象的差异性也尽可能地大。也就是说,聚类后同一类的数据尽可能聚集到一起,不同类数据尽量分离。
聚类和分类
- 聚类(Clustering):是指把相似的数据划分到一起,具体划分的时候并不关心这一类的标签,目标就是把相似的数据聚合到一起,聚类是一种无监督学习(Unsupervised Learning)方法。
- 分类(Classification):是把不同的数据划分开,其过程是通过训练数据集获得一个分类器,再通过分类器去预测未知数据,分类是一种监督学习(Supervised Learning)方法。
Kmeans 算法是一种常用的聚类算法,它是基于划分方法聚类的。它的原理是将数据划分为k个簇,每个簇由距离中心最近的数据点组成,基于计算样本与中心点的距离归纳各簇类下的所属样本,迭代实现样本与其归属的簇类中心的距离为最小的目标。
简单来说,Kmeans 算法就是通过不断地调整簇的中心点,并将数据点指派到距离它最近的中心点所在的簇,来逐步将数据划分成若干个簇。
常见目标函数:
算法执行步骤如下:
【注意】常用的中止条件有迭代次数、最小平方误差MSE、簇中心点变化率
Kmeans划分k个簇,不同k值的情况对最终结果的影响至关重要,不同的k值会带来不同的结果,如下图所示:
一般情况下,我们确定k值常用两种方法:先验法、手肘法
先验法比较简单,就是凭借着业务知识确定k的取值。比如对于iris花数据集,我们大概知道有三种类别,可以按照k=3做聚类验证。从下图可看出,对比聚类预测与实际的iris种类是比较一致的。
可以知道k值越大,划分的簇群越多,对应的各个点到簇中心的距离的平方的和(类内距离,WSS)越低,我们通过确定WSS随着K的增加而减少的曲线拐点,作为K的取值。
Kmeans具体代码如下:
- import matplotlib.pyplot as plt
- import numpy as np
- import pandas as pd
-
-
- def assignment(df, center, colmap):
- # 计算所有样本分别对K个类别中心点的距离
- for i in center.keys():
- df["distance_from_{}".format(i)] = np.sqrt((df["x"] - center[i][0]) ** 2 + (df["y"] - center[i][1]) ** 2)
-
- distance_from_centroid_id = ['distance_from_{}'.format(i) for i in center.keys()]
- df["closest"] = df.loc[:, distance_from_centroid_id].idxmin(axis=1) # "closest"列表示每个样本点离哪个类别的中心点距离最近
- print(df["closest"])
- df["closest"] = df["closest"].map(lambda x: int(x.lstrip('distance_from_')))
- df["color"] = df['closest'].map(lambda x: colmap[x])
- return df
-
-
- def update(df, centroids):
- # 更新K个类别的中心点
- for i in centroids.keys():
- # 每个类别的中心点为 属于该类别的点的x、y坐标的平均值
- centroids[i][0] = np.mean(df[df['closest'] == i]['x'])
- centroids[i][1] = np.mean(df[df['closest'] == i]['y'])
- return centroids
-
-
- def main():
- df = pd.DataFrame({
- 'x': [12, 20, 28, 18, 10, 29, 33, 24, 45, 45, 52, 51, 52, 55, 53, 55, 61, 64, 69, 72, 23],
- 'y': [39, 36, 30, 52, 54, 20, 46, 55, 59, 63, 70, 66, 63, 58, 23, 14, 8, 19, 7, 24, 77]
- })
- k = 3
-
- # 一开始随机指定 K个类的中心点
- center = {
- i: [np.random.randint(0, 80), np.random.randint(0, 80)]
- for i in range(k)
- }
-
- colmap = {0: "r", 1: "g", 2: "b"}
- df = assignment(df, center, colmap)
-
- for i in range(10): # 迭代10次
- closest_center = df['closest'].copy(deep=True)
- center = update(df, center) # 更新K个类的中心点
- df = assignment(df, center, colmap) # 类别中心点更新后,重新计算所有样本点到K个类别中心点的距离
- if closest_center.equals(df['closest']): # 若各个样本点对应的聚类类别不再变化,则结束聚类
- break
-
- plt.scatter(df['x'], df['y'], color=df['color'], alpha=0.5, edgecolor='b')
- for j in center.keys():
- plt.scatter(*center[j], color=colmap[j], linewidths=6)
- plt.xlim(0, 80)
- plt.ylim(0, 80)
- plt.show()
-
-
- if __name__ == '__main__':
- main()
实现效果:
优点:
缺点:
原始Kmeans算法最开始随机选取数据集中k个点作为聚类中心,k个初始化的质心的位置选择对最后的聚类结果和运行时间都有很大的影响,因此需要选择合适的k个质心。如果仅仅是完全随机的选择,有可能导致算法收敛很慢。Kmeans++算法主要对对K-Means初始值选取的方法的优化。也就是说,Kmeans++算法与Kmeans算法最本质的区别是在k个聚类中心的初始化过程。
其实通过上面的介绍,我们知道了 Kmeans++算法和Kmeans算法就是选择一开始的k个聚类中心点的方法有差别而已。其初始点的选择过程如下:
其余训练过程与KMeans一致。
Kmeans++具体代码如下:
- import matplotlib.pyplot as plt
- import numpy as np
- import pandas as pd
- import random
-
-
- def select_center(first_center, df, k, colmap):
- center = {}
- center[0] = first_center
-
- for i in range(1, k):
- df = assignment(df, center, colmap)
- sum_closest_d = df.loc[:, 'cd'].sum() # cd = 最近中心点的距离。把所有样本点对应最近中心点的距离都加在一起
- df["p"] = df.loc[:, 'cd'] / sum_closest_d
- sum_p = df["p"].cumsum()
-
- # 下面是轮盘法取新的聚类中心点
- next_center = random.random()
- for index, j in enumerate(sum_p):
- if j > next_center:
- break
- center[i] = list(df.iloc[index].values)[0:2]
-
- return center
-
-
- def assignment(df, center, colmap):
- # 计算所有样本分别对K个类别中心点的距离
- for i in center.keys():
- df["distance_from_{}".format(i)] = np.sqrt((df["x"] - center[i][0]) ** 2 + (df["y"] - center[i][1]) ** 2)
-
- distance_from_centroid_id = ['distance_from_{}'.format(i) for i in center.keys()]
- df["closest"] = df.loc[:, distance_from_centroid_id].idxmin(axis=1) # "closest"列表示每个样本点离哪个类别的中心点距离最近
- df["cd"] = df.loc[:, distance_from_centroid_id].min(axis=1)
- df["closest"] = df["closest"].map(lambda x: int(x.lstrip('distance_from_')))
- df["color"] = df['closest'].map(lambda x: colmap[x])
- return df
-
-
- def update(df, centroids):
- # 更新K个类别的中心点
- for i in centroids.keys():
- # 每个类别的中心点为 属于该类别的点的x、y坐标的平均值
- centroids[i][0] = np.mean(df[df['closest'] == i]['x'])
- centroids[i][1] = np.mean(df[df['closest'] == i]['y'])
- return centroids
-
-
- def main():
- df = pd.DataFrame({
- 'x': [12, 20, 28, 18, 10, 29, 33, 24, 45, 45, 52, 51, 52, 55, 53, 55, 61, 64, 69, 72, 23],
- 'y': [39, 36, 30, 52, 54, 20, 46, 55, 59, 63, 70, 66, 63, 58, 23, 14, 8, 19, 7, 24, 77]
- })
- k = 3
- colomap = {0: "r", 1: "g", 2: "b"}
- first_center_index = random.randint(0, len(df) - 1)
- first_center = [df['x'][first_center_index], df['y'][first_center_index]]
- center = select_center(first_center, df, k, colomap)
-
- df = assignment(df, center, colomap)
-
- for i in range(10): # 迭代10次
- closest_center = df['closest'].copy(deep=True)
- center = update(df, center) # 更新K个类的中心点
- df = assignment(df, center, colomap) # 类别中心点更新后,重新计算所有样本点到K个类别中心点的距离
- if closest_center.equals(df['closest']): # 若各个样本点对应的聚类类别不再变化,则结束聚类
- break
-
- plt.scatter(df['x'], df['y'], color=df['color'], alpha=0.5, edgecolor='b')
- for j in center.keys():
- plt.scatter(*center[j], color=colomap[j], linewidths=6)
- plt.xlim(0, 80)
- plt.ylim(0, 80)
- plt.show()
-
-
- if __name__ == '__main__':
- main()
实现效果:
优点:
缺点:
本文参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。