k均值聚类

k均值聚类

k均值聚类是基于样本集合划分的聚类算法。k均值聚类将样本划分为kk个子集,构成kk个类,将nn个样本分到kk个类中每个样本到其所属类的中心的距离最小。每个样本只能属于一个类,所以kk均值聚类是硬聚类。下面分别介绍kk均值聚类的模型、策略、算法,讨论算法的特性及相关问题。

实际上,均值聚类是使用最大期望算法(Expectation-Maximization algorithm)求解的高斯混合模型(Gaussian Mixture Model, GMM)在正态分布的协方差为单位矩阵,且隐变量的后验分布为一组狄拉克δ函数时所得到的特例。

模型

给定mm个样本的集合X={x1,x2,...,xn}X=\{x_1, x_2, ... , x_n\},每个样本由一个特征向量表示,特征向量的维数是mm。k均值聚类的目标是将nn个样本分到kk个不同的类或着簇中,这里假设k<nk<nkk个类G1,G2,...,GkG_1, G_2, ... ,G_k形成对样本集合XX的划分,其中

GiGj=, i=1k=XG_i \bigcap G_j = \varnothing ,\ \bigcup_{i=1}^{k}=X

。用CC表示划分,一个划分对应着一个聚类结果。

划分CC是一个多对一的函数。事实上,如果把每个样本用一个整数i{1,2,...,n}i \in \{1, 2, ... , n\}表示,每个类也用一个整数l{1,2,...,k}l \in \{1, 2, ... , k\}表示,那么划分或者聚类可以用函数l=C(i)l = C(i)表示,其中i{1,2,...,n}i \in \{1, 2, ... , n\}l{1,2,...,k}l \in \{1, 2, ... , k\}。所以k均值聚类的模型是一个从样本到类的函数

策略

k均值聚类归结为样本集合XX的划分,或者从样本到类的函数的选择问题。k均值聚类的策略是通过损失函数的最小化选取最优的划分或函数CC^{*}

首先,采用欧式距离平方作为样本之间的距离d(xi,xj)d(x_i, x_j)

d(xi,xj)=k=1m(xkixkj)2=xixj2\begin{aligned} d(x_i,x_j)&=\sum_{k=1}^{m}(x_{ki}-x_{kj})^2\\ &= ||x_i-x_j||^2 \end{aligned}

然后,定义样本与其所属类的中心之间的距离的总和为损失函数,即

W(C)=l=1kC(i)=lxixˉl2W(C)=\sum_{l=1}^{k}\sum_{C(i)=l}||x_i-\bar{x}_l||^2

式中,

xˉl=(xˉ1l,xˉ2l,...,xˉml)T\bar{x}_l=(\bar{x}_{1l},\bar{x}_{2l},...,\bar{x}_{ml})^T

是第ll个类的均值或者聚类中心,属于第ll类的样本个数为nl=i=1nI(C(i)=l)n_l=\sum_{i=1}^nI\left(C(i)=l\right)I(C(i)=l)I(C(i) = l)是指示函数,取值为1或0。函数W(C)W(C)也称为能量,表示相同类中的样本相似的程度。

k均值聚类就是求解最优化问题:

C=arg minCW(C)=arg minCl=1kC(i)=lxixˉl2\begin{aligned} C^*&=\text{arg }\mathop{\text{min}}_C W(C)\\ &=\text{arg }\mathop{\text{min}}_C \sum_{l=1}^{k}\sum_{C(i)=l}||x_i-\bar{x}_l||^2 \end{aligned}

相似的样本被聚到同类时,损失函数最小,这个目标函数的最优化能达到聚类的目的。但是,这是一个组合优化问题,nn个样本分到kk类,所有可能分法的数目时:

S(n,k)=1k!l=1k(1)kl(kl)kn\begin{aligned} S(n,k)=\frac{1}{k!}\sum_{l=1}^k(-1)^{k-l} \begin{pmatrix} k\\ l \end{pmatrix} k^n \end{aligned}

这个数字是指数级的。事实上,k均值聚类的最优解求解问题是NP难问题。现实中采用迭代的方法求解。

算法

k均值聚类的算法是一个迭代的过程,每次迭代包括两个步骤:

  • 首先选择kk个类的中心,将样本逐个指派到与其最近的中心的类中,得到一个聚类的结果;

  • 然后更新每个类的样本的均值,作为类的新的中心

重复以上步骤,知道收敛为止。具体过程如下。

首先,对于给定的中心值(m1,m2,...,mk)(m_1, m_2, ... , m_k),求一个划分CC,使得目标函数极小化:

minCl=1kC(i)=lximl2\mathop{\text{min}}_C \sum_{l=1}^{k}\sum_{C(i)=l}||x_i-m_l||^2

就是说在类中心确定的情况下,将每个样本分到一个类中,使样本和其所属类的中心之间的距离总和最小。求解结果,将每个样本指派到与其最近的中心mlm_l的类GlG_l中。

然后,对给定的划分CC,再求各个类的中心(m1,m2,...,mk)(m_1, m_2, ... , m_k),使得目标函数极小化:

minm1,...,mkl=1kC(i)=lximl2\mathop{\text{min}}_{m_1,...,m_k} \sum_{l=1}^{k}\sum_{C(i)=l}||x_i-m_l||^2

就是说在划分确定的情况下,使样本和其所属类的中心之间的距离总和最小。求解结果,对于每个包含nln_l个样本的类GlG_l,更新其均值mlm_l

ml=1nlG(i)=lxi,l=1,...,km_l=\frac{1}{n_l}\sum_{G(i)=l}x_i,\quad l=1,...,k

重复以上两个步骤,直到划分不再改变,得到聚类结果。现将k均值聚类算法叙述如下。

k均值聚类算法

输入:nn个样本的集合XX

输出:样本集合的聚类CC^*

(1)初始化。令t=0,随机选择k个样本点作为初始聚类中心

m(0)=(m1(0),...,ml(0),...,mk(0))m^{(0)}=\left(m_1^{(0)},...,m_l^{(0)},...,m_k^{(0)}\right)

(2)对样本进行聚类。对固定的类中心

m(t)=(m1(t),...,ml(t),...,mk(t))m^{(t)}=\left(m_1^{(t)},...,m_l^{(t)},...,m_k^{(t)}\right)

,其中ml(t)m_l^{(t)}为类GtG_t的中心,计算每个样本到类中心的距离,将每个样本指派到与其最近的中心的类中,构成聚类结果C(t)C^{(t)}

(3)计算新的类中心。对聚类结果C(t)C^{(t)},计算当前各个类中的样本的均值,作为新的类中心

m(t+1)=(m1(t+1),...,ml(t+1),...,mk(t+1))m^{(t+1)}=\left(m_1^{(t+1)},...,m_l^{(t+1)},...,m_k^{(t+1)}\right)

(4)如果迭代收敛或符合停止条件,输出

C=C(t)C^*=C^{(t)}

否则,令t=t+1t=t+1,返回步(2)。

k均值聚类算法的复杂度是O(mnk)O(mnk),其中mm是样本维数,nn是样本个数,kk是类别个数。在实际代码中,还要乘上迭代次数iteriter

例子:

给定含有5个样本的集合

X=[0015520002]\begin{aligned} X= \begin{bmatrix} 0 & 0 & 1 & 5 & 5\\ 2 & 0 & 0 & 0 & 2 \end{bmatrix} \end{aligned}

试用k均值聚类算法将样本聚到2个类中。

解:按照上述算法,

(1)选择两个样本点作为类的中心。假设选择

m1(0)=x1=(0,2)Tm2(0)=x2=(0,0)T\begin{aligned} &m_1^{(0)}=x_1=(0,2)^T\\ &m_2^{(0)}=x_2=(0,0)^T \end{aligned}

(2)以m1(0),m2(0)m_1^{(0)},m_2^{(0)}为类G1(0),G2(0)G_1^{(0)},G_2^{(0)}的中心,计算x3=(1,0)T,x4=(5,0)T,x5=(5,2)Tx_3=(1,0)^T,x_4=(5,0)^T,x_5=(5,2)^Tm1(0)=(0,2)T,m2(0)=(0,0)Tm_1^{(0)}=(0,2)^T,m_2^{(0)}=(0,0)^T的欧式距离平方。

x3=(1,0)Tx_3 = (1, 0)^Td(x3,m1(0))=5,d(x3,m2(0))=1d(x_3,m_1^{(0)})=5,d(x_3,m_2^{(0)})=1,将x3x_3分到类G2(0)G_2^{(0)}

x4=(5,0)Tx_4= (5, 0)^Td(x4,m1(0))=29,d(x4,m2(0))=25d(x_4,m_1^{(0)})=29,d(x_4,m_2^{(0)})=25,将x4x_4分到类G2(0)G_2^{(0)}

x5=(5,2)Tx_5= (5, 2)^Td(x5,m1(0))=25,d(x5,m2(0))=29d(x_5,m_1^{(0)})=25,d(x_5,m_2^{(0)})=29,将x5x_5分到类G1(0)G_1^{(0)}

(3)得到新的类G1(1)={x1,x5},G2(1)={x2,x3,x4}G_1^{(1)}=\{x_1,x_5\},G_2^{(1)}=\{x_2,x_3,x_4\},计算类的中心m1(1),m2(1)m_1^{(1)}, m_2^{(1)}

m1(1)=(2.5,2.0)T,m2(1)=(2,0)Tm_1^{(1)}=(2.5,2.0)^T,m_2^{(1)}=(2,0)^T

(4)重复步骤(2)和步骤(3)。

x1x_1分到类G1(1)G_1^{(1)},将x2x_2分到类G2(1)G_2^{(1)},将x3x_3分到类G2(1)G_2^{(1)},将x4x_4分到类G2(1)G_2^{(1)},将x5x_5分到类G1(1)G_1^{(1)}

得到新的类G1(2)={x1,x5},G2(2)={x2,x3,x4}G_1^{(2)}=\{x_1,x_5\},G_2^{(2)}=\{x_2,x_3,x_4\}

由于得到的新的类没有改变,聚类停止。得到聚类结果:

G1={x1,x5},G2={x2,x3,x4}G_1^*=\{x_1,x_5\},G_2^*=\{x_2,x_3,x_4\}

算法特性

总体特点

k均值聚类有以下特点:基于划分的聚类方法:类别数kk事先指定;以欧式距离平方表示样本之间的距离,以中心或样本的均值表示类别;以样本和其他所属类的中心之间的距离的总和为最优化的目标函数;得到的类别是平坦的、非层次化的;算法是迭算法,不能保证得到全局最优。

收敛性

k均值聚类属于启发式方法,不能保证收敛到全局最优,初始中心的选择会直接影响聚类结果。注意,类中心在聚类的过程中会发生移动,但是往往不会移动太大,因为在每一步,样本被分到与其最近的中心的类中。

初始类的选择

选择不同的初始中心,会得到不同的聚类结果。针对上面的例子,如果改变两个类的初始中心,比如选择

m1(0)=x1,m2(0)=x5m_1^{(0)}=x_1,m_2^{(0)}=x_5

那么x2x_2x3x_3会分到G1(0)G_1^{(0)}x4x_4会分到G2(0)G_2^{(0)},形成聚类结果G1(1)={x!,x2,x3}, G2(1)={x4,x5}G_1^{(1)}=\{x_!,x_2,x_3\},\ G_2^{(1)}=\{x_4,x_5\},中心是m1(1)=(0.33,0.67)T,m2(1)=(5,1)Tm_1^{(1)}=(0.33,0.67)^T,m_2^{(1)}=(5,1)^T。继续迭代,聚类结果仍然是G1(1)={x!,x2,x3}, G2(1)={x4,x5}G_1^{(1)}=\{x_!,x_2,x_3\},\ G_2^{(1)}=\{x_4,x_5\}。聚类停止。

初始中心的选择,比如可以用层次聚类对样本进行聚类,得到kk个类时停止。然后从每个类中选择一个与中心距离最近的点。

类别个数k的选择

k均值聚类中的类别数kk值需要预先指定,而在实际应用中最优的kk值是不知道的,解决这个问题的一个方法是尝试用不同的kk值聚类,检验各自的到的聚类结果的质量,推测最优的kk值。聚类结果的质量可以用类的平方直径来衡量。一般地,类别数目变小时,平均直径会增加;类别数目变大超过某个值以后,平均直径会不变;而这个值正是最有的kk值。下图说明类别数与平均直径的关系。实验时,可以采用二分查找,快速找到最优的kk值。

下图是聚类的类别数和平均直径的关系。

KMeans-classes-and-diameter

代码实践

sklearn中的KMeans实现

这个简单的例子来自sklearn.cluster.KMeans

word2vec中KMeans实现

word2vec中的KMeans源码摘录

下面这段代码是从word2vec.c中的源码摘录出来的KMeans部分,我自己改了一些变量的名称,还加了一些注释,方便看懂。

对embdding进行KMeans聚类

word2vec训练出来的embedding结果,以二进制进行存储,其格式为

注意:vec[0] vec[1] ... vec[end]需要以二进制格式存储。

将下面KMeans.c文件进行编译,然后用下面的sh脚本运行。

这里面的向量是经过归一化的,其中心向量也经过了归一化,所以其实算的是cos距离,是球面聚类。

用于编译KMeans.c文件的makefile文件如下,用make命令编译。

在shell脚本中确定输入文件,输出文件,以及聚类的类别个数,下面的sh中指定了聚类类别为30。

运行shell文件,则会输出聚类结果文件。至此聚类完成。经过测试,量级在5百万的数据,可在3分钟内聚类完成。

然后如果需要用sql查询做一些分析,就考虑将生成的文件传到hdfs中

然后导入sql

就可以做想做的分析了

参考资料

  • 《统计学习方法第二版-李航》

本文的理论部分摘抄自李航书的对应章节。

“word2vec中的KMeans源码摘录”参考此文。

Last updated

Was this helpful?