机器学习实战笔记(4)

本文详细介绍了K-均值聚类算法的原理、实现步骤以及可能遇到的局部最优问题,并提供了一个简单的Python实现。随后,为解决局部最优问题,介绍了二分K-均值算法,通过不断划分簇来寻找全局最小误差。二分K-均值算法能有效避免局部最小,提高聚类效果。
摘要由CSDN通过智能技术生成

机器学习实战笔记(4)

一、K-均值聚类算法

1、算法介绍

​ k均值聚类算法(k-means clustering algorithm)是一种理解和实现都比较简单的算法,属于无监督学习,主要实现聚类(Clustering),即将相似的数据归到同一簇,簇内对象越相似,聚类的效果越好。

​ k均值聚类算法是一种迭代求解的聚类分析算法,其步骤是,预将数据分为K组,则随机选取K个对象作为初始的聚类中心,然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。每次分配结束后,更新每个聚类中心。这个过程将不断重复直到满足某个终止条件:终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有聚类中心再发生变化,误差平方和局部最小。


2、算法实现
(1)辅助函数
def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA - vecB, 2)))   # 两数据点之间的距离计算

def randCent(dataSet, k):
    '''随机初始化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] = mat(minJ + rangeJ * random.rand(k,1))
    return centroids
(2)K-均值聚类算法主体

​ 输入参数有四个,分别为数据集、簇个数k、距离函数和初始化中心,其中数据集和k一定要指定。输出则为最终的k个簇中心和每个数据的簇分类情况。

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    '''K_均值算法主体部分'''
    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
            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  # 更新该点分配矩阵
        print(centroids)
        for cent in range(k):  # 更新每个簇的中心点
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]  # 获取所有属于该簇的数据点
            centroids[cent,:] = mean(ptsInClust, axis=0)  # 更新均值
    return centroids, clusterAssment

用两组简单的数据对该算法进行测试,结果如下图所示:

第一组:
在这里插入图片描述

第二组:
在这里插入图片描述

从两组结果图很容易看出,对第一组数据聚类情况看着还不错。而由于K均值算法中每次初始化的中心点均为随机生成的点,偶尔可能陷入局部最优解,而非全局最优解。具体情况类似与第二组数据的聚类情况,从图中很明显可以看出聚类结果并不理想。

对于类似情况可对该算法重复多次,避免陷入某些局部最优解,或者改进K-均值算法,如接下来介绍的二分K-均值算法。

二、二分K-均值算法

​ 为克服K-均值算法收敛于局部最小值的问题,有人提出了另个称为二分K-均值(bisectingK-means)的算法。该算法首先将所有点作为个簇, 然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪个簇进行划分取决于对其划分是否可以最大程度降低SSE的值(Sum of Squared Error ,误差平方和,一种度量聚类效果的指标)。上述基于SSE的划分过程不断重复,直到得到用户指定的簇数目为止。另一种做法则是选择SSE最大的簇进行划分,直到簇数目达到用户指定的数目为止。

(1)二分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):  # 当前簇个数尚未到达k时
        lowestSSE = inf
        for i in range(len(centList)):  # 尝试划分每个簇,确定用于下次划分最好的簇
            ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]  # 获取所有第i个簇的数据
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)  # 对第i个簇划分
            sseSplit = sum(splitClustAss[:,1])  # 划分后簇的SSE
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])  # 未进行划分的其他簇的SSE
            print("sseSplit, and notSplit: ",sseSplit,sseNotSplit)
            if (sseSplit + sseNotSplit) < lowestSSE:  # 保留划分后总体SSE最小的
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)  # 更新簇的分配结果,(0,1 ——> x,i)
        bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
        centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]  # 更新存储簇中心的列表
        centList.append(bestNewCents[1,:].tolist()[0])
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss  # 更新距离
    return mat(centList), clusterAssment
  • nonzero函数是numpy中用于得到数组array中非零元素的位置(数组索引)的函数。它的返回值是一个长度为a.ndim(数组a的轴数)的元组,元组的每个元素都是一个整数数组,其值为非零元素的下标在对应轴上的值。

    如:dataSet[nonzero(clusterAssment[:,0].Ai )[0],:] 中nonzero(clusterAssment[:,0].Ai[0]返回的即为满足clusterAssment[:,0].A==i条件(簇类别为i)的数据的二维索引元组。

二分K均值算法最终会收敛到全局最小值,对原始K均值聚类算法案例中出现局部最小值的数据,再次使用此算法进行聚类测试,结果如下图所示:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值