《机器学习实战》学习笔记第十章-K-means

目录

聚类算法

K-means均值算法

工作流程

缺陷

二分k-均值算法

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


聚类算法

聚类与分类最大的不同在于,分类的目标事先已知,而聚类则不知道。聚类是一种无监督学习,事先并不知道要寻找的内容,没有目标变量,只是将相似的对象归到一个簇中。簇内的对象越相似,聚类的效果越好。

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

优点:容易实现

缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢。

适用数据类型:数值型数据

一般流程:

收集数据:任意方法

准备数据:需要数值型数据来计算距离,也可以将标称型数据映射为二值型数据再计算距离

分析数据:任意方法

训练算法:不适用于无监督学习

测试算法:应用聚类算法、观察结果。可以使用量化的误差指标如误差平方和来评价算法的结果。

使用算法:可以用于所希望的任何应用。通常情况下,簇质心可以代表整个簇的数据来做出决策。

K-means均值算法

工作流程

首先,随机确定k个初始点作为质心,然后将数据集中的每个点分配到一个簇中,具体来说,为每个点找距离其最近的质心,并将其分配给该质心所对应的簇。这一步完成之后,每个簇的质心更新为该簇所有点的平均值。

《机器学习实战》学习笔记第十章-K-means_第1张图片

 流程图来自:

机器学习实战 第十章 K-均值聚类算法 Python3代码_Jeremy_Ren的博客-CSDN博客

#生成数据集
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 np.sqrt(np.sum(np.power((VecA-VecB),2)))
 
#随机生成k个质心的集合
def randCent(dataset,k):
    n = dataset.shape[1]
    centroids = np.mat(np.zeros((k,n))) #k个簇
    for j in range(n):  #生成簇 不过要在所有数据的区域内
        rangeJ = float(max(dataset[:,j]) - min(dataset[:,j])) #最大值与最小值的差距
        centroids[:,j] =  rangeJ * np.random.rand(k,1) + min(dataset[:,j])
    return centroids
datMat = np.mat(LoadDataSet('Ch10/testSet.txt'))

#创建k个质心,将每个点分配到最近的质心,再重新计算质心
def kMeans(dataMat,k,distMeans=distEclud,createCent=randCent):
    m = np.shape(dataMat)[0]
    centroids = createCent(dataMat,k)
    clusterAssment = np.mat(np.zeros((m,2))) #0列记录簇索引值,1列存储误差
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = float('inf'); minIndex = -1
            for j in range(k):
                distJI = distMeans(dataMat[i],centroids[j]) #计算每个点到质心的距离
                if distJI < minDist:
                    minDist= distJI
                    minIndex = j
            if clusterAssment[i,0]!=minIndex:
                clusterChanged=True
            clusterAssment[i,:] = minIndex,minDist**2#每个点找到了最近质心和与质心之间的距离
        print(centroids)
        for cent in range(k):
            ptsInClust = dataMat[np.nonzero(clusterAssment[:,0]==cent)[0]] #找到同一个簇中的所有点
            centroids[cent,:] = np.mean(ptsInClust,axis=0) #重新计算簇的质心
    return centroids,clusterAssment #返回质心和点的分配结果

缺陷

k是用户自定的参数,难以准确确定。

生成簇可能不是最优簇,因为最开始的质心是随机出来的,只能保证从这个起点出发得到的距离最小值(局部最小值),而无法保证是全局的距离最小值。

可以采用SSE(Sum of Squared Error,误差平方和)来度量聚类的效果。SSE值越小表示数据点越接近于它们的质心,聚类效果也越好。因为对误差取了平方,因此更重视那些远离中心的点。

一种方法是可以将具有最大SSE的簇划分成2个簇,然后再将某两个离得近的簇合并。

二分k-均值算法

为克服k均值算法收敛于局部最小值的问题,有人提出了二分k均值算法。该算法首先将所有点作为一个簇,然后将该簇一分为二,之后选择其中一个簇继续划分,选择的依据是划分之后可以最大程度降低SSE的值。上述代码不断重复,直到得到用户指定的簇数目为止。

def biKmeans(dataSet, k, distMeas=distEclud):
    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 = float('inf')
        for i in range(len(centList)):
            ptsInCurrCluster =dataSet[np.nonzero(clusterAssment[:,0].A==i)[0],:] #每个簇看成一个小数据集,
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2 , distMeas) #输入到kmeans算法中,计算k=2的质心簇和误差值

            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 bestCentToSplit is: ',bestCentToSplit)
        print('the len of bestClustAss is: ', len(bestClustAss))
        centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]
        centList.append(bestNewCents[1,:].tolist()[0])
        clusterAssment[np.nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss
    return np.mat(centList), clusterAssment

datMat = np.mat(LoadDataSet('Ch10/testSet2.txt'))
centList,mynewassments = biKmeans(datMat,3)
print(centList)

输出质心:

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

从网站扒经纬度的过程就不做了,书中给的文件也有palce.txt,最后两列分别为经纬度数据,直接用就好了。

def distSLC(vecA, vecB):#根据经纬度算地球表面距离
    a = np.sin(vecA[0,1]*math.pi/180) * np.sin(vecB[0,1]*math.pi/180)
    b = np.cos(vecA[0,1]*math.pi/180) * np.cos(vecB[0,1]*math.pi/180) * np.cos(math.pi * (vecB[0,0]-vecA[0,0]) /180)
    return np.arccos(a + b)*6371.0 

def clusterClubs(numClust=5):
    datList = []
    for line in open('Ch10/places.txt').readlines():
        lineArr = line.split('\t')
        datList.append([float(lineArr[4]), float(lineArr[3])])
    datMat = np.mat(datList)
    myCentroids, clustAssing = biKmeans(datMat, numClust, distMeas=distSLC)
    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('Ch10/Portland.png')
    ax0.imshow(imgP)
    ax1=fig.add_axes(rect, label='ax1', frameon=False)
    for i in range(numClust):
        ptsInCurrCluster = datMat[np.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)
    print(myCentroids)
    ax1.scatter(myCentroids[:,0].flatten().A[0], myCentroids[:,1].flatten().A[0], marker='+', s=300)
    plt.show()

clusterClubs()

5个簇时:

《机器学习实战》学习笔记第十章-K-means_第2张图片

 4个时:

《机器学习实战》学习笔记第十章-K-means_第3张图片

 3个时:

《机器学习实战》学习笔记第十章-K-means_第4张图片

 

你可能感兴趣的:(机器学习,学习,kmeans)