K-means 算法原理
Posted ITAK
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K-means 算法原理相关的知识,希望对你有一定的参考价值。
1. 聚类
K-means算法是一种常用的聚类算法,所谓的聚类就是指给定 N N N个样本的数据集,需要构造 k k k 个簇(类),使得这 k k k 个簇之间的关系最小。
2. K-means算法基本步骤
- 随机初始化 k k k个点,作为聚类中心
- 在第 i i i次迭代中,对于每个样本点,选取距离最近的聚类中心,归为该类
- 遍历一遍之后,更新聚类中心,其中更新规则为:聚类中心取当前类的平均值
- 重复步骤2、3,直到满足迭代次数,或者聚类状态不发生改变
3. 算法优化
3.1 轮廓系数
轮廓系数(Silhouette Coefficient)结合了聚类的凝聚度(Cohesion)和分离度(Separation),用于评估聚类的效果。该值处于 [ − 1 , 1 ] [-1,1] [−1,1]之间,值越大,表示聚类效果越好。具体计算方法如下:
- 对于每个样本点 i i i,计算点 i i i与其同一个簇内的所有其他元素距离的平均值,记作 a ( i ) a(i) a(i),用于量化簇内的凝聚度。
- 选取i外的一个簇 b b b,计算 i i i与 b b b中所有点的平均距离,遍历所有其他簇,找到最近的这个平均距离,记作 b ( i ) b(i) b(i),即为 i i i的邻居类,用于量化簇之间分离度。
- 对于样本点 i i i ,轮廓系数 s ( i ) = b ( i ) – a ( i ) m a x a ( i ) , b ( i ) s(i) = \\frac b(i) – a(i) max\\a(i),b(i)\\ s(i)=maxa(i),b(i)b(i)–a(i)
- 计算所有i的轮廓系数,求出平均值即为当前聚类的整体轮廓系数,度量数据聚类的紧密程度
3.2 K值的选择
方法一:轮廓系数法
可以通过枚举的方法,令 k k k 从 2 2 2 到 10 10 10(随便的一个固定值,但不能太大),对于每一个 k k k 值进行 K-means 算法,然后取轮廓系数最大的那个 k k k 作为最终聚类的簇的数目。
方法二:误差平方和法
定义误差平方和公式如下:
S
S
E
=
∑
i
=
1
k
∑
d
∈
C
i
∣
d
−
c
e
n
t
e
r
i
∣
2
SSE = \\sum_i=1^k\\sum_d\\in C_i |d-center_i|^2
SSE=∑i=1k∑d∈Ci∣d−centeri∣2
其中
C
i
C_i
Ci 是第
i
i
i 个簇,其中
d
d
d 是 簇
C
i
C_i
Ci 的样本,
c
e
n
t
e
r
i
center_i
centeri 是第
i
i
i 个簇的聚类中心
随着聚类数
k
k
k 的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高,那么误差平方和SSE自然会逐渐变小。并且,当k小于真实聚类数时,由于
k
k
k的增大会大幅增加每个簇的聚合程度,故SSE的下降幅度会很大,而当
k
k
k到达真实聚类数时,再增加
k
k
k所得到的聚合程度回报会迅速变小,所以SSE的下降幅度会骤减,然后随着
k
k
k值的继续增大而趋于平缓,也就是说SSE和
k
k
k的关系图是一个手肘的形状,而这个肘部对应的
k
k
k值就是数据的真实聚类数。–转自手肘法理论
3.3 初始点的选择
- 从给定的样本集合中随机选择一个样本作为第一个聚类中心
- 然后对于样本集合中的每一个样本点 x x x,计算离该样本 x x x与最近的聚类中心的距离 D ( x ) D(x) D(x)
- 概率的选择D(x)较大的点作为新的样本中心
- 重复步骤2和3,直到聚类中心达到 k k k 个为止
4. 代码
以鸢尾花数据为例,下面是k-means的代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
# 加载数据
def loadData():
iris = load_iris()
#print(iris)
X = np.array(iris.data[:])
#print(X.shape)
# 以鸢尾花后两个特征作为聚类特征
# plt.scatter(X[:, 2], X[:, 3], c="red", marker='o', label='two features')
# plt.xlabel('petal length')
# plt.ylabel('petal width')
# plt.legend(loc=2)
# plt.show()
return X[:,2:]
# 第一次随机获取k个中心点
def getCenterPoints(X, k):
m, n = X.shape
centerPoints = np.zeros((k, n))
for i in range(k):
id = int(np.random.uniform(0, m))
centerPoints[i, :] = X[id, :]
return centerPoints
#得到两个数据点之间的欧氏距离
def getDis(x, y):
return np.sqrt(np.sum((x - y) **2 ))
# k-means算法
def KMeans(X, k):
m = X.shape[0]
cluster = np.mat(np.zeros((m, 2)))
#1. 随机获取中心点
centerPoints = getCenterPoints(X, k)
for times in range(100):
for i in range(m):
minDist, minID = 1000000.0, -1
for j in range(k):
dis = getDis(X[i, :], centerPoints[j, :])
if dis < minDist:
minDist, minID = dis, j
#2.对于每个样本点,选取最近的中心点,归为该类
cluster[i, :] = minID, minDist**2
#3. 更新中心点
for i in range(k):
pointsInCluster = X[np.nonzero(cluster[:, 0].A == i)[0]] # 获取簇类所有的点
centerPoints[i, :] = np.mean(pointsInCluster, axis=0) # 对矩阵的行求均值
return centerPoints, cluster
#对聚类好的数据进行画图
def showCluster(X, k, cluster, centerPoints):
m = X.shape[0]
mark = ['or', 'ob', 'og']
for i in range(m):
markIndex = int(cluster[i, 0])
plt.plot(X[i, 0], X[i, 1], mark[markIndex])
mark = ['Dr', 'Db', 'Dg']
# 绘制中心点
for i in range(k):
plt.plot(centerPoints[i, 0], centerPoints[i, 1], mark[i])
plt.show()
def main():
X, k = loadData(), 3
centerPoints, cluster = KMeans(X, k)
showCluster(X, k, cluster, centerPoints)
if __name__ == '__main__':
main()
分类效果:
以上是关于K-means 算法原理的主要内容,如果未能解决你的问题,请参考以下文章