机器学习笔记(10)-K-均值(K-means)

K-Means

算法原理

聚类是一种无监督的学习, 它将相似的对象归到一个簇中, 将不相似对象归到不同簇中,相似这一概念取决于所选择的相似度计算方法。
K-Means 是发现给定数据集的 K 个簇的聚类算法, 之所以称之为 K-均值 是因为它可以发现 K 个不同的簇, 且每个簇的中心采用簇中所含值的均值计算而成。
1,簇: 所有数据点点集合,簇中的对象是相似的。
2,质心: 簇中所有点的中心(计算所有点的均值而来)。
3,SSE: Sum of Sqared Error(平方误差和), SSE 值越小,表示越接近它们的质心. 由于对误差取了平方,因此更加注重那么远离中心的点。

算法实现

预处理

from numpy import *
def loadDataset(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = map(float, curLine)  # 注意区分字符串与float
        dataMat.append(fltLine)
    return dataMat

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] = minJ + rangeJ * random.rand(k, 1)
    return centroids

datMat = mat(loadDataset('testSet.txt'))
print(randCent(datMat,2)) # [[ 2.09164585  0.14506447][-0.72461871 -0.75427027]]

k-means 聚类算法
1,该算法会创建k个质心,然后将每个点分配到最近的质心,再重新计算质心。
2,这个过程重复数次,直到数据点的簇分配结果不再改变位置。
3,运行结果(多次运行结果可能会不一样,原因为随机质心的影响,但总的结果是对的, 因为数据足够相似,也可能会陷入局部最小值(局部最优的结果,但不是全局最优的结果))

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    '''
    :param distMeas: 距离计算
    :param createCent: 创建初始质心函数
    :return:
        centroids:质心
        clusterAssment:簇分配结果矩阵,一列记录簇索引值,一列记录存储误差【点到簇质心的距离】。
    '''
    m = shape(dataSet)[0]                  # 行数
    clusterAssment = mat(zeros((m, 2)))    # 创建一个与 dataSet 行数一样,但是有两列的矩阵,用来保存簇分配结果
    centroids = createCent(dataSet, k)     # 创建质心,随机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(最小距离)还小,更新 minDist(最小距离)和最小质心的 index(索引)
                    minDist = distJI; minIndex = j
            if clusterAssment[i, 0] != minIndex:    # 簇分配结果改变
                clusterChanged = True    # 簇改变
                clusterAssment[i, :] = minIndex,minDist**2    # 更新簇分配结果为最小质心的 index(索引),minDist(最小距离)的平方
        print centroids
        for cent in range(k): # 更新质心
            ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A==cent)[0]] # 获取该簇中的所有点
            centroids[cent,:] = mean(ptsInClust, axis=0) # 将质心修改为簇中所有点的平均值,mean 就是求平均值的
    return centroids, clusterAssment

对结果进行改进,可以对生成的簇进行后处理,一种方法是讲具有最大SSE值的簇划分成两个簇,另一种是合并最近的质心,或者合并两个使SSE增幅最小的质心。

二分 K-Means

为了克服 KMeans 算法收敛于局部最小值的问题而提出了另一个称之为二分K-均值(bisecting K-Means)的算法。
二分 K-Means 聚类算法伪代码

将所有点看成一个簇
当簇数目小于k 时
对于每一个簇
    计算总误差
    在给定的簇上面进行 KMeans 聚类(k=2)
    计算将该簇一分为二之后的总误差
选择使得误差最小的那个簇进行划分操作

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

def biKMeans(dataSet, k, distMeas=distEclud):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))             # 保存每个数据点的簇分配结果和平方误差
    centroid0 = mean(dataSet, axis=0).tolist()[0]  # 质心初始化为所有数据点的均值
    centList =[centroid0]                          # 初始化只有 1 个质心的 list
    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 进行二分 kMeans 处理
            sseSplit = sum(splitClustAss[:,1]) # 将二分 kMeans 结果中的平方和的距离进行求和
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1]) # 将未参与二分 kMeans 分配结果中的平方和的距离进行求和
            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) # 调用二分 kMeans 的结果,默认簇是 0,1. 当然也可以改成其它的数字
        bestClustAss[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] # 更新原质心 list 中的第 i 个质心为使用二分 kMeans 后 bestNewCents 的第一个质心
        centList.append(bestNewCents[1,:].tolist()[0]) # 添加 bestNewCents 的第二个质心
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss # 重新分配最好簇下的数据(质心)以及SSE
    return mat(centList), clusterAssment
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值