《机器学习实战》第10章学习笔记(k-Means)

一、聚类

1.1聚类任务

聚类试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇”(cluster)。

1.2 性能度量

聚类性能度量亦称聚类“有效性指标”(validity index)。与监督学习中的性能度量作用类似,对于聚类结果,我们需要通过某种性能度量来评估其好坏;另一方面,若明确了最终要使用的性能度量,则可直接将其作为聚类过程的优化目标,从而更好地得到符合要求的聚类结果。

1.2.1 常用的外部指标:

1.2.2 常用内部指标


1.3  距离计算

                     


二、K-Means算法

2.1 定义

K-Means算法可以发现k个不同的簇, 且每个簇的中心采用簇中所含值的均值计算而成。

2.2 K-Means算法过程

2.3 实现代码

# -*- coding: utf-8 -*-
"""
Created on Thu May 10 10:12:53 2018

@author: lizihua
"""
from numpy import *
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))
        dataMat.append(fltLine)
    return dataMat

#计算欧式距离
def distEclud(vecA,vecB):
    return sqrt(sum(power(vecA-vecB,2)))

#随机选取k个簇的质心
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)
        #random.rand(k,1)随机产生k行1列的array,数组的值的范围:[0,1)
        #min+(max-min)*(0,1)之间的数,保证了质心在数据集边界之内
        centroids[:,j] = minJ + rangeJ*random.rand(k,1)
    return centroids

#K-means聚类算法
def kMeans(dataSet,k,distM = distEclud,createCent = randCent):
    m = shape(dataSet)[0]
    #创建矩阵来存储每个点的簇的分配结果,第一列记录簇索引值,第二列存储误差(点到簇质心的距离)
    #且默认所有点的簇都为0
    clusterAssment = mat(zeros((m,2)))
    #随机选取k个簇质心
    centroids = createCent(dataSet,k)
    #标志变量clusterChanged,用以标志簇分配结果是否发生变化
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        #对于每一个点,计算每个点与k个簇质心的距离,并每个点距离k个簇质心中的最小距离的那个簇
        for i in range(m):
            minDist = inf
            minIndex = -1
            for j in range(k):
                distJI = distM(centroids[j,:],dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI ; minIndex = j
            #如果簇索引发生变化,则,标志变量clusterChanged为True
            if clusterAssment[i,0] != minIndex:
                clusterChanged = True
            #反之,则将离该点最近的簇索引和距离的平方存储到clusterAssment
            clusterAssment[i,:] = minIndex,minDist**2
        print("centroids:\n",centroids)
        #遍历所有簇质心
        for cent in range(k):
            #通过数组过滤来获得给定簇的所有点
            pstInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]]
            #计算所有点的均值
            centroids[cent,:] = mean(pstInClust,axis = 0)
    #返回类质心和分配结果
    return centroids,clusterAssment

def kMeansPlot(dataMat,centroids,clusterAssment):
    k=len(centroids)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(centroids[:,0].tolist(),centroids[:,1].tolist(),marker='+',c='r')
    markers=['o','s','v','*'];colors=['blue','green','yellow','red']
    for i in range(k):
        data_class=dataMat[nonzero(clusterAssment[:,0].A == i)[0]]
        ax.scatter(data_class[:,0].tolist(),data_class[:,1].tolist(),marker=markers[i],c=colors[i])
    plt.show()
    
#测试
if __name__ =="__main__":
    dataMat = mat(loadDataSet('testSet.txt'))
    print("簇质心:\n",randCent(dataMat,2))
    print("距离:\n",distEclud(dataMat[0],dataMat[1]))  #距离: 5.18463281668
    myCentroids, clustAssing = kMeans(dataMat,4)
    print("类质心:\n",myCentroids)
    print("点分配结果:\n",clustAssing)
    kMeansPlot(dataMat,myCentroids, clustAssing)

2.4 结果显示

三、二分K-Means算法

3.1 实现方法

二分K-Means算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE(Sum of Squared Error,误差平方和)的值。上述基于SSE的划分过程不断重复,直到得到用户指定的簇数目为止。


另一种做法是:选择SSE最大的簇进行划分,直到簇数目达到用户指定的数目为止。

3.2 实现代码

#二分K-Means算法
def biKmeans(dataSet, k, distM = distEclud):
    m = shape(dataSet)[0]
    #创建矩阵来存储每个点的簇的分配结果,第一列记录簇索引值,第二列存储误差(点到簇质心的距离)
    clusterAssment = mat(zeros((m,2)))
    #计算整个数据集的每个特征的质心,centroid0:1*n列表
    centroid0 = mean(dataSet,axis = 0).tolist()[0]
    #用centList列表保存所有质心
    centList = [centroid0]
    #遍历所有点,计算每个点到质心的误差值,并保存
    for j in range(m):
        clusterAssment[j,1] = distM(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,distM)
            sseSplit = sum(splitClustAss[:,1])
            #剩余数据集的误差
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A != i)[0],1])
            print("sseSplit,and notSplit:",sseSplit,sseNotSplit)
            #若该簇划分后的误差和剩余数据的误差之和作为本次最终误差,
            #若本次划分的SSE值最小,则本次划分保存,即执行划分操作
            if (sseSplit + sseNotSplit) <lowestSSE:
                bestCenToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
        #更新簇的分配结果,且新的质点会被添加到centList中
        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)
        bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCenToSplit
        print("the bestCentToSplit is :",bestCenToSplit)
        print("the len of bestClustAss is:",len(bestClustAss))
        centList[bestCenToSplit] = bestNewCents[0,:].tolist()[0]
        centList.append(bestNewCents[1,:].tolist()[0])
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCenToSplit)[0],:] =bestClustAss
    return mat(centList),clusterAssment


def kMeansPlot(dataMat,centroids,clusterAssment):
    k=len(centroids)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(centroids[:,0].tolist(),centroids[:,1].tolist(),marker='+',c='r')
    markers=['o','s','v','*'];colors=['blue','green','yellow','red']
    for i in range(k):
        data_class=dataMat[nonzero(clusterAssment[:,0].A == i)[0]]
        ax.scatter(data_class[:,0].tolist(),data_class[:,1].tolist(),marker=markers[i],c=colors[i])
    plt.show()
    
#测试
if __name__ =="__main__":
    dataMat = mat(loadDataSet('testSet2.txt'))
    centList,myNewAssments = biKmeans(dataMat,3)
    print("centList:\n",centList)
    kMeansPlot(dataMat,centList,myNewAssments)

3.3 结果显示


猜你喜欢

转载自blog.csdn.net/lzh_12345/article/details/80270029