K-Means 聚类算法原理分析与代码实现

前言

  在前面的文章中,涉及到的机器学习算法均为监督学习算法。

  所谓监督学习,就是有训练过程的学习。再确切点,就是有 "分类标签集" 的学习。

  现在开始,将进入到非监督学习领域。从经典的聚类问题展开讨论。所谓聚类,就是事先并不知道具体分类方案的分类 (允许知道分类个数)。

  本文将介绍一个最为经典的聚类算法 - K-Means 聚类算法以及它的两种实现。

现实中的聚类分析问题 - 总统大选

  假设 M 国又开始全民选举总统了,目前 Mr.OBM 的投票率为48%(投票数占所有选民人数的百分比),而 Mr.MKN 的为47%,而剩下的一部分出于【种种原因】没有投票。

  做为其中某个阵营的人,自然是希望能够尽可能的争取到这些剩余的票 -因为这完全可能影响最终选举结果。

  然而,你不可能争取到这些人的所有投票,因为你满足某个群体的人,也许就伤害到了另一群人的利益。

  一个很不错的想法是将这些人分为 K 个群体,然后主要对其中人数最多的几个群体做工作。这就需要使用到聚类的策略了。

  聚类策略是搜集剩余选民的用户信息(各种满意/不满意的信息),将这些信息输入进聚类算法,然后对聚类结果中人数最多的簇的选民做思想工作。

  可能你会发现某个簇的选民都是一个社区的,一个宗教信仰的,或者具有某些共性。这样就方便各种各样的拉票活动了。

K-Means 聚类算法

  K,指的是它可以发现 K 个簇;Means,指的是簇中心采用簇所含的值的均值来计算。

  下面先给出伪代码:

1 创建 k 个点作为起始质心 (随机选择):
2     当任意一个点的簇分配结果发生改变的时候:
3         对数据集中的每个数据点:
4             对每个质心:
5                 计算质心与数据点之间的距离
6             将数据点分配到距其最近的簇
7         对每一个簇:
8             求出均值并将其更新为质心

  然后是一个具体实现Python程序:

  1 #!/usr/bin/env python
  2 # -*- coding:UTF-8 -*-
  3
  4 ‘‘‘
  5 Created on 2015-01-05
  6
  7 @author: fangmeng
  8 ‘‘‘
  9
 10 from numpy import *
 11
 12 #==================================
 13 # 输入:
 14 #        fileName: 数据文件名(含路径)
 15 # 输出:
 16 #        dataMat: 数据集
 17 #==================================
 18 def loadDataSet(fileName):
 19     ‘载入数据文件‘
 20
 21     dataMat = []
 22     fr = open(fileName)
 23     for line in fr.readlines():
 24         curLine = line.strip().split(‘\t‘)
 25         fltLine = map(float,curLine)
 26         dataMat.append(fltLine)
 27     return dataMat
 28
 29 #==================================================
 30 # 输入:
 31 #        vecA: 样本a
 32 #        vecB: 样本b
 33 # 输出:
 34 #        sqrt(sum(power(vecA - vecB, 2))): 样本距离
 35 #==================================================
 36 def distEclud(vecA, vecB):
 37     ‘计算样本距离‘
 38
 39     return sqrt(sum(power(vecA - vecB, 2)))
 40
 41 #===========================================
 42 # 输入:
 43 #        dataSet: 数据集
 44 #        k: 簇个数
 45 # 输出:
 46 #        centroids: 簇划分集合(每个元素为簇质心)
 47 #===========================================
 48 def randCent(dataSet, k):
 49     ‘随机初始化质心‘
 50
 51     n = shape(dataSet)[1]
 52     centroids = mat(zeros((k,n)))#create centroid mat
 53     for j in range(n):#create random cluster centers, within bounds of each dimension
 54         minJ = min(dataSet[:,j])
 55         rangeJ = float(max(dataSet[:,j]) - minJ)
 56         centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))
 57     return centroids
 58
 59 #===========================================
 60 # 输入:
 61 #        dataSet: 数据集
 62 #        k: 簇个数
 63 #        distMeas: 距离生成器
 64 #        createCent: 质心生成器
 65 # 输出:
 66 #        centroids: 簇划分集合(每个元素为簇质心)
 67 #        clusterAssment: 聚类结果
 68 #===========================================
 69 def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
 70     ‘K-Means基本实现‘
 71
 72     m = shape(dataSet)[0]
 73     # 簇分配结果矩阵。一列为簇分类结果,一列为误差。
 74     clusterAssment = mat(zeros((m,2)))
 75     # 创建原始质心集
 76     centroids = createCent(dataSet, k)
 77     # 簇更改标记
 78     clusterChanged = True
 79
 80     while clusterChanged:
 81         clusterChanged = False
 82
 83         # 每个样本点加入其最近的簇。
 84         for i in range(m):
 85             minDist = inf; minIndex = -1
 86             for j in range(k):
 87                 distJI = distMeas(centroids[j,:],dataSet[i,:])
 88                 if distJI < minDist:
 89                     minDist = distJI; minIndex = j
 90             if clusterAssment[i,0] != minIndex: clusterChanged = True
 91             clusterAssment[i,:] = minIndex,minDist**2
 92
 93         # 更新簇
 94         for cent in range(k):#recalculate centroids
 95             ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]
 96             centroids[cent,:] = mean(ptsInClust, axis=0)
 97
 98     return centroids, clusterAssment
 99
100 def main():
101     ‘k-Means聚类操作展示‘
102
103     datMat = mat(loadDataSet(‘/home/fangmeng/testSet.txt‘))
104     myCentroids, clustAssing = kMeans(datMat, 4)
105
106     #print myCentroids
107     print clustAssing
108
109 if __name__ == "__main__":
110    main()

  测试结果:

  

K-Means性能优化

  主要有两种方式:

  1. 分解最大SSE (误差平方和)的簇

  PS:直接在簇内执行一次 k=2 的 K-Means 聚类即可。

  2. 合并距离最小的簇 或者 合并SSE增幅最小的两个簇

  基于这两种最基本优化策略,有一种更为科学的聚类算法 - 二分K-Means算法,下面进行详细介绍。

二分K-Means算法

  该算法大致思路为:首先将所有的点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续划分。

  选择方法自然是选择SSE增加更小的那个方式。

  如此不断 "裂变",直到得到用户指定数目的簇。

  伪代码:

将所有点视为一个簇:
    当簇数目小于k时:
        对于每一个簇:
            计算SSE
            在给定的簇上面进行 k=2 的K-Means聚类
            计算将簇一分为二后的SSE
        选择使得误差最小的那个簇进行划分操作

  具体实现函数:

 1 #======================================
 2 # 输入:
 3 #        dataSet: 数据集
 4 #        k: 簇个数
 5 #        distMeas: 距离生成器
 6 # 输出:
 7 #        mat(centList): 簇划分集合(每个元素为簇质心)
 8 #        clusterAssment: 聚类结果
 9 #======================================
10 def biKmeans(dataSet, k, distMeas=distEclud):
11     ‘二分K-Means聚类算法‘
12
13     m = shape(dataSet)[0]
14     # 聚类结果数据结构
15     clusterAssment = mat(zeros((m,2)))
16     # 原始质心
17     centroid0 = mean(dataSet, axis=0).tolist()[0]
18     centList =[centroid0]
19
20     # 统计原始SSE
21     for j in range(m):
22         clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2
23
24     # 循环执行直到得到k个簇
25     while (len(centList) < k):
26         # 最小SSE
27         lowestSSE = inf
28         # 找到最适合分裂的簇进行分裂
29         for i in range(len(centList)):
30             ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
31             centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
32             sseSplit = sum(splitClustAss[:,1])
33             sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
34
35             if (sseSplit + sseNotSplit) < lowestSSE:
36                 bestCentToSplit = i
37                 bestNewCents = centroidMat
38                 bestClustAss = splitClustAss.copy()
39                 lowestSSE = sseSplit + sseNotSplit
40
41         # 本次划分信息
42         bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)
43         bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
44
45         # 更新簇集
46         centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]
47         centList.append(bestNewCents[1,:].tolist()[0])
48         # 更新聚类结果集
49         clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss
50
51     return mat(centList), clusterAssment

  测试结果:

  

小结

  1. KMeans的用途很广泛,再举个例子吧:比如你计划要去中国100个城市旅游,那么如何规划路线呢?

  ---> 可以采用聚类的方法,将这些城市聚到几个簇里面,然后一个 ”簇"一个 "簇" 的进行游玩。质心就相当于机场,误差平方和就相当于游玩城市到质心的距离 :)

  2. KMeans算法是很常用的聚类算法,然而,这里也要提一提它的缺点 - k值选取很难。这个话题也产生了很多研究,文章。有兴趣的读者可以进一步研究。

  

时间: 2024-10-18 01:19:05

K-Means 聚类算法原理分析与代码实现的相关文章

朴素贝叶斯分类算法原理分析与代码实现

前言 本文介绍机器学习分类算法中的朴素贝叶斯分类算法并给出伪代码,Python代码实现. 词向量 朴素贝叶斯分类算法常常用于文档的分类,而且实践证明效果是挺不错的. 在说明原理之前,先介绍一个叫词向量的概念. --- 它一般是一个布尔类型的集合,该集合中每个元素都表示其对应的单词是否在文档中出现. 对应关系和词汇表一一对应. 比如说,词汇表只有三个单词:'apple', 'orange', 'melo',某文档中,apple和melo出现过,那么其对应的词向量就是 {1, 0, 1}. 这种模型

第五篇:朴素贝叶斯分类算法原理分析与代码实现

前言 本文介绍机器学习分类算法中的朴素贝叶斯分类算法并给出伪代码,Python代码实现. 词向量 朴素贝叶斯分类算法常常用于文档的分类,而且实践证明效果挺不错的. 在说明原理之前,先介绍一个叫词向量的概念. --- 它一般是一个布尔类型的集合,该集合中每个元素都表示其对应的单词是否在文档中出现. 比如说,词汇表只有三个单词:'apple', 'orange', 'melo',某文档中,apple和melo出现过,那么其对应的词向量就是 {1, 0, 1}. 这种模型通常称为词集模型,如果词向量元

第十四篇:Apriori 关联分析算法原理分析与代码实现

前言 想必大家都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事. 那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢? 这就是关联分析所要完成的任务了. 本文将讲解关联分析领域中最为经典的Apriori算法,并给出具体的代码实现. 关联分析领域的一些概念 1. 频繁项集: 数据集中经常出现在一起的物品的集合.例如 "啤酒和尿布" 2. 关联规则: 指两个物品集之间可能存在很强的关系.例如 "{啤酒} -> {尿布}"

Apriori 关联分析算法原理分析与代码实现

前言 想必大家都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事. 那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢? 这就是关联分析所要完成的任务了. 本文将讲解关联分析领域中最为经典的Apriori算法,并给出具体的代码实现. 关联分析领域的一些概念 1. 频繁项集: 数据集中经常出现在一起的物品的集合.例如 "啤酒和尿布" 2. 关联规则: 指两个物品集之间可能存在很强的关系.例如 "{啤酒} -> {尿布}"

k-均值聚类算法;二分k均值聚类算法

根据<机器学习实战>一书第十章学习k均值聚类算法和二分k均值聚类算法,自己把代码边敲边理解了一下,修正了一些原书中代码的细微差错.目前代码有时会出现如下4种报错信息,这有待继续探究和完善. 报错信息: Warning (from warnings module): File "F:\Python2.7.6\lib\site-packages\numpy\core\_methods.py", line 55 warnings.warn("Mean of empty

Adaboost算法原理分析和实例+代码(简明易懂)

Adaboost算法原理分析和实例+代码(简明易懂) [尊重原创,转载请注明出处] http://blog.csdn.net/guyuealian/article/details/70995333     本人最初了解AdaBoost算法着实是花了几天时间,才明白他的基本原理.也许是自己能力有限吧,很多资料也是看得懵懵懂懂.网上找了一下关于Adaboost算法原理分析,大都是你复制我,我摘抄你,反正我也搞不清谁是原创.有些资料给出的Adaboost实例,要么是没有代码,要么省略很多步骤,让初学者

Kmeans聚类算法原理与实现

Kmeans聚类算法 1 Kmeans聚类算法的基本原理 K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一.K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类.通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果. 假设要把样本集分为k个类别,算法描述如下: (1)适当选择k个类的初始中心,最初一般为随机选取: (2)在每次迭代中,对任意一个样本,分别求其到k个中心的欧式距离,将该样本归到距离最短的中心所在的类: (3)利用

常见经典排序算法学习总结,附算法原理及实现代码(插入、shell、冒泡、选择、归并、快排等)

博主在学习过程中深感基础的重要,经典排序算法是数据结构与算法学习过程中重要的一环,这里对笔试面试最常涉及到的7种排序算法(包括插入排序.希尔排序.选择排序.冒泡排序.快速排序.堆排序.归并排序)进行了详解.每一种算法都有基本介绍.算法原理分析.算法代码. 转载请注明出处:http://blog.csdn.net/lsh_2013/article/details/47280135 插入排序 1)算法简介 插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过

机器学习实战笔记-利用K均值聚类算法对未标注数据分组

聚类是一种无监督的学习,它将相似的对象归到同一个簇中.它有点像全自动分类.聚类方法几乎可以应用于所有对象,簇内的对象越相似,聚类的效果越好 簇识别给出聚类结果的含义.假定有一些数据,现在将相似数据归到一起,簇识别会告诉我们这些簇到底都是些什么.聚类与分类的最大不同在于,分类的目标事先巳知,而聚类则不一样.因为其产生的结果与分类相同,而只是类别没有预先定义,聚类有时也被称为无监督分类(unsupervised classification ). 聚类分析试图将相似对象归人同一簇,将不相似对象归到不