机器学习实战-K-均值学习笔记

本文介绍了K-均值聚类算法,它能发现k个簇,簇中心由均值计算。阐述了聚类和分类的区别,指出K-均值可能收敛到局部最小值。还提到用SSE度量聚类效果,给出改进方法。为克服K-均值问题,介绍了二分K-均值算法,通过不断划分降低SSE至指定簇数目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

聚类和分类的区别:
最大的区别在于,分类的目标事先已知,而聚类的不一样,其产生的结果和分类相同,但是类别没有预先定义,因此聚类有时候也被称为 无监督分类
代码如下:

from numpy import *
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = map(float, curLine) 			#python2中源代码
        fltLine = list(map(float, curLine))  	#python3中修改后的代码
        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)
        centroids[:, j] = minJ + rangeJ * random.rand(k, 1)
    return centroids

k-均值聚类算法:

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m, 2)))
    # 创建k个初识的质心向量
    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
            # 当聚类结果中的第i个样本发生变化时,布尔类型变为真,继续聚类算法
            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-均值算法收敛到了局部最小值,而非全局最小值(局部最小值指结果还可以但是并非是最好结果,全局最小值可能是最好结果)
一种用于度量聚类效果的指标是SSE(误差平均和),SSE值越小表示数据点越接近它们的质心,聚类效果也越好。
聚类的目标是在保持簇数目不变的情况下提高簇的质量。
改进方法:
1、合并最近的质心 或者 合并两个使得SSE增幅最小的质心
2、合并两个簇然后计算总的SSE,遍历所有可能的两个簇,找到最佳的两个簇。

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

def biKmeans(dataSet, k, distMeas=distEclud):
    m = shape(dataSet)[0]
    # 初始化,簇点都为0
    clusterAssment = mat(zeros((m, 2)))
    # 起始的第一个聚类点,即所有点的质心
    centroid0 = mean(dataSet, axis=0).tolist()[0]  # axis=0:压缩行,对各列求均值,返回一个1*n的矩阵
    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)):
            # 通过数组过滤筛选出属于第i簇的数据集合
            ptsInCurrCluster = dataSet[nonzero(clusterAssment[:, 0].A == i)[0], :]
            # 对于该簇利用二分K-均值算法进行划分,返回划分后的结果及误差
            centroidMat, splitClusterAss = kMeans(ptsInCurrCluster, 2, distMeas)
            # 计算该簇划分为的两个簇的误差平方的和
            sseSplit = sum(splitClusterAss[:, 1])
            # 计算未划分的的簇中数据的误差平方的和
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
            print("sseSplit, and notSplot: ", sseSplit, sseNotSplit)
            # 遍历当前簇后划分后找到最佳的划分簇,更新
            if (sseSplit + sseNotSplit) < lowestSSE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClusterAss.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:', bestCentToSplit)
        print('the len of bestClustAss is: ', len(bestClustAss))
        # 新的簇的分配结果更新,新的质心添加到centList中
        centList[bestCentToSplit] = bestNewCents[0, :]
        centList.append(bestNewCents[1, :])
        clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :] = bestClustAss
    return mat(centList), clusterAssment 		 #python2中的源代码
    return centList, clusterAssment          #python3中修改
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值