1. K-means概论
K-Means算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。
假设簇划分为
(
C
1
,
C
2
,
.
.
.
C
k
)
(C_1,C_2,...C_k)
(C1,C2,...Ck),则我们的目标是最小化平方误差E:
E
=
∑
i
=
1
k
∑
x
∈
C
i
∥
x
−
μ
i
∥
2
2
E=\sum_{i=1}^{k} \sum_{x \in C_{i}}\left\|x-\mu_{i}\right\|_{2}^{2}
E=i=1∑kx∈Ci∑∥x−μi∥22
其中
μ
i
μ_i
μi是簇
C
i
C_i
Ci的均值向量,有时也称为质心,表达式为:
μ
i
=
1
∣
C
i
∣
∑
x
∈
C
i
x
\mu_{i}=\frac{1}{\left|C_{i}\right|} \sum_{x \in C_{i}} x
μi=∣Ci∣1x∈Ci∑x
如果我们想直接求上式的最小值并不容易,这是一个NP难的问题,因此只能采用启发式的迭代方法。
2. K-means算法流程
设输入样本集为 D = { x 1 , x 2 , . . . x m } D=\{x_1,x_2,...x_m\} D={x1,x2,...xm},聚类的簇数为 k,最大迭代次数N, 输出是簇划分 C = { C 1 , C 2 , . . . C k } C=\{C_1,C_2,...C_k\} C={C1,C2,...Ck}
(1)从数据集D中随机选择k个样本作为初始的k个质心向量: { μ 1 , μ 2 , . . . , μ k } \{μ_1,μ_2,...,μ_k\} {μ1,μ2,...,μk}
(2)对于n=1,2,…,N
- 将簇划分C初始化为 C t = ∅ t = 1 , 2... k C_t=∅ \,\,\,\,t=1,2...k Ct=∅t=1,2...k
- 对于i=1,2…m,计算样本 x i x_i xi和各个质心向量 μ j μ_j μj(j=1,2,…k)的距离: d i j = ∣ ∣ x i − μ j ∣ ∣ 2 2 d_{ij}=||x_i−μ_j||_2^2 dij=∣∣xi−μj∣∣22,将 x i x_i xi标记为最小的 d i j d_{ij} dij所对应的类别 λ i λ_i λi。此时更新 C λ i = C λ i ∪ x i C_{λi}=C_{λi}∪{x_i} Cλi=Cλi∪xi
- 对于j=1,2,…,k,对 C j C_j Cj中所有的样本点重新计算新的质心
- 如果所有的k个质心向量都没有发生变化,则转到步骤3
(3)输出簇划分 C = { C 1 , C 2 , . . . C k } C=\{C_1,C_2,...C_k\} C={C1,C2,...Ck}
2.1 Kmeans实现
import numpy as np
def kmeans(k, max_iters, data, tol):
centers = data[np.random.choice(data.shape[0], k, replace=False)]
for _ in range(max_iters):
labels = np.argmin(np.linalg.norm(data[:, np.newaxis]-centers, axis=2), axis=1)
new_centers = np.array([data[labels == i].mean(axis=0) for i in range(k)])
if np.all(np.linalg.norm(new_centers-centers, axis=1) < tol):
break
centers = new_centers
return centers, labels
if __name__ == "__main__":
test_data = np.random.randn(300, 2)
ctr, lbs = kmeans(3, 200, test_data, 0.001)
print(ctr)
注意事项
要注意的k值的选择,一般来说,我们会根据对数据的先验经验选择一个合适的k值,如果没有什么先验知识,则可以通过交叉验证选择一个合适的k值。
选择k个初始化的质心时,由于我们是启发式方法,k个初始化的质心的位置选择对最后的聚类结果和运行时间都有很大的影响,因此需要选择合适的k个质心,最好这些质心不能太近。
3. k-means++算法
K-Means++算法就是对K-Means随机初始化质心的方法的优化
K-Means++的对于初始化质心的优化策略如下:
(1)从输入的数据点集合中随机选择一个点作为第一个聚类中心
μ
1
μ_1
μ1
(2)对于数据集中的每一个点 x i x_i xi,计算它与已有的聚类中心中最短的距离(即与最近的一个聚类中心的距离)用 D ( X ) D(X) D(X)表示;接着计算每个样本点被选作下一个聚类中心的概率 D ( x ) 2 ∑ x ∈ X D ( x ) 2 \frac{D(x)^{2}}{\sum_{x \in X} D(x)^{2}} ∑x∈XD(x)2D(x)2,然后按照轮盘法选择出下一个聚类中心;轮盘法就是首先随机产生出一个0~1之间的随机数,判断它属于哪个区间,那么该区间对应的序号就是被选择出来的第二个聚类中心了。例如对下图而言1号点的区间为[0,0.2),2号点的区间为[0.2, 0.525)。
(3)重复第二步直到选出K个聚类中心
之后使用经典K-means算的过程训练
从上表可以直观的看到第二个初始聚类中心是1号,2号,3号,4号中的一个的概率为0.9。而这4个点正好是离第一个初始聚类中心6号点较远的四个点。这也验证了K-means的改进思想:即离当前已有聚类中心较远的点有更大的概率被选为下一个聚类中心。可以看到,该例的K值取2是比较合适的。当K值大于2时,每个样本会有多个距离,需要取最小的那个距离作为D(x)。
4. K-means和KNN
K-Means是无监督学习的聚类算法,没有样本输出;
KNN是监督学习的分类算法,有对应的类别输出。
KNN基本不需要训练,对测试集里面的点,只需要找到在训练集中最近的k个点,用这最近的k个点的类别来决定测试点的类别。而K-Means则有明显的训练过程,找到k个类别的最佳质心,从而决定样本的簇类别。
KNN算法过程:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。
5.总结
K-Means的主要优点有:
1)原理比较简单,实现也是很容易,收敛速度快。
2)聚类效果较优。
3)算法的可解释度比较强。
4)主要需要调参的参数仅仅是簇数k。
K-Means的主要缺点有:
1)K值的选取不好把握
2)对于不是凸的数据集比较难收敛
3)如果各隐含类别的数据不平衡,比如各隐含类别的数据量严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。
4) 采用迭代方法,得到的结果只是局部最优。
5) 对噪音和异常点比较的敏感。
参考
[1]. K-means聚类算法的三种改进(K-means++,ISODATA,Kernel K-means)介绍与对比
[2]. K-means聚类算法原理