原型聚类总结

概述

原型聚类是指聚类结构能通过一组原型刻画,原型是指样本空间中具有代表性的点。通常情况下,算法先对原型进行初始化,然后对原型进行迭代更新求解,下面是几种著名的原型聚类算法。

K均值算法

给定样本集合D,K均值算法针对聚类所得簇划分C,最小化平方误差
E=ki=1xCi||xμi||22
其中 μi=1|Ci|xCix 是簇 Ci 的均值向量。直观来看,这个误差刻画了簇内样本围绕簇均值向量的紧密程度,E值越小则簇内样本相似度越高。
K均值法采用贪心策略,通过迭代近似求解上式,算法首先随机选择k个向量作为初始均值向量,然后是迭代过程,根据均值向量将样本划分到距离最近的均值向量所在的簇中,划分完成之后更新新的均值向量,直到迭代完成。
代码见最后

二分K均值法

K均值法依赖于初始均值向量的选择,导致算法可能收敛到局部最小值,一种改进的方法是二分K均值法,这里的二分不是指算法课程中的二分,而是一分为二的意思。
二分K均值法初始时将所有点看成一个簇,在簇的数量小于K的时候进行迭代,算法的核心是选择一个簇一分为二,这里一分为二的方法还是K均值法,只不过K变成了2。二分K均值依次计算每个簇一分为二后新的总平方误差,选择划分后总体平方误差最小的簇进行划分,代码见最后。

学习向量量化LVQ

与K均值法类似,学习向量量化也是试图找到一组原型向量来刻画聚类结构,但不同的是,LVQ假设数据样本带有类别信息,学习过程利用样本的这些监督信息来辅助聚类,可看作通过聚类来形成类别的子类结构,每个子类对应一个聚类簇。
给定样本集,原型向量个数,各原型向量的类别标记,学习率,
算法首先初始化一组原型向量,可以从该类别中随机选取一个向量作为原型向量。初始化完成之后,开始迭代过程,每次从样本集中选取一个样本 x,y ,将这个样本划分到距离它最近的原型向量 p 所在的簇中,如果这两个向量的类别标记相同,那么更新p为
p=p+η(xp)
若类别标记不相同,更新p为
p=pη(xp)
直观上来看,若类别标记相同,原型向量向x的方向靠拢,否则远离。算法的停止条件是达到最大迭代轮数或者原型向量更新很小。
算法学习得到一组原型向量,对任意样本来说,它将被划分到与其最近的原型向量所代表的簇中,该划分通常称为“Voronoi”划分。
代码见最后

高斯混合聚类

与K均值,LVQ用原型向量刻画聚类结构不同,高斯混合聚类采用概率模型来表达聚类原型。
多元高斯分布的定义:对于n维样本空间中的随即向量 x ,若 x 服从高斯分布,其概率密度函数为
p(x)=1(2π)n/2|Σ|1/2e1/2(xμ)TΣ1(xμ)
可以看到,高斯分布完全由均值向量 μ 和协方差矩阵 Σ 确定,将概率密度函数记为 p(x|μ,Σ)
我们定义高斯混合分布为
pM(x)=ki=1αip(x|μi,Σi)
该分布共由k个混合成分构成,每个混合成分对应一个高斯分布,其中 μi,Σi 是第i个高斯混合成分的参数,而 αi>0 是相应的混合系数。
假设样本的生成过程由高斯分布给出,首先,根据 αi 定义的先验分布选择高斯混合成分,然后根据被选择的混合成分的概率密度函数进行采样。
若训练集D由上述过程生成,令随机变量 zj{1…k} 表示生成样本 xj 的高斯混合成分,其取值未知,显然, zj 的先验概率 P(zj=i)=αi 。根据贝叶斯定理, zj 的后验分布等于
pM(zj=i|xj)=P(zj=i)pM(xj|zj=i)pM(xj)=αipM(xj|μi,Σi)kl=1αlpM(xj|μl,Σl)

将这个后验概率记为 γji
当高斯混合分布已知,即所有参数都已知时,可以计算每个样本由第i个高斯混合成分生成的后验概率,取其中的最大值然后标记样本所属该簇。
因此,从原型聚类的角度来看,高斯混合聚类是采用概率模型对原型进行刻画,簇划分则由原型对应的后验概率确定。

现在的问题就是求解各混合成分的参数,这一步可以用极大似然估计。
对数似然为
LL(D)=ln(mj=1pM(xj))=mj=1ln(ki=1αip(xj|μi,Σi))
然后可以用EM算法进行迭代优化求解,具体见下一篇博客。

K均值法和二分K均值法代码

from numpy import *


#k均值算法


def loadData(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 sqrt(sum(power(vecA-vecB, 2)))


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, n = shape(dataSet)
    centroids = createCent(dataSet, k)
    # 每个样本被分到了到各类,以及到这个类质心的距离
    clusterAssign = mat(-1*ones((m, 2)))
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDistance = inf; minIndex = -1
            for j in range(k):
                distanceIj = distMeas(dataSet[i, :], centroids[j, :])
                if distanceIj < minDistance:
                    minDistance = distanceIj
                    minIndex = j
            if minIndex != clusterAssign[i, 0]:
                clusterChanged = True
            clusterAssign[i, :] = minIndex, minDistance**2
        for cent in range(k):
            pointsInCluster = dataSet[nonzero(clusterAssign[:, 0] == cent)[0], :]
            centroids[cent, :] = mean(pointsInCluster, axis=0)
    return centroids, clusterAssign



# 二分k均值法
def biKmeans(dataSet, k, distMeas=distEclud):
    m, n = shape(dataSet)
    clusterAssign = mat(zeros((m, 2)))
    centroid0 =  mean(dataSet, axis=0).tolist()[0]
    centList = [centroid0], numCents = 1
    for j in range(m):
        clusterAssign[j, 1] = distMeas(dataSet[j, :], mat(centroid0))**2
    while numCents < k:
        minSSE = inf
        bestCentToSplit = -1
        bestNewCents = centList
        bestNewAssign = clusterAssign
        for i in range(numCents):
            pointsInCluster = dataSet[nonzeros(clusterAssign[:, 0] == i)[0], :]
            splitCent, splitClusterAssign = kMeans(pointsInCluster, 2)
            splitSSE = sum(splitClusterAssign[:, 1])
            noSplitSSE = sum(clusterAssign[nonzeros(cluster[:, 0] != i)[0], 1])
            currentSSE = splitSSE + noSplitSSE
            if currentSSE < minSSE:
                minSSE = currentSSE
                bestCentToSplit = i
                bestNewCents = splitCent
                bestNewAssign = splitClusterAssign

        # 更新簇的分配结果,将新得到的两个质点一个序号设为bestCentsToSplit,另一个序号设为numCents
        centList[bestCentToSplit] = bestNewCents[0, :]
        centList.append(bestNewCents[1, :])
        bestNewAssign[nonzeros(bestNewAssign[:, 0] == 0)[0], 0] = bestCentToSplit
        bestNewAssign[nonzeros(bestNewAssign[:, 0] == 1)[0], 0] = numCents
        clusterAssign[nonzeros(clusterAssign[:, 0] == bestCentToSplit)[0], :] = bestNewAssign
        numCents += 1
    return mat(centList), clusterAssign


dataMat = mat(loadData('testSet2.txt'))
centroids, clusterAssign = kMeans(dataMat, 3)
print(centroids)

LVQ算法代码

from numpy import *


# LVQ算法


def loadData(filename):
    dataSet = []
    fr = open(filename)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = list(map(float, curLine))
        dataSet.append(fltLine)
    return dataSet


def randomSelect(dataMat):
    m = shape(dataMat)[0]
    return dataMat[random.randint(0, m-1), :]


def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA-vecB, 2)))


def LVQ(dataMat, q, labels, maxIter=500, eta=0.1, distMeas = distEclud):
    m, n = shape(dataMat)
    centroids = mat(zeros((q, n)))
    # 初始化原型向量
    for i in range(q):
        curClassDataMat = dataMat[nonzero(dataMat[:, -1] == labels[i])[0], :]
        centroids[i, :] = randomSelect(curClassDataMat)

    for i in range(maxIter):
        # 随机选择一个样本
        sample = randomSelect(dataMat)
        bestIndex = -1
        minDistance = inf
        # 选择距离这个样本最近的原型向量
        for j in range(q):
            curDistance = distMeas(sample[0, :n-1], centroids[j, :n-1])
            if curDistance < minDistance:
                minDistance = curDistance
                bestIndex = j
        # 若样本和这个原型向量类别相同
        if sample[0, -1] == centroids[bestIndex, -1]:
            centroids[bestIndex, :n-1] += eta*(sample[0, :n-1]-centroids[bestIndex, :n-1])
        # 类别不同
        else:
            centroids[bestIndex, :n-1] -= eta*(sample[0, :n-1]-centroids[bestIndex, :n-1])
    return centroids


dataMat = mat(loadData('waterMelon4.txt'))
centroids = LVQ(dataMat, 5, [1, 2, 2, 1, 1], 1000)
print(centroids)

    原文作者:聚类算法
    原文地址: https://blog.csdn.net/u014664226/article/details/51944092
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞