1.简介
K-means算法也称为K_均值算法,用于聚类算法。聚类是一种无监督学习,他将相似的对象归于一个簇中,簇中心通过簇中所有点的均值来计算。聚类算法与分类算法的主要区别就是分类的目标类别已知,而聚类的目标类别未知。
2.聚类概念
简单来说是将一堆没有标签的数据分为不同的簇,每个簇中数据为一类
B. 在聚类的过程中,图中左上角的原始数据分布散点图呈灰色,没有用特定的颜色(红色或橙色)区分,也就是说数据没有标签。聚类后将数据分为不同的簇,用红橙黑三色标记。在一个簇中的数据就认为是同一类,也就是说这些数据具有相似性
3.算法步骤
1.随机选取k个初始中心点;
2.针对数据集中的每个样本点,计算样本点与k个中心点的距离,将样本点划分到离它最近的中心点所对应的类别中;
3.类别划分完成后,重新确定类别的中心点,将类别中所有样本各特征的均值作为新的中心点对应特征的取值,即该类中所有样本的质心;
4.重复上面的2 3步骤,直到达到某个终止条件(迭代次数、每一个簇中的点不再变等)
4.例子
5.K的选择
1.手肘法
手肘法的核心指标是SSE(sum of the squared errors,误差平方和),其中,Ci是第i个簇,p是Ci中的样本点,mi是Ci的质心(Ci中所有样本的均值),SSE是所有样本的聚类误差,代表了聚类效果的好坏。
肘法的核心思想是:随着聚类数k的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高,那么误差平方和SSE自然会逐渐变小。并且,当k小于真实聚类数时,由于k的增大会大幅增加每个簇的聚合程度,故SSE的下降幅度会很大,而当k到达真实聚类数时,再增加k所得到的聚合程度回报会迅速变小,所以SSE的下降幅度会骤减,然后随着k值的继续增大而趋于平缓,也就是说SSE和k的关系图是一个手肘的形状,而这个肘部对应的k值就是数据的真实聚类数。当然,这也是该方法被称为手肘法的原因。
计算k从1到n的的SSE
SSE会逐渐减小,直到k=n时SSE=0,每个点都是质心
在SSE减小过程,会出现拐点,这个拐点就是肘,下降率突然变缓时,就是最佳K值。
2.轮廓系数法
该方法的核心指标是轮廓系数(Silhouette Coefficient),某个样本点Xi的轮廓系数定义如下:其中,a是Xi与同簇的其他样本的平均距离,称为凝聚度,b是Xi与最近簇中所有样本的平均距离,
称为分离度。而最近簇的定义是
其中p是某个簇Ck中的样本。事实上,简单点讲,就是用Xi到某个簇所有样本平均距离作为衡量该点到该簇的距离后,选择离Xi最近的一个簇作为最近簇。
求出所有样本的轮廓系数后再求平均值就得到了平均轮廓系数。平均轮廓系数的取值范围为[-1,1],且簇内样本的距离越近,簇间样本距离越远,平均轮廓系数越大,聚类效果越好。那么,很自然地,平均轮廓系数最大的k便是最佳聚类数。
6.代码
import numpy as np
import matplotlib.pyplot as plt
# 两点欧氏距离
def distance(e1, e2):
return np.sqrt((e1[0] - e2[0]) ** 2 + (e1[1] - e2[1]) ** 2)
# 集合中心
def means(arr):
return np.array([np.mean([e[0] for e in arr]), np.mean([e[1] for e in arr])]) # mean用于求取均值 arr存放某一个簇中的点
if __name__ == "__main__":
## 生成二维随机坐标(如果有数据集就更好)
arr = np.random.randint(0, 100, size=(100, 2))
## 初始化聚类中心和聚类容器
m = 5 # 聚类个数
k_arr = np.random.randint(0, 100, size=(5, 2)) # 随机初始5个中心
cla_temp = [[], [], [], [], []] # 存放每个簇中的点
## 迭代聚类
n = 20 # 迭代次数
for i in range(n): # 迭代n次
for e in arr: # 把集合里每一个元素聚到最近的类
ki = 0 # 假定距离第一个中心最近
min_d = distance(e, k_arr[ki])
for j in range(1, k_arr.__len__()):
if distance(e, k_arr[j]) < min_d: # 找到更近的聚类中心
min_d = distance(e, k_arr[j])
ki = j
cla_temp[ki].append(e)
# 迭代更新聚类中心
for k in range(k_arr.__len__()):
if n - 1 == i:
break
k_arr[k] = means(cla_temp[k])
cla_temp[k] = []
## 可视化展示
col = ['HotPink', 'Aqua', 'Chartreuse', 'yellow', 'LightSalmon'] # 仅提供了5种颜色
for i in range(m):
plt.scatter(k_arr[i][0], k_arr[i][1], linewidth=10, color=col[i]) # 画中心的散点图
plt.scatter([e[0] for e in cla_temp[i]], [e[1] for e in cla_temp[i]], color=col[i]) # 画簇中的点
plt.show()
7.优缺点
1.优点
1、原理比较简单,实现也是很容易,收敛速度快。
2、当结果簇是密集的,而簇与簇之间区别明显时, 它的效果较好。
3、主要需要调参的参数仅仅是簇数k。
4、算法的可解释度比较强。
2.缺点
1、K值需要预先给定,很多情况下K值的估计是非常困难的。
2、K-Means算法对初始选取的质心点是敏感的,不同的随机种子点得到的聚类结果完全不同 ,对结果影响很大。
3、对噪音和异常点比较的敏感。用来检测异常值。
4、采用迭代方法,可能只能得到局部的最优解,而无法得到全局的最优解。
5、对于不是凸的数据集比较难收敛。
6、如果各类隐含类别的数据不均衡,比如隐含类别的数据严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。