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

  1. K-均值(K-means)算法:它可以发现k个不同的簇,且每个簇的中心采用簇中所含值的均值计算而成。
  2. 聚类与分类最大的不同在于,分类的目标实现已知,而聚类则不一样。因为其产生的结果与分类相同,而只是类别没有预先定义。
  3. 聚类分析试图将相似对象归入同一簇,将不相似对象归到不同簇。相似这一概念取决于所选择的相似度计算方法。

如何影响选民,进行拉票?
首先,收集用户的信息,可以同时收集用户满意或不满意的信息,这是因为任何对用户重要的内容都可能影响用户的投票结果。然后,将这些信息输入到某个聚类算法中。接着,对聚类结果中的每一个簇(最好选择最大簇),精心构造能够吸引该簇玄冥的消息。最后,开展竞选活动并观察上述做法是否有效。

10.1 K-均值聚类算法

  1. 优缺点:
    优点:容易实现
    缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢
  2. 流程:
    随机确定k个初始点作为质心,然后将数据集中的每个点分配到一个簇中,具体来讲,为每个点找距其最近的质心,并将其分配给该质心所对应的簇。这一步完成之后,每个簇的质心更新为该簇所有点的平均值。(KNN计算要归类点到所有点的距离,然后排序选择最近K个点中所属分类)
  3. 实现的伪代码:
    创建k个点作为起始质心(经常是随机选择)
    当任意一个点的簇分配结果发生改变时:
    对数据集中的每个数据点
    对每个质心
    计算质心与数据点之间的距离
    将数据点分配到距其最近的簇
    对每一个簇,计算簇中所有点的均值并将均值作为质心
  4. 最近质心
    进行距离的计算。数据集上K-means算法的性能会受到所选距离计算方法的影响
from numpy import *

#K-means聚类支持函数
def loadDataSet(filename):
    dataMat = []
    fr = open(filename)
    for line in fr.readlines():
        curline = line.strip().split('\t')
        fltline = list(map(float,curline))
        dataMat.append(fltline)
    return dataMat
def distEclud(vecA,vecB):
    """计算两个向量之间的欧几里得距离"""
    return sqrt(sum(power(vecA-vecB,2)))
def randCent(dataSet,k):
    """随机选择簇的质心,确定初始质心"""
    n = shape(dataSet)[1]   #特征数量
    centroids = mat(zeros((k,n)))
    #计算每个特征的质心,进而得到整个数据的质心
    for j in range(n):
        #构建簇质心
        minJ = min(dataSet[:,j])
        rangeJ = float(max(dataSet[:,j]) - minJ)    #最大值与最小值的差值
        centroids[:,j] = minJ + rangeJ*random.rand(k,1) #rand()生成一个或一组服从"0-1"均匀分布的随机样本值
    return centroids
#K-均值聚类算法
def kMeans(dataSet,k,distMeas=distEclud,createCent=randCent):
    """K-means算法
    dataSet:数据集
    k:簇的数量
    distMeas=distEclud:计算距离的函数类型,通过传递引用使函数更具通用性.默认使用欧几里得计算距离
    createCent=randCent:随机选择质心
    return centroids,clusterAssment:质心和相应的误差"""
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))  #簇分配结果矩阵包含两列:一列记录簇索引值,一列存储误差。这里的误差是指当前点到簇质心的距离,后边会使用该误差来评价聚类的效果
    centroids = createCent(dataSet,k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        #对每个点进行归类
        for i in range(m):
            minDist = inf
            minIndex = -1
            #寻得离第i个数据点最近的质心
            for j in range(k):
                distJI = distMeas(centroids[j,:],dataSet[i,:]) 
                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[nonzero(clusterAssment[:,0].A==cent)[0]]
            centroids[cent,:] = mean(ptsInClust,axis=0) #axis=0表示沿矩阵的列方向进行均值计算
    return centroids,clusterAssment

10.2 使用后处理来提高聚类性能

  1. K-均值聚类方法收敛到局部最小,而非全局最小。
  2. 一种用于度量聚类效果的指标是SSE(sum of squared error,误差平方和clusterAssment中的第二列)。SSE值越小表示数据点越接近于它们的质心,聚类效果也越好。因为对误差取了平方,因此更加重视那些远离中心的点。
  3. 聚类的目标是在保持簇数目不变的情况下提高簇的质量。
    可以对误差最大的簇进行分割,然后再对最近的质心进行合并,或合并两个使得SSE增幅最小的质心。

10.3 二分K-均值算法

  1. 过程
    为克服K-均值算法收敛于局部最小值的问题,使用二分K-均值算法。
    该算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE值。上述基于SSE的划分过程不断重复,直到得到用户指定的簇数目为止。
    在这里插入图片描述
伪代码
    将所有点看成一个簇
    当簇数目小于k时
        对于每一个簇
            计算总误差
            在给定的簇上面进行K-均值聚类(k=2)
            计算将该簇一分为二之后的总误差
        选择使得误差最小的那个簇进行划分操作
#二分K-均值聚类算法
def biKmeans(dataSet,k,distMeas=distEclud):
    """二分K-均值聚类算法"""
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))
    #创建一个初始簇
    centroid0 = mean(dataSet,axis=0).tolist()[0]
    centList = [centroid0]  #保存质心
    for j in range(m):
        clusterAssment[j,1] = distMeas(mat(centroid0),dataSet[j,:])**2
    while(len(centList) < k):
        lowestSSE = inf
        #尝试划分每一个簇
        for i in range(len(centList)):  #遍历簇列表
            ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A == i)[0],:]    #获取每个簇中包含的数据集
            centroidMat,splitClustAss = kMeans(ptsInCurrCluster,2,distMeas)
            sseSplit = sum(splitClustAss[:,1])
            sseNotSplit = sum(clusterAssment[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[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)
        bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
        print('the bestCentToSplit is:',len(bestClustAss))
        centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]
        # print("========Numpy.matrix>>>list============")
        # print(bestNewCents[0,:])
        # print(bestNewCents[0,:].tolist())
        # print(bestNewCents[0,:].tolist()[0])
        # print("========Numpy.matrix>>>list============")
        centList.append(bestNewCents[1,:].tolist()[0])
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:] = bestClustAss
    return mat(centList),clusterAssment

if __name__ == '__main__':
    dataMat = mat(loadDataSet('./M_10_Kmeans/testSet.txt'))
    myCentroids,clustAssing = biKmeans(dataMat,4)
    print(myCentroids)

10.4 示例:对地图上的点进行聚类

"""地图点聚类"""

import M_1002_Kmeans as Kmeans
from numpy import *
import matplotlib
import matplotlib.pyplot as plt

def distSLC(vecA,vecB):
    """球面距离计算"""
    a = sin(vecA[0,1]*pi/180) * sin(vecB[0,1]*pi/180)
    b = cos(vecA[0,1]*pi/180) * cos(vecB[0,1]*pi/180) * cos(pi*(vecB[0,0] - vecA[0,0])/180)
    return arccos(a+b)*6371.0
def clusterClubs(numClust=5):
    datList = []
    for line in open('./M_10_Kmeans/places.txt').readlines():
        lineArr = line.split('\t')
        datList.append([float(lineArr[4]),float(lineArr[3])])
    datMat = mat(datList)
    print(datMat)
    myCentroids,clustAssing = Kmeans.biKmeans(datMat,numClust,distMeas=distSLC)
    myCentroids = mat(myCentroids)
    fig = plt.figure()
    rect = [0.1,0.1,0.8,0.8]
    scatterMarkers = ['s','o','^','8','p','d','v','h','>','<']
    axprops = dict(xticks=[],yticks=[])
    ax0 = fig.add_axes(rect,label='ax0',**axprops)
    imgP = plt.imread('./M_10_Kmeans/Portland.png')
    ax0.imshow(imgP)
    ax1 = fig.add_axes(rect,label='ax1',frameon=False)
    for i in range(numClust):
        ptsInCurrCluster = datMat[nonzero(clustAssing[:,0].A == i)[0],:]
        markerStyle = scatterMarkers[i % len(scatterMarkers)]
        ax1.scatter(ptsInCurrCluster[:,0].flatten().A[0],ptsInCurrCluster[:,1].flatten().A[0],marker=markerStyle,s=90)
    ax1.scatter(myCentroids[:,0].flatten().A[0],myCentroids[:,1].flatten().A[0],marker='+',s=300)
    plt.show()
clusterClubs()

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值