K-均值算法的原理与实战

K-均值(K-means)算法是一种常用的聚类算法。

  当我们需要对未标记的数据划分类别时,往往用到的算法是聚类(clustering)。聚类是一种无监督的学习,它试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇”(cluster);相似的样本归到一个簇中,不相似的样本分到不同的簇中。
  K-均值(K-means)算法是一种聚类算法,k 是用户指定的簇个数,每个簇通过其质心(centroid),即簇中所有点的中心来描述。

算法原理

  给定样本集 D D D = { x 1 , x 2 , . . . , x m x_1,x_2,...,x_m x1,x2,...,xm}, k k k 均值算法将样本集 D D D划分为 k k k个互不相交的簇 C C C = { C 1 , C 2 , . . . , C k C_1,C_2,...,C_k C1,C2,...,Ck},划分的依据是样本的相似度,相似的样本会归入同一簇,不相似的样本会归入不同簇。
  相似度的计算方法有多种,常见的有曼哈顿距离,欧几里得距离,余弦相似度等。 k k k 均值算法使用的是欧几里得距离(Euclidean Distance),即两点间的真实距离。给定样本 x i = ( x i 1 , x i 2 , . . . , x i n ) x_i = (x_{i1},x_{i2},...,x_{in}) xi=(xi1,xi2,...,xin) x j = ( x j 1 , x j 2 , . . . , x j n ) x_j = (x_{j1},x_{j2},...,x_{jn}) xj=(xj1,xj2,...,xjn),计算样本间的欧几里得距离的公式如下:
d i s t e d ( x i , x j ) = ∣ ∣ x i − x j ∣ ∣ 2 = ∑ u = 1 n ∣ x i u − x j u ∣ 2 dist_{ed}(x_i,x_j) = ||x_i-x_j||_2 = \sqrt{\sum_{u=1}^{n}{|x_{iu}-x_{ju}|}^2} disted(xi,xj)=xixj2=u=1nxiuxju2

   k k k 均值算法的流程是这样的。第一步,从数据集中随机选择 k k k个点作为簇质心。第二步,将样本集中的每个点分配到一个簇中,具体来讲,为每个点找距其最近的质心,并将其分配给该质心所对应的簇。第三步,每个簇的质心更新为该簇所有点的平均值。重复上述步骤二与步骤三,直到所有的簇不再发生变化。该过程的伪代码表示如下:


输入: 样本集 D D D = { x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn};
       聚类簇数 k k k
过程:

  1. D D D中随机选择 k k k个样本作为初始均值向量{ u 1 , u 2 , . . . , u k u_1,u_2,...,u_k u1,u2,...,uk}
  2. repeat
  3.   令 C i = ∅ ( 1 ≤ i ≤ k ) C_i = \emptyset (1 \leq i \leq k) Ci=(1ik)
  4.   for j = 1 , 2 , . . . , m j = 1,2,...,m j=1,2,...,m do
  5.     计算样本 x i x_i xi与各均值向量 u i u_i ui( 1 ≤ i ≤ k ) 1 \leq i \leq k) 1ik)的距离: d j i = ∣ ∣ x j − u i ∣ ∣ 2 d_{ji}=||x_j-u_i||_2 dji=xjui2
  6.     根据距离最近的均值向量确定 x i x_i xi的簇标记: λ i = a r g m i n i ∈ { 1 , 2 , . . . , k } d j i \lambda_i = argmin_{i \in {\{1,2,...,k\}}} d_{ji} λi=argmini{1,2,...,k}dji;
  7.     将样本 x j x_j xj划入相应的簇: C λ j ⋃ { x j } C_{\lambda_j}\bigcup\{x_j\} Cλj{xj}
  8.   end for
  9.   for i = 1 , 2 , . . . , k i = 1,2,...,k i=1,2,...,k do
  10.     计算新均值向量: u i ′ = 1 ∣ C i ∣ ∑ x ∈ C i x u_i^{'} = \frac{1}{|C_i|}\sum_{x \in C_i}x ui=Ci1xCix
  11.     if u i ′ ≠ u i u_i^{'} \neq u_i ui=ui then
  12.       将当前均值向量 u i u_i ui更新为 u i ′ u_i^{'} ui
  13.     else
  14.       保持当前均值向量不变
  15.     end if
  16.   end for
  17. until 所有样本的簇标记均未更新

输出: 簇划分 C = { C 1 , C 2 , . . . , C k } C = \{C_1,C_2,...,C_k\} C={C1,C2,...,Ck}


  簇划分完成后,如何评价聚类效果的好与坏呢?一种用于度量聚类效果的指标是SSE(Sum of Squared Error,误差平方和),SSE值越小表示数据点越接近于它们的质心,聚类效果也越好。
S S E = ∑ i = 1 k ∑ x ∈ C i ∣ ∣ x − u i ∣ ∣ 2 2 SSE = \sum_{i=1}^k\sum_{x\in C_i}||x-u_i||_2^2 SSE=i=1kxCixui22

其中 u i = 1 ∣ C i ∣ ∑ x ∈ C i x u_i = \frac{1}{|C_i|}\sum_{x \in C_i}x ui=Ci1xCix 是簇 C i C_i Ci的均值向量。该式在一定程度上刻画了簇内样本围绕簇均值向量的紧密程度, S S E SSE SSE值越小则簇内样本相似度越高。

编程实战

  首先从百度网盘下载测试数据集,下载链接:https://pan.baidu.com/s/1wShFlsq36Fez5-Qh10Jpog ,提取码:ltaw。然后在Jupyter Notebook中运行以下代码。

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
#文本文件导入矩阵
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t') #当前行切割为列表
        fltLine = list(map(float,curLine)) #转换元素的数据类型为float
        dataMat.append(fltLine)
    return np.mat(dataMat)

#计算两个向量的欧氏距离
def distED(vecA, vecB):
    return np.sqrt(np.sum(np.power(vecA - vecB, 2))) 

#构建k个初始簇质心
def randCent(dataSet, k):
    m = np.shape(dataSet)[0] #总样本数
    centIdx = set()   #随机样本索引
    while len(centIdx) < k:
        randj = np.random.randint(m)
        centIdx.add(randj)
    centroids = dataSet[list(centIdx)] #初始簇质心
    return centroids

#K-均值聚类方法
def kMeans(dataSet, k, distMeas=distED, createCent=randCent):
    m = np.shape(dataSet)[0]  #样本总数
    clusterAssment=np.mat(np.zeros((m,2))) #簇分配结果矩阵
    centroids=createCent(dataSet,k) #构建k个初始随机质心
    clusterChanged=True  #簇变化标志
    while clusterChanged:  #直到簇不再变化
        clusterChanged=False
        for i in range(m): #遍历所有样本点
            minDist=np.inf  #样本点离最近质心的距离
            minIndex=-1  #样本的簇标记
            for j in range(k): #遍历所有的质心
                distJI = distMeas(centroids[j,:],dataSet[i,:]) #样本点I到质心J的距离
                if distJI < minDist:
                    minDist=distJI
                    minIndex=j 
            if clusterAssment[i,0] != minIndex: clusterChanged = True  #样本点的簇标记发生变化
            clusterAssment[i,:]= minIndex,minDist**2  #簇分配结果矩阵的两列分别为簇标记和误差
        for cent in range(k): #更新质心的位置
            ptsInClust = dataSet[np.nonzero(clusterAssment[:,0].A == cent)[0]] #给定簇下的所有样本点
            centroids[cent,:] = np.mean(ptsInClust, axis=0) #axis=0表示沿列方向计算均值
    return centroids,clusterAssment

#绘制所有样本点与质心
def drawPic(dataMat,centroids,clusterAssment):
    type1_x=[];type1_y=[];type2_x=[];type2_y=[];type3_x=[];type3_y=[];type4_x=[];type4_y=[]
    for i in range(dataMat.shape[0]):
        if clusterAssment[i,0]==0:
            type1_x.append(dataMat[i,0])
            type1_y.append(dataMat[i,1])
        elif clusterAssment[i,0]==1:
            type2_x.append(dataMat[i,0])
            type2_y.append(dataMat[i,1])
        elif clusterAssment[i,0]==2:
            type3_x.append(dataMat[i,0])
            type3_y.append(dataMat[i,1])
        elif clusterAssment[i,0]==3:
            type4_x.append(dataMat[i,0])
            type4_y.append(dataMat[i,1])
    cent_x=centroids[:,0].T.tolist()[0]
    cent_y=centroids[:,1].T.tolist()[0]
    p1 = plt.scatter(type1_x,type1_y,s=30,marker='x')
    p2 = plt.scatter(type2_x,type2_y,s=30,marker='x')
    p3 = plt.scatter(type3_x,type3_y,s=30,marker='x')
    p4 = plt.scatter(type4_x,type4_y,s=30,marker='x')
    cent = plt.scatter(cent_x,cent_y,s=200,marker='o')
    plt.legend(["p1","p2","p3",'p4','cent'],bbox_to_anchor=(1, 1))
    plt.show()

#加载数据集
dataMat=loadDataSet('kMeansTestSet.txt') 
#生成聚类结果(簇质心和簇分配结果)
myCentroids, myClusterAssment = kMeans(dataMat, 4) 
#绘制聚类结果图
drawPic(dataMat, myCentroids, myClusterAssment) 

  最终的聚类结果图如下,其中p1-p4为划分为4种类别的簇,cent为簇质心。
在这里插入图片描述

结语

  聚类是一种无监督学习方法。所谓无监督学习是指事先并不知道要寻找的内容,即没有目标变量。聚类试图做到“物以类聚”,即将相似数据点归于同一簇,而不相似数据点归于不同簇。聚类中相似的概念取决于所选择的相似度计算方法。
  一种广泛使用的聚类算法是K-均值算法,其中k是用户指定的要创建的簇的数目。K-均值算法以 k k k个随机质心开始。算法会计算每个点到质心的距离,每个点会被分配到距其最近的簇质心。然后基于新分配到簇的点更新簇质心。以上过程重复数次,直到簇质心不再改变。
  K-均值算法非常简单有效,但是也容易受到初始簇质心的影响。算法只是收敛到局部最小值,而非全局最小值。一种聚类效果的评估指标是SSE,值越小表示数据点越接近质心,聚类效果也越好。其中一种比较笨的解决办法是,通过多次执行该算法,选择SSE值最小的那一个。
  为了克服K-均值算法只收敛到局部最小值的问题,下篇文章将会讲解聚类效果更好的二分K-均值(bisecting K-means)算法。

【摘要】 目前,对于聚类问题的研究普遍存在于社会生活中的各个领域,如模式识别、图像处理、机器学习和统计学等。关于对生活中各种各样的数据的聚类分类问题已经成为众多学者的研究热题之一。聚类和分类的区别在于,聚类没有任何先验知识可循,要通过数据自身的特点,将数据自动的划分到不同的类别中。聚类的基本形式定义为“在已给的数据集合中寻找数据点集的同类集合。每一个集合叫做一个类,并确定了一个区域,在区域中对象的密度高于其他区域中的密度。”聚类方法有很多种,其中最简单的形式便是划分式聚类,划分式聚类试图将给定的数据集合分割成不相交的子集,使具体的聚类准则是最优的。实际中应用最广泛的准则是聚类误差平方和准则,即对于每一个点都计它到相应的聚类中心点的平方距离,并对数据集合上的所有点的距离进行求和。一种最流行的基于最小聚类误差平法和的聚类方法是K-均值算法。然而,K-均值算法是一个局部搜索的算法,它存在一些严重的不足,比如K值需要预先确定、聚类结果的好坏依赖于初始点的选取。为了解决这些问题,这个领域的研究者开发了很多其他的一些技术,试图基于全局最优化的方法来解决聚类问题(比如模拟退火算法、遗传算法等)。然而这些技术并没有得到广泛的认可,在许多实际应用中应用最多的还是反复利用K-均值算法。K-均值算法是一种基于划分的聚类算法,它通过不断的迭代来进行聚类,当算法收敛到一个结束条件时就终止迭代过程,输出聚类结果。由于其算法思想简便,又容易实现对大规模数据的聚类,因此K-均值算法已成为一种最常用的聚类算法之一K-均值算法能找到关于聚类误差的局部的最优解,是一个能应用在许多聚类问题上的快速迭代算法。它是一种以点为基础的聚类算法,以随机选取的初始点为聚类中心,迭代地改变聚类中心来使聚类误差最小化。这种方法最主要的不足就是对于初始聚类中心点位置的选取敏感。因此,为了得到近似最优解,初始聚类中心的位置必须安排的有差异。本文就K-均值聚类算法的聚类结果依赖于初始中心,而且经常收敛于局部最优解,而非全局最优解,以及聚类类别数K需要事先给定这两大缺憾展开研究。提出了分别解决这两个问题的算法各一个首先,本文将Hae-Sang等人的快速K-中心点算法确定初始中心点的思想应用于Aristidis Likas的全局K-均值聚类算法中下一个簇的初始中心选择上,提出一种改进的全局K-均值聚类算法,试图寻找一个周围样本点分布比较密集,且距离现有簇的中心都较远的样本点,将其作为下一个簇的最佳初始中心。通过对UCI机器学习数据库数据及人工随机模拟数据的测试,证明本文算法与Aristidis Likas的全局K-均值聚类算法和快速全局K-均值聚类算法比,在不影响聚类误差平方和的前提下,聚类时间更短,具有更好的性能。同时,本文介绍了自组织特征映射网络(Self-Organizing Feature Map, SOFM)的相关内容,SOFM网络是将多维数据映射到低维规则网格中,可以有效的进行大规模的数据挖掘,其特点是速度快,但是分类的精度不高。而K-均值聚类算法,是一种通过不断迭代调整聚类质心的算法,其特点是精度高,主要用于中小数据集的分类,但是聚类速度比较慢。因此,本文在分析了基于自组织特征映射网络聚类的学习过程,权系数自组织过程中邻域函数,以及学习步长的一般取值问题后,给出了基于自组织特征映射网络聚类实现的具体算法,将自组织特征网络与K-均值聚类算法相结合,提出了一种基于自组织映射网络的聚类方法,来实现对输入模式进行聚类,实现K-均值聚类算法的聚类类别数的自动确定。同时通过实验进行仿真实现,证明该算法的有效性。 还原 【Abstract】 Clustering is a fundamental problem that frequently arises in a great variety of fields such as pattern recognition, image processing, machine learning and statistics. In general, clustering is defined as the problem of finding homogeneous groups of samples in a given data set. Each of these groups is called a cluster and can be defined as a region in which the density of exemplars is locally higher than in other regions.The simplest form of clustering is partition clustering w
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值