第10章 利用K-均值聚类算法对未标注数据分组

写在前面

关于无监督学习:
在无监督学习中,类似分类和回归的目标变量事先是不存在的。与前面的“对于输入数据X能预测变量Y”的不同是,这个要回答的问题是:“从数据X能发现什么?构成X的最佳6个数据簇都是哪些?”或者“X中哪三个特征是最频繁共现的?”


本章内容:
● K-均值聚类算法
● 对聚类得到的簇进行后处理
● 二分K-均值聚类算法
● 对地理位置进行聚类

聚类是一种无监督的学习,它将相似的对象归到同一个簇中。它有点像全自动分类。聚类方法几乎可以应用于所有对象,簇内的对象越相似,聚类的效果越好。
K-均值可以发现K个不同的簇,且每个簇的中心采用簇中所含值得均值计算而成。
假定有一些数据,现在讲相似数据归到一起,簇识别会告诉我们这些簇到底是什么。聚类和分类的最大不同在于,分类的目标已经事先得知,而聚类则不一样,类别没有预先定义,被称之为无监督学习。

10.1 K-均值聚类算法
优点:容易实现。
缺点:可能收敛到局部最小值,在大规模数据集熵收敛较慢。
适用数据类型:数值型数据。

K-均值是发现给定数据集的k个簇的算法。簇个数k是用户给定的,每一个簇通过其质心,即簇中所有点的中心来描述。

具体流程:(1).随机确定k个起始点为质心;(2)为每个点寻找距离最近的质心;(3)将其分配给该质心所对应的簇;(4)每个簇的质心更新为该簇所有点的平均值。

测试上面三个函数的效果:

In [20]: reload(kMeans)
Out[20]: <module 'kMeans' from 'kMeans.pyc'>

In [21]: from numpy import *
    ...: import numpy as np

In [24]: datMat = np.mat(kMeans.loadDataSet(r'E:\ML\ML_source_code\mlia\Ch10\testSet.txt'))

In [25]: min(datMat[:,0])
Out[25]: matrix([[-5.379713]])

In [27]: min(datMat[:,1])
Out[27]: matrix([[-4.232586]])

In [28]: max(datMat[:,0])
Out[28]: matrix([[ 4.838138]])

In [29]: max(datMat[:,1])
Out[29]: matrix([[ 5.1904]])

randCent()函数会生成min和max之间的值。
In [30]: kMeans.randCent(datMat, 2)
Out[30]:
matrix([[ 4.27029516, 1.54977582],
[-0.13624927, -1.21262219]])

In [31]: kMeans.distEclub(datMat[0], datMat[1])
Out[31]: 5.184632816681332

下面创建完整的K-Means均值算法:
算法随机创建k个质心,然后将每个点分配到最近的质心,再重做计算质心,这个过程重复无数次,直到数据点的簇分配结果不在改变为止。
迭代方式:计算质心–分配–重新计算

In [4]: reload(kMeans)
Out[4]: <module 'kMeans' from 'kMeans.pyc'>

In [5]: from numpy import *
   ...: import numpy as np
   ...:

In [6]: datMat = np.mat(kMeans.loadDataSet(r'E:\ML\ML_source_code\mlia\Ch10\testSet.txt'))

In [7]: import matplotlib.pyplot as plt
   ...: datMat = np.mat(kMeans.loadDataSet(r'E:\ML\ML_source_code\mlia\Ch10\testSet.txt'))
   ...: plt.plot(datMat[:,0],datMat[:,1],'ro')
   ...: plt.show()

未进行聚类的原始数据

这里写图片描述

In [8]: myCentroids, clustAssing = kMeans.kMeans(datMat, 4)
[[-0.98796094 -0.58696604]
[-3.70551776 1.09689364]
[ 0.3879127 0.68233075]
[ 1.06703587 -2.64449294]]
[[-3.3499106 -2.81942887]
[-2.97746971 1.98856076]
[ 2.24547378 2.92273278]
[ 2.51621757 -2.96890957]]
[[-3.53973889 -2.89384326]
[-2.46154315 2.78737555]
[ 2.6265299 3.10868015]
[ 2.65077367 -2.79019029]]

In [28]: f = plt.figure(1)
    ...: plt.subplot(111)
    ...: # with legend
    ...: plt.scatter(myCentroids[:,0],myCentroids[:,1], marker = 'x',color = 'b', s = 50)
    ...: plt.scatter(datMat[:,0],datMat[:,1], color = 'r', s = 30)
    ...: plt.legend(loc = 'upper right')
    ...: plt.show()

这里写图片描述

经过三次迭代之后K-均值算法收敛。

10.2 使用后来处理提高聚类性能
k是K-均值聚类中用户预先定义的参数,那如何才能知道k的选择是否正确呢?
实际上在包含簇的分配结果的矩阵中保存着每个点的误差,即为点到簇质心的距离的平方。

下图找那个聚类结果,包含一个三个簇的数据集上运行K-均值算法之后的结果,但是结果并不那么准确。 是因为K-均值算法收敛到了局部最小值,而不是全局最小值。

一种用于度量聚类效果的指标是SSE(误差平方和),对应程序的中clusterAssment矩阵的第一列之和。SSE值越小表示数据点月接近他的质心,聚类的效果也就越好。

两种量化的方法:合并最近的质心,或者合并两个使得SSE增幅最小的质心。
1.通过计算所有质心之间的距离,然后合并距离最近的两个点来实现。第二种方法需要合并两个簇然后计算总得SSE值。须在所有可能的两个簇上重复上述的过程,直到找到合并最佳的两个簇为止。

10.3 二分K-均值算法
为了克服K-均值算法收敛于局部最小的问题,有人提出了二分K-均值算法。
该算法首先将所有额点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续划分(根据是否可以最大程序降低SSE值来决定选择哪个簇)。基于SSE的划分过程不断重复,直到得到用户指定的簇数为止。

二分均值算法,伪代码

"""
将所有点看成一个簇
当簇数目小于K时
对于每一个簇
   计算总误差
   在给定的簇上进行K-均值聚类(k = 2)
   计算将该簇一分为二之后的总误差
选择使得误差最小的那个簇进行划分操作
"""
##二分K-均值聚类算法
def biKmeans(dataSet, k ,distMeas = distEclub):
    m = np.shape(dataSet)[0]
    clusterAssment = np.mat(np.zeros((m, 2)))
    #创建一个初始簇
    centroid0 = np.mean(dataSet, axis = 0).tolist()[0]
    centList = [centroid0]
    for j in range(m):
        clusterAssment[j ,1] = distMeas(np.mat(centroid0), dataSet[j,:])**2
    while (len(centList) < k):
        lowestSSE = inf
        for i in range(len(centList)):
            #尝试划分每一簇
            ptsInCurrCluster = dataSet[np.nonzero(clusterAssment[:,0].A==i)[0],:]
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
            sseSplit = sum(splitClustAss[:, 1])
            sseNotSplit = sum(clusterAssment[np.nonzero(clusterAssment[:,0].A!=i)[0], 1])
            print "sseSplit, and notSplit:",sseSplit, sseNotSplit
            if (sseSplit + sseNotSplit) < lowestSSE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
                #更新簇的分配结果
        bestClustAss[np.nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)
        bestClustAss[np.nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
        print 'the bestCentToList is:', bestCentToSplit
        print 'the len of bestClustAss is:',len(bestClustAss)
        centList[bestCentToSplit] = bestNewCents[0, :]
        centList.append(bestNewCents[1,:])
        clusterAssment[np.nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:] = bestClustAss
    #return np.mat(centList), clusterAssment
    return centList, clusterAssment
In [6]: import kMeans

In [7]: reload(kMeans)
Out[7]: <module 'kMeans' from 'kMeans.pyc'>

In [8]: datMat3 = np.mat(kMeans.loadDataSet(r'E:\ML\ML_source_code\mlia\Ch10\testSet2.txt'))

In [10]: centList,myNewAssment = kMeans.biKmeans(datMat3, 3)
In [10]: centList,myNewAssment = kMeans.biKmeans(datMat3, 3)
[[-0.56322601 0.52448821]
[ 4.21102263 -2.41709936]]
......
[[-0.00675605 3.22710297]
[-0.45965615 -2.7782156 ]]
sseSplit, and notSplit: 453.033489581 0.0
the bestCentToList is: 0
the len of bestClustAss is: 60
[[-2.8214654 2.94281516]
[ 3.03994787 1.33537265]]
[[-2.94737575 3.3263781 ]
[ 2.93386365 3.12782785]]
sseSplit, and notSplit: 77.5922493178 29.1572494441
[[-0.77465427 -1.66915815]
[ 0.65907832 -3.42600304]]
[[-1.12616164 -2.30193564]
[ 0.35496167 -3.36033556]]
sseSplit, and notSplit: 12.7532631369 423.876240137
the bestCentToList is: 0
the len of bestClustAss is: 40

查看质心结果:

In [11]: centList
Out[11]:
[matrix([[-2.94737575, 3.3263781 ]]),
matrix([[-0.45965615, -2.7782156 ]]),
matrix([[ 2.93386365, 3.12782785]])]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本程序是在python中完成,基于sklearn.cluster中的k-means聚类包来实现数据聚类,对于里面使用的数据格式如下:(注意更改程序中的相关参数) 138 0 124 1 127 2 129 3 119 4 127 5 124 6 120 7 123 8 147 9 188 10 212 11 229 12 240 13 240 14 241 15 240 16 242 17 174 18 130 19 132 20 119 21 48 22 37 23 49 0 42 1 34 2 26 3 20 4 21 5 23 6 13 7 19 8 18 9 36 10 25 11 20 12 19 13 19 14 5 15 29 16 22 17 13 18 46 19 15 20 8 21 33 22 41 23 69 0 56 1 49 2 40 3 52 4 62 5 54 6 32 7 38 8 44 9 55 10 70 11 74 12 105 13 107 14 56 15 55 16 65 17 100 18 195 19 136 20 87 21 64 22 77 23 61 0 53 1 47 2 33 3 34 4 28 5 41 6 40 7 38 8 33 9 26 10 31 11 31 12 13 13 17 14 17 15 25 16 17 17 17 18 14 19 16 20 17 21 29 22 44 23 37 0 32 1 34 2 26 3 23 4 25 5 25 6 27 7 30 8 25 9 17 10 12 11 12 12 12 13 7 14 6 15 6 16 12 17 12 18 39 19 34 20 32 21 34 22 35 23 33 0 57 1 81 2 77 3 68 4 61 5 60 6 56 7 67 8 102 9 89 10 62 11 57 12 57 13 64 14 62 15 69 16 81 17 77 18 64 19 62 20 79 21 75 22 57 23 73 0 88 1 75 2 70 3 77 4 73 5 72 6 76 7 76 8 74 9 98 10 90 11 90 12 85 13 79 14 79 15 88 16 88 17 81 18 84 19 89 20 79 21 68 22 55 23 63 0 62 1 58 2 58 3 56 4 60 5 56 6 56 7 58 8 56 9 65 10 61 11 60 12 60 13 61 14 65 15 55 16 56 17 61 18 64 19 69 20 83 21 87 22 84 23 41 0 35 1 38 2 45 3 44 4 49 5 55 6 47 7 47 8 29 9 14 10 12 11 4 12 10 13 9 14 7 15 7 16 11 17 12 18 14 19 22 20 29 21 23 22 33 23 34 0 38 1 38 2 37 3 37 4 34 5 24 6 47 7 70 8 41 9 6 10 23 11 4 12 15 13 3 14 28 15 17 16 31 17 39 18 42 19 54 20 47 21 68 22

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值