K-means聚类法
1.简介
用层次聚类法聚类时,随着聚类样本对象的增多,计算量会迅速增加,而且聚类结果-谱系图会十分复杂,不便于分析,特别是样品的个数很大(如n>=100)的时,层次聚类法的计算量会非常大,将占据大量的计算机内存空间和较多的计算时间.
为了改进上述缺点, 一个自然的想法是先粗略地分一下类,然后按某种最优原则进行修正,直到将类分得比较合理为止. 基于这种思想就产生了动态聚类法,也称逐步聚类法.
动态距离法适用大型数据.动态聚类法有许多种方法,这里介绍一种比较流行的动态聚类法-K-means法.它是一种快速聚类法,该方法得到的结果简单易懂,对计算机的性能要求不高,因而应用广泛,该方法由Macqueen于1967年提出。
算法的思想是假定样本集中的全体样本可分为C类,并选定C个初始聚类中心,然后根据最小距离原则将每个样品分配到某一类中,之后不断迭代计算各类的聚类中心,并依据新的聚类中心调整聚类状况,直到迭代收敛或聚类中心不再改变.
K-means algorithms最后将总样本集G划分为C个子集:G1,G2,···Gc,它们满足下面条件:
1. G 1 ∪ G 2 ∪ ⋅ ⋅ ⋅ ∪ G C = G ; 1. G_1\cup{G_2}\cup···\cup{G_C}=G; 1.G1∪G2∪⋅⋅⋅∪GC=G;
2. G i ∩ G j = ∅ ( 1 ≤ i < j ≤ C ) ; 2.G_i\cap{G_j}=\emptyset(1\leq{i}<j\leq{C}); 2.Gi∩Gj=∅(1≤i<j≤C);
3.
G
i
≠
∅
,
G
i
≠
G
(
1
≤
i
≤
C
)
3.G_i\neq{\emptyset},G_i\neq{G}(1\leq{i}\leq{C})
3.Gi=∅,Gi=G(1≤i≤C).
设
m
i
(
i
=
1
,
⋅
⋅
⋅
,
C
)
为
C
个
聚
类
中
心
,
记
设m_i(i=1,···,C)为C个聚类中心,记
设mi(i=1,⋅⋅⋅,C)为C个聚类中心,记
J e = ∑ i = 1 C ∑ ω ∈ G i ∣ ∣ ω − m i ∣ ∣ 2 J_e=\sum_{i=1}^{C}\sum_{\omega\in{G_i}}{||\omega-m_i||}^2 Je=i=1∑Cω∈Gi∑∣∣ω−mi∣∣2
使
J
e
最
小
的
聚
类
是
误
差
平
方
和
准
则
下
的
最
优
结
果
.
使J_e最小的聚类是误差平方和准则下的最优结果.
使Je最小的聚类是误差平方和准则下的最优结果.
K均值聚类算法描述如下:
1. 初 始 化 . 设 总 样 本 集 G = ω j , j = 1 , 2 ⋅ ⋅ ⋅ , n 是 n 个 样 品 组 成 的 集 合 , 聚 类 数 为 C ( 2 ≤ C ≤ n ) , 1. 初始化.设总样本集G={\omega_j,j=1,2···,n}是n个样品组成的集合,聚类数为C(2\leq{C}\leq{n}), 1.初始化.设总样本集G=ωj,j=1,2⋅⋅⋅,n是n个样品组成的集合,聚类数为C(2≤C≤n),
将 样 品 集 G 任 意 划 分 为 C 类 , 记 为 G 1 , G 2 , ⋅ ⋅ ⋅ , G c , 计 算 对 应 的 C 个 初 始 聚 类 中 心 , 记 为 m 1 , m 2 , ⋅ ⋅ ⋅ , m c , 并 计 算 J e . 将样品集G任意划分为C类,记为G_1,G_2,···,G_c,计算对应的C个初始聚类中心,记为m_1,m_2,···,m_c,并计算J_e. 将样品集G任意划分为C类,记为G1,G2,⋅⋅⋅,Gc,计算对应的C个初始聚类中心,记为m1,m2,⋅⋅⋅,mc,并计算Je.
2. G i = ∅ ( i = 1 , 2 , ⋅ ⋅ ⋅ , C ) , 按 最 小 距 离 原 则 将 样 品 ω j ( j = 1 , 2 , ⋅ ⋅ ⋅ , n ) 进 行 聚 类 , 即 若 2.G_i=\emptyset(i=1,2,···,C),按最小距离原则将样品\omega_j(j=1,2,···,n)进行聚类,即若 2.Gi=∅(i=1,2,⋅⋅⋅,C),按最小距离原则将样品ωj(j=1,2,⋅⋅⋅,n)进行聚类,即若
d ( ω j , G k ) = m i n 1 ≤ i ≤ C d ( ω j , m i ) , 则 ω j ∈ G k , G k = G k ∪ ω j , j = 1 , 2 , ⋅ ⋅ ⋅ , n . d(\omega_j,G_k)=min_{1\leq{i}\leq{C}}{d(\omega_j,m_i)},则\omega_j\in{G_k},G_k=G_k\cup{{\omega_j}},j=1,2,···,n. d(ωj,Gk)=min1≤i≤Cd(ωj,mi),则ωj∈Gk,Gk=Gk∪ωj,j=1,2,⋅⋅⋅,n.
重新计算聚类中心
m i = 1 n i ∑ ω j ∈ G i ω j , i = 1 , 2 , ⋅ ⋅ ⋅ , C m_i=\frac{1}{n_i}\sum_{\omega_j\in{G_i}}^{\omega_j},i=1,2,···,C mi=ni1ωj∈Gi∑ωj,i=1,2,⋅⋅⋅,C
上
式
中
,
n
i
为
当
前
G
i
类
中
的
样
本
数
目
,
并
重
新
计
算
J
e
.
上式中,n_i为当前G_i类中的样本数目,并重新计算J_e.
上式中,ni为当前Gi类中的样本数目,并重新计算Je.
3.
若
连
续
两
次
迭
代
的
J
e
不
变
,
则
算
法
终
止
,
否
则
算
法
转
(
2
)
.
3.若连续两次迭代的J_e不变,则算法终止,否则算法转(2).
3.若连续两次迭代的Je不变,则算法终止,否则算法转(2).
值得注意的是,实际计算(编程)时,可以不计算J_e,只要聚类中心不发生变化,算法即可终止
2.栗子:
已知聚类的指标变量为x1,x2,四个样本点的数据分别为
ω 1 = ( 1 , 3 ) , ω 2 = ( 1.5 , 3.2 ) , ω 3 = ( 1.3 , 2.8 ) , ω 4 = ( 3 , 1 ) . \omega_1=(1,3),\omega_2=(1.5,3.2),\omega_3=(1.3,2.8),\omega_4=(3,1). ω1=(1,3),ω2=(1.5,3.2),ω3=(1.3,2.8),ω4=(3,1).
要求用k-means algorithm把样本点分成两类.
解:
首先先目测随机分为两类,选定两个聚类中心m1,m2
G 1 类 : ω 1 值 , 即 m 1 = ( 1 , 3 ) ; G1类:\omega_1值,即m1=(1,3); G1类:ω1值,即m1=(1,3);
G 2 类 : m 2 = ( 1.5 + 1.3 + 3 3 , 3.2 + 2.8 + 1 3 ) = ( 1.9333 , 2.3333 ) G2类:m2=(\frac{1.5+1.3+3}{3},\frac{3.2+2.8+1}{3})=(1.9333,2.3333) G2类:m2=(31.5+1.3+3,33.2+2.8+1)=(1.9333,2.3333)
计算每个样本点到G1,G2聚类中心的距离
d 11 = ∣ ∣ ω 1 − m 1 ∣ ∣ = ( 1 − 1 ) 2 + ( 3 − 3 ) 2 = 0 d11=||\omega_1-m1||=\sqrt{{(1-1)}^2+{(3-3)}^2}=0 d11=∣∣ω1−m1∣∣=(1−1)2+(3−3)2=0
d 12 = ∣ ∣ ω 1 − m 2 ∣ ∣ = 1.1470 d12=||\omega_1-m2||=1.1470 d12=∣∣ω1−m2∣∣=1.1470
d 21 = ∣ ∣ ω 2 − m 1 ∣ ∣ = 0.5385 d21=||\omega_2-m1||=0.5385 d21=∣∣ω2−m1∣∣=0.5385
d 22 = ∣ ∣ ω 2 − m 2 ∣ ∣ = 0.9690 d22=||\omega_2-m2||=0.9690 d22=∣∣ω2−m2∣∣=0.9690
d 31 = ∣ ∣ ω 3 − m 1 ∣ ∣ = 0.3606 d31=||\omega_3-m1||=0.3606 d31=∣∣ω3−m1∣∣=0.3606
d 32 = ∣ ∣ ω 3 − m 2 ∣ ∣ = 0.7867 d32=||\omega_3-m2||=0.7867 d32=∣∣ω3−m2∣∣=0.7867
d
41
=
∣
∣
ω
4
−
m
1
∣
∣
=
2.8284
d41=||\omega_4-m1||=2.8284
d41=∣∣ω4−m1∣∣=2.8284
d
42
=
∣
∣
ω
4
−
m
2
∣
∣
=
1.7075
d42=||\omega_4-m2||=1.7075
d42=∣∣ω4−m2∣∣=1.7075
经 过 上 面 的 计 算 , 我 们 得 到 新 的 划 分 为 G 1 = ω 1 , ω 2 , ω 3 , G 2 = ω 4 经过上面的计算,我们得到新的划分为G1={\omega_1,\omega_2,\omega_3},G2={\omega_4} 经过上面的计算,我们得到新的划分为G1=ω1,ω2,ω3,G2=ω4
新的聚类中心为:
G 1 类 : m 1 = ( 1 + 1.5 + 1.3 / 3 3 , 3 + 3.2 + 2.8 3 ) = ( 1.2667 , 3.0 ) ; G1类:m1=(\frac{1+1.5+1.3/3}{3},\frac{3+3.2+2.8}{3})=(1.2667,3.0); G1类:m1=(31+1.5+1.3/3,33+3.2+2.8)=(1.2667,3.0);
G 2 类 : ω 4 值 , 即 m 2 = ( 3 , 1 ) G2类:\omega_4值,即m2=(3,1) G2类:ω4值,即m2=(3,1)
再次重新计算每个样本点到G1,G2聚类中心的距离
d11,d12,d21,d22,d31,d32,d41,d42
得到的新的划分和我们的上一个最新的假设一样,所以到此聚类结束
当然我们也可以设计程序(利用sklearn库的cluster模块的KMeans函数)进行计算
import numpy as np
from sklearn.cluster import KMeans
a=np.array([[1,3],[1.5,3.2],[1.3,2.8],[3,1]])
md=KMeans(n_clusters=2) #构建模型 选择参数-构造两个聚类中心的模型
md.fit(a) #求解模型
labels=1+md.labels_ #提取聚类标签 #因为聚类标签一般是从0开始的
centers=md.cluster_centers_ #提取聚类中心,每一行是一个聚类中心
print(labels,'\n',centers)
3.k-means算法思想揭秘-期望最大化
期望最大化(expectation-maximization,E-M)是一种非常强大的算法,在数据科学中应用广泛.
而K-means就是其一个较为简单且易于理解的应用.
简单的说,期望最大化有以下几个步骤:
-
猜测一些簇中心点(后面会详细介绍如何猜测)
-
重复直至收敛
-
期望步骤(E-step):将点分配至离其最近的簇中心点
-
最大化步骤(M-step):将簇中心点设置为所有点坐标的平均值
-
E-step不断更新每个点是属于哪一个簇的期望值
M-step计算关于簇中心点的拟合函数值最大化对应坐标(argmax)-一般应用是求每个簇中所有数据点坐标的平均值去得到簇中心点坐标.
简单来说:在典型环境下,每一次重复的E-step和M-step都将会得到更好的聚类效果.
可视化
所以,根据这个思想,我们可以简单地实现一下其源码
from sklearn.metrics import pairwise_distances_argmin
import numpy as np
def find_clusters(X,n_clusters,rseed=2):
#随机选择簇中心点
rng=np.random.RandomState(rseed)
i=rng.permutation(X.shape[0])[:n_clusters]
centers=X[i]
while True:
#基于最近的中心指定标签
labels=pairwise_distances_argmin(X,centers)
#根据点的平均值找到新的中心
new_centers=np.array([X[labels==i].mean(0) for i in range(n_clusters)])
#确认收敛
if np.all(centers==new_centers):
break
centers=new_centers
return centers,labels
a=np.array([[1,3],[1.5,3.2],[1.3,2.8],[3,1]])
centers,labels=find_clusters(a,2)
print(labels+1,'\n·······\n',centers)
4.K-means最佳簇数K值的确定
对于K均值聚类来说,如何确定簇数K值是一个至关重要的问题,为了解决这个问题,通过会选用探索法,即给定不同的k值,对比某些评估指标的变动情况,进而选择一个比较合理的k值. 本文将重点介绍两种非常实用的评估方法,即簇内离差平方和拐点法与轮廓系数法.
1.簇内离差平方和拐点法
顾名思义,簇内离差平方和拐点法的思想很简单,就是在不同的k值下计算簇内离差平方和,然后通过可视化的方法找到“拐点”所对应的k值.重点关注的是斜率的变化,当斜率由大突然变小时,并且之后的斜率变化缓慢,则认为突然变换的点就是寻找的目标点,因为继续随着簇数K的增加,聚类效果不再有大的变化.所以,整个核心就是找到”k的剧变点”.有点类似于常见的梯度下降,同样需要计算cost画图看k,不过这个不需要梯度,因为不需要方向.
这里我们随机生成三组二维正态分布数据,首先基于该数据绘制散点图,模拟的数据呈现三个簇.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['axes.unicode_minus']=False
from sklearn.cluster import KMeans
mean=np.array([[-2,-2],[2,2],[6,6]]) #设定三个二维正态分布的均值
cov=np.array([[[0.3,0],[0,0.3]],[[0.4,0],[0,0.4]],[[0.5,0],[0,0.5]]]) #设定三个二维正态分布的协方差矩阵
X0=[];Y0=[];
for i in range(3):
x,y=np.random.multivariate_normal(mean[i],cov[i],1000).T
X0=np.hstack([X0,x]); Y0=np.hstack([Y0,y]) #水平堆叠,方便画图
plt.rc('font',size=16);
plt.rc('font',family='SimHei')
plt.rc('axies');plt.subplot(121)
plt.scatter(X0,Y0,marker='.') #画模拟数据散点图
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['axes.unicode_minus']=False
from sklearn.cluster import KMeans
mean=np.array([[-2,-2],[2,2],[6,6]])
cov=np.array([[[0.3,0],[0,0.3]],[[0.4,0],[0,0.4]],[[0.5,0],[0,0.5]]])
X0=[];Y0=[];
for i in range(3):
x,y=np.random.multivariate_normal(mean[i],cov[i],1000).T
X0=np.hstack([X0,x]); Y0=np.hstack([Y0,y])
plt.rc('font',size=16);
plt.rc('font',family='SimHei')
X=np.vstack([X0,Y0]).T
TSSE=[];k=10
for k in range(1,k+1):
SSE=[]
md=KMeans(k)
md.fit(X)
labels=md.labels_
centers=md.cluster_centers_
for label in set(labels):
SSE.append(np.sum((X[labels==label,:]-centers[label,])**2)) #labels=label来筛选数据集X,true才会进行计算
TSSE.append(np.sum(SSE))
plt.subplot(122);plt.style.use('ggplot')
plt.plot(range(1,k+1),TSSE,'b*-')
plt.xlabel('簇的个数');plt.ylabel('簇内离差平方和之和')
plt.show()
经过上面的可视化过程后,我们可以看到,在簇为3个的那里k发生了剧烈的变化,也就是拐点,而后变动都较小,所以合理的k值应该为3.(不浪费算力资源)
2.轮廓系数法
该方法综合考虑了簇的密集性和分散性两个信息,如果数据集被切割为理想的k个簇,那么对应的簇内样本会很密集,而簇间样本会很分散.
如下图所示,假设数据集被拆分为三个簇G1,G2,G3,样本点i对应的 ai 值为所有G1中其他样本点与样本点i的距离平均值;样本点i对应的 bi 值分两步计算,首先计算该点分别到G2和G3中样本点的平均距离,然后将两个平均值中的最小值作为bi的度量.
定义样本点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)} Si=max(ai,bi)bi−ai
k个簇的总轮廓系数定义为所有样本点轮廓系数的平均值.
当总轮廓系数小于0时,说明聚类效果不佳;
当总轮廓系数接近于1时,说明簇内样本的平均距离非常小,而簇间的最近距离非常大,进而表示聚类效果非常理想.
思想虽然较为简单,但实际当样本数据量较大时,运行时间会比较长。
对于轮廓系数的计算,可以直接调用sklearn.metrics中的函数silhouette_score
但是其需要聚类簇数必须大于等于2.
我们同样利用上面的模拟数据来看看其于簇内离差平方和拐点法的效果是否是一样的:
import numpy as np; import matplotlib.pyplot as plt
from sklearn.cluster import KMeans; from sklearn import metrics
X=np.load("Pzdata11_1.npy")
S=[]; K=10
for k in range(2,K+1):
md = KMeans(k); md.fit(X)
labels = md.labels_;
S.append(metrics.silhouette_score(X,labels,metric='euclidean')) #计算轮廓系数
plt.rc('font',size=16); plt.rc('font',family='SimHei')
plt.plot(range(2,K+1), S, 'b*-')
plt.xlabel('簇的个数'); plt.ylabel('轮廓系数'); plt.show()
可以看到在簇数=3的时候,轮廓系数最接近于1,这与用上一种方法得到的结果是一样的.
所以,在实际情况中,我们可以更为保险地同时使用两种方法来判定结果,也可以只使用一种方法.
5.k均值聚类的应用.
在做k均值聚类时需要注意两点,一个是聚类前必须指定具体的簇数k值,如果k值是已知的,可以直接调用cluster子模块中的KMeans函数,对数据集进行分割; 如果k值是未知的,可以根据行业经验或前面介绍的两种方法确定合理的k值.
另一个是对原始数据做必要的标准化处理.由于k均值的思想是基于点之间的距离实现“物以类聚”的,所以**如果原始数据集存在量纲上的差异,就必须对其进行标准化的预处理.**数据集的标准化预处理可以借助sklearn子模块preprocessing中的scale函数或minmax_scale函数.scale函数的标准化公式为:
x ∗ = x − u σ x^*=\frac{x-u}{\sigma} x∗=σx−u
minmax_scale函数的标准化公式为
x ∗ = x − x m i n x m a x − x m i n x^*=\frac{x-x_{min}}{x_{max}-x_{min}} x∗=xmax−xminx−xmin
其 中 , u , σ , x m i n , x m a x 分 别 为 x 取 值 的 均 值 、 标 准 值 、 最 小 值 和 最 大 值 . 其中,u,\sigma,x_{min},x_{max}分别为x取值的均值、标准值、最小值和最大值. 其中,u,σ,xmin,xmax分别为x取值的均值、标准值、最小值和最大值.