2019-12-29

K-Means聚类算法

1、原理

第一步:选K个初始聚类中心,z1(1),z2(1),…,zK(1),其中括号内的序号为寻找聚类中心的迭代运算的次序号。聚类中心的向量值可任意设定,例如可选开始的K个模式样本的向量值作为初始聚类中心。

第二步:逐个将需分类的模式样本{x}按最小距离准则分配给K个聚类中心中的某一个zj(1)。

假设i=j时, ,则 ,其中k为迭代运算的次序号,第一次迭代k=1,Sj表示第j个聚类,其聚类中心为zj。

第三步:计算各个聚类中心的新的向量值,zj(k+1),j=1,2,…,K

求各聚类域中所包含样本的均值向量:

其中Nj为第j个聚类域Sj中所包含的样本个数。以均值向量作为新的聚类中心,可使如下聚类准则函数最小:

在这一步中要分别计算K个聚类中的样本均值向量,所以称之为K-均值算法。

第四步:若 ,j=1,2,…,K,则返回第二步,将模式样本逐个重新分类,重复迭代运算;

若 ,j=1,2,…,K,则算法收敛,计算结束。

2、程序实现

def biKmeans(dataSet, k, distMeas=distEclud):

    m = shape(dataSet)[0]

    # 这里第一列为类别,第二列为SSE

    clusterAssment = mat(zeros((m,2)))

    # 看成一个簇是的质心

    centroid0 = mean(dataSet, axis=0).tolist()[0]

    centList =[centroid0] #create a list with one centroid

    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)):

            # 得到属于该质心的数据

            ptsInCurrCluster =\ dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]

            # 对该质心划分成两类

            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)

            # 计算该簇划分后的SSE

            sseSplit = sum(splitClustAss[:,1])

            # 没有参与划分的簇的SSE

            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])

            print "sseSplit, and notSplit: ",sseSplit,sseNotSplit

            # 寻找最小的SSE进行划分

            # 即对哪一个簇进行划分后SSE最小

            if (sseSplit + sseNotSplit) < lowestSSE:

                bestCentToSplit = i

                bestNewCents = centroidMat

                bestClustAss = splitClustAss.copy()

                lowestSSE = sseSplit + sseNotSplit

        # 较难理解的部分

        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #change 1 to 3,4, or whatever

        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]#replace a centroid with two best centroids

        centList.append(bestNewCents[1,:].tolist()[0])

        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss#reassign new clusters, and SSE

    return mat(centList), clusterAssment

3、结论1. K值需要预先给定,属于预先知识,很多情况下K值的估计是非常困难的,对于像计算全部微信用户的交往圈这样的场景就完全的没办法用K-Means进行。对于可以确定K值不会太大但不明确精确的K值的场景,可以进行迭代运算,然后找出Cost Function最小时所对应的K值,这个值往往能较好的描述有多少个簇类。

2. K-Means算法对初始选取的聚类中心点是敏感的,不同的随机种子点得到的聚类结果完全不同

3. K均值算法并不是很所有的数据类型。它不能处理非球形簇、不同尺寸和不同密度的簇,银冠指定足够大的簇的个数是他通常可以发现纯子簇。

4. 对离群点的数据进行聚类时,K均值也有问题,这种情况下,离群点检测和删除有很大的帮助。


参考文献:https://blog.csdn.net/taoyanqi8932/article/details/53727841

你可能感兴趣的:(2019-12-29)