文章目录
- 聚类概念
- 聚类与Kmeans
-
- K-Means算法步骤:
- 优缺点
- K-Means++算法
-
- K-Means++算法的基本思路
- 二分K-Means算法
- Mini Batch K-Means算法
- KMeans小结
- 谱和谱聚类
聚类概念
- 无监督问题:没有标签
- 聚类:相似的东西分到一组
- 难点:如果评估,如何调参
聚类与Kmeans K-Means算法,也被称为K-平均或K-均值算法,是一种广泛使用的聚类算法。
与分类、序列标注等任务不同,聚类是在事先并不知道任何样本标签的情况下,通过数据之间的内在关系把样本划分为若干类别,使得同类别样本之间的相似度高,不同类别之间的样本相似度低(即增大类内聚,减少类间距)。
文章图片
K-Means算法是聚焦于相似的无监督的算法,以距离作为数据对象间相似性度量的标准,即数据对象间的距离越小,则它们的相似性越高,则它们越有可能在同一个类簇。之所以被称为K-Means是因为它可以发现k个不同的簇,且每个簇的中心采用簇中所含值的均值计算而成。
文章图片
在介绍K-Means算法之前,先讨论下簇识别(cluster identification)。簇识别给出聚类结果的含义。假定有一些数据,现在将相似的数据归在一起,簇识别会告诉我们这些簇到达都是些什么。聚类和分类最大的不同在于,分类的目标事先已知,而聚类则不一样。因为其产生的结果和分类相同,而只是类别没有预先定义。K-Means是发现给定数据集的k个簇的算法。簇个数k是用户给定的,每一个簇通过其质心(centroid),即簇中所有点的中心来描述。
这个算法其实很简单,如下图所示:
文章图片
从上图中,我们可以看到,A, B, C, D, E 是五个在图中点。而灰色的点是我们的种子点,也就是我们用来找点群的点。有两个种子点,所以K=2。
- 随机在图中取K(这里K=2)个种子点。
- 然后对图中的所有点求到这K个种子点的距离,假如点Pi离种子点Si最近,那么Pi属于Si点群。(上图中,我们可以看到A,B属于上面的种子点,C,D,E属于下面中部的种子点)
- 接下来,我们要移动种子点到属于他的“点群”的中心。(见图上的第三步)
- 然后重复第2)和第3)步,直到种子点没有移动(我们可以看到图中的第四步上面的种子点聚合了A,B,C,下面的种子点聚合了D,E)。
- 初始化常数K,随机初始化k个聚类中心
- 重复计算以下过程,知道聚类中心不再改变
- 计算每个样本与每个聚类中心点的距离,将样本划分到最近的中心点
- 计算划分到每个类别中的所有样本特征的均值,并将该均值作为每个类新的聚类中心
- 输出最终的聚类中心以及每个样本所属的类别。
文章图片
算法中使用到距离可以是任何的距离计算公式,最常用的是欧氏距离,应用时具体应该选择哪种距离计算方式,需要根据具体场景确定。
import numpy as np
from matplotlib import pyplot as pltdef euclidean_distance(vecA, vecB):
'''计算vecA与vecB之间的欧式距离'''
# return np.sqrt(np.sum(np.square(vecA - vecB)))
return np.linalg.norm(vecA - vecB)def random_centroids(data, k):
''' 随机创建k个中心点'''
dim = np.shape(data)[1]# 获取向量的维度
centroids = np.mat(np.zeros((k, dim)))
for j in range(dim):# 随机生成每一维中最大值和最小值之间的随机数
min_j = np.min(data[:, j])
range_j = np.max(data[:, j]) - min_j
centroids[:, j] = min_j * np.mat(np.ones((k, 1))) + np.random.rand(k, 1) * range_j
return centroidsdef KMeans(data, k, distance_func=euclidean_distance):
'''根据k-means算法求解聚类的中心'''
m = np.shape(data)[0]# 获得行数m
cluster_assment = np.mat(np.zeros((m, 2)))# 初试化一个矩阵,用来记录簇索引和存储距离平方
centroids = random_centroids(data, k)# 生成初始化点
cluster_changed = True# 判断是否需要重新计算聚类中心
while cluster_changed:
cluster_changed = False
for i in range(m):
distance_min = np.inf# 设置样本与聚类中心之间的最小的距离,初始值为正无穷
index_min = -1# 所属的类别
for j in range(k):
distance_ji = distance_func(centroids[j, :], data[i, :])
if distance_ji &distance_min:
distance_min = distance_ji
index_min = j
if cluster_assment[i, 0] != index_min:
cluster_changed = True
cluster_assment[i, :] = index_min, distance_min ** 2# 存储距离平方
for cent in range(k):# 更新质心,将每个族中的点的均值作为质心
pts_in_cluster = data[np.nonzero(cluster_assment[:, 0].A == cent)[0]]
centroids[cent, :] = np.mean(pts_in_cluster, axis=0)
return centroids, cluster_assmentdef show_cluster(data, k, centroids, cluster_assment):
num, dim = data.shape
mark = ['or', 'ob', 'og', 'oy', 'oc', 'om']
for i in range(num):
mark_index = int(cluster_assment[i, 0])
plt.plot(data[i, 0], data[i, 1], mark[mark_index])
for i in range(k):
plt.plot(centroids[i, 0], centroids[i, 1], 'o', markeredgecolor='k', markersize=16)
plt.show()if __name__ == "__main__":
data = https://www.it610.com/article/[]
f = open("sz.txt", 'r')
for line in f:
data.append([float(line.split(',')[0]), float(line.split(',')[1])])
data = https://www.it610.com/article/np.array(data)
k = 4
centroids = random_centroids(data, k)
centroids, cluster_assment = KMeans(data, k)
show_cluster(data, k, centroids, cluster_assment)
结果
文章图片
优缺点 优点:
简单,快速,适合常规数据集
劣势:
- K值难确定
- 复杂度与样本呈线性关系
- 很难发现任意形状的簇
K-Means++算法的基本思路 K-Means++算法在聚类中心的初始化过程中的基本原则是使得初始的聚类中心之间的相互距离尽可能远,这样可以避免出现上述的问题。K-Means++算法的初始化过程如下所示:
- 在数据集中随机选择一个样本点作为第一个初始化的聚类中心
- 选择出其余的聚类中心:
- 计算样本中的每一个样本点与已经初始化的聚类中心之间的距离,并选择其中最短的距离,记为d_i
- 选择一个新的数据点作为新的聚类中心,选择的原则是:距离较大的点,被选取作为聚类中心的概率较大
- 重复上述过程,直到k个聚类中心都被确定
对k个初始化的聚类中心,利用K-Means算法计算最终的聚类中心。
def nearest(data, cluster_centers, distance_func=euclidean_distance):
min_dist = np.inf
m = np.shape(cluster_centers)[0]# 当前已经初始化的聚类中心的个数
for i in range(m):
d = distance_func(data, cluster_centers[i, ])# 计算point与每个聚类中心之间的距离
if min_dist >
d:# 选择最短距离
min_dist = d
return min_distdef get_centroids(data, k, distance_func=euclidean_distance):
m, n = np.shape(data)
cluster_centers = np.mat(np.zeros((k, n)))
index = np.random.randint(0, m)# 1、随机选择一个样本点为第一个聚类中心
cluster_centers[0, ] = np.copy(data[index, ])
d = [0.0 for _ in range(m)]# 2、初始化一个距离的序列
for i in range(1, k):
sum_all = 0
for j in range(m):
d[j] = nearest(data[j, ], cluster_centers[0:i, ], distance_func)# 3、对每一个样本找到最近的聚类中心点
sum_all += d[j]# 4、将所有的最短距离相加
sum_all *= random()# 5、取得sum_all之间的随机值
for j, di in enumerate(d):# 6、获得距离最远的样本点作为聚类中心点
sum_all -= di
if sum_all >
0:
continue
cluster_centers[i] = np.copy(data[j, ])
break
return cluster_centers
执行结果:(计算出来的最终聚类中心与K-Means在正常情况下计算出来的聚类中心一致)
文章图片
二分K-Means算法 K-Means是一种最大期望算法,这类算法会在“期望”和“最大化”两个阶段不断迭代。比如K-Means的期望阶段是将各个点分配到它们所“期望”的分类中,然后在最大化阶段重新计算中心点的位置。再继续讨论K-Means算法之前,我想先介绍一下登山式算法。
假设我们想要登上一座山的顶峰,可以通过以下步骤实现:
- 在山上随机选取一个点作为开始
- 向高处爬一点
- 重复第2步,直到没有更高的点
这种做法看起来很合理,比如对于下图所示的山峰:
文章图片
无论我们从哪个点开始攀登,最终都可以达到顶峰。但对于下面这张图:
文章图片
所以说,这种简单的登山式算法并不一定能得到最优解。K-Means就是这样一种算法,它不能保证最终结果是最优的,因为我们一开始选择的中心点是随机的,很有可能就会选到上面的A点,最终获得局部最优解B点。因此,最终的聚类结果和起始点的选择有很大关系。但尽管如此,K-Means通常还是能够获得良好的结果的。
K-Means算法收敛,但是聚类效果较差的原因是,K-Means算法收敛到了局部最小值,而非全局最小值(局部最小值指结果还可以但并非最好结果,全局最小值是可能的最好结果)。
一种用于度量聚类效果的指标是SSE(Sum of Squared Error,误差平方和)。SSE值越小表示数据点越接近他们的质心,聚类效果也最好。因为对误差取了平方,因此更加重视远离中心的点。一种肯定可以降低SSE的方法是增加族的个数,但这违背了聚类的目标。聚类的目标是在保持簇数目不变的情况下提高簇的质量。
文章图片
如何对下图的的结果进行改进?你只可以多生成的簇进行后处理,一种方法是将具有最大SSE值的簇划分成为2个簇。具体实现时可以将最大簇包含的点过滤出来并在这些点上运行K-Means算法,其中k设为2。
文章图片
为了保持簇总数不变,可以将某两个簇进行合并。从上图中很明显就可以看出,应该将上图下部两个出错的簇质心进行合并。那么问题来了,我们可以很容易对二维数据上的聚类进行可视化, 但是如果遇到40维的数据应该如何去做?
有两种可以量化的办法:合并最近的质心,或者合并两个使得SSE增幅最小的质心。 第一种思路通过计算所有质心之间的距离, 然后合并距离最近的两个点来实现。第二种方法需要合并两个簇然后计算总SSE值。必须在所有可能的两个簇上重复上述处理过程,直到找到合并最佳的两个簇为止。接下来将讨论利用上述簇划分技术得到更高的聚类结果的方法。
为克服K-Means算法收敛于局部最小的问题,有人提出了另一种称为二分K-Means(bisecting K-Means)的算法。该算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于对其划分时候可以最大程度降低 SSE(平方和误差)的值。上述基于 SSE 的划分过程不断重复,直到得到用户指定的簇数目为止。
二分 K-Means 聚类算法伪代码:
- 将所有点看成一个簇
- 当簇数目小于 k 时,对于每一个簇
- 计算总误差
- 在给定的簇上面进行 KMeans 聚类(k=2)
- 计算将该簇一分为二之后的总误差
- 选择使得误差最小的那个簇进行划分操作
另一种做法是选择 SSE 最大的簇进行划分,直到簇数目达到用户指定的数目位置。
Mini Batch K-Means算法是K-Means算法的变种,采用小批量的数据子集减小计算时间,同时仍试图优化目标函数,这里所谓的小批量是指每次训练算法时所随机抽取的数据子集,采用这些随机产生的子集进行训练算法,大大减小了计算时间,与其他算法相比,减少了k-均值的收敛时间,小批量k-均值产生的结果,一般只略差于标准算法。
该算法的迭代步骤有两步:
- 从数据集中随机抽取一些数据形成小批量,把他们分配给最近的质心
- 更新质心
与K均值算法相比,数据的更新是在每一个小的样本集上。对于每一个小批量,通过计算平均值得到更新质心,并把小批量里的数据分配给该质心,随着迭代次数的增加,这些质心的变化是逐渐减小的,直到质心稳定或者达到指定的迭代次数,停止计算。
文章图片
KMeans小结 K-Means是个简单实用的聚类算法,这里对K-Means的优缺点做一个总结。
K-Means的主要优点有:
- 原理比较简单,实现也是很容易,收敛速度快(在大规模数据集上收敛较慢,可尝试使用Mini Batch K-Means算法)。
- 聚类效果较优。
- 算法的可解释度比较强。
- 主要需要调参的参数仅仅是簇数k。
- K值的选取不好把握(实际中K值的选定是非常难以估计的,很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适。下篇博客专门讲这个)
- 对于不是凸的数据集比较难收敛(于密度的聚类算法更加适合,比如DBSCAN算法,后面再介绍DBSCAN算法)
- 如果各隐含类别的数据不平衡,比如各隐含类别的数据量严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。
- 采用迭代方法,得到的结果只是局部最优。(可以尝试采用二分K-Means算法)
- 对噪音和异常点比较敏感。(改进1:离群点检测的LOF算法,通过去除离群点后再聚类,可以减少离群点和孤立点对于聚类效果的影响;改进2:改成求点的中位数,这种聚类方式即K-Mediods聚类)
- 对初始值敏感(初始聚类中心的选择,可以尝试采用二分K-Means算法或K-Means++算法)
- 时间复杂度高O(nkt),其中n是对象总数,k是簇数,t是迭代次数。
- 只适用于数值型数据,只能发现球型类簇。
- 方阵的谱半径为最大的特征值
- 矩阵A的谱半径:( A T A A^TA ATA)的最大特征值
拉普拉斯LE详细解释请通过:https://blog.csdn.net/weixin_48167570/article/details/124056136
推荐阅读
- 机器学习|【进阶版】机器学习之神经网络与深度学习基本知识和理论原理(07)
- 机器学习|【进阶版】机器学习之贝叶斯分类器细节回顾及原理完善(10)
- 机器学习|【进阶版】机器学习之决策树知识与易错点总结(06)
- 机器学习|【进阶版】机器学习之线性模型介绍及过拟合欠拟合解决方法岭回归、loss回归、elasticnet回归(05)
- 机器学习|机器学习之EM算法的原理及推导(三硬币模型)及Python实现
- 机器学习|【进阶版】机器学习之特征降维、超参数调优及检验方法(04)
- FIT3178 iOS
- 深度学习|深度学习面试题——深度学习的技术发展史
- 资讯|小米造车 500 天,现在怎么样了()