聚类

聚类

无监督学习

机器学习可以分为监督学习和无监督学习,监督学习中每个样本都有属于的已知类别,比如色泽为青绿,甜度高的西瓜为好瓜,而无监督学习则没有已知的类别,只有样本的特征值是已知的,此时就不是分类和回归的任务,聚类成为这些样本的一种学习

聚类

聚类试图将数据集中的样本划分为若干个不相交的子集,每个子集称为一个簇,通过这样的划分,每个簇可能对于于一些潜在的类别

形式化的说,假定样本集 D D D={ x 1 , x 2 ⋯   , x m x_1,x_2\cdots,x_m x1,x2,xm}包含m个无标记样本,每个样本 x i = ( x i 1 , ⋯   , x i n ) x_i=(x_{i1},\cdots,x_{in}) xi=(xi1,,xin)是一个n维特征向量,则聚类算法将样本集 D D D划分为k个不相交的簇{ C l ∣ l = 1 , 2 , ⋯   , k C_l|l=1,2,\cdots,k Cll=1,2,,k},其中 C l ∩ C i = ∅   ( l ≠ i ) C_l\cap Ci=\emptyset \space(l\neq i) ClCi= (l=i),且 D = ∪ l = 1 k C l D=\cup_{l=1}^kC_l D=l=1kCl

下面介绍聚类的几种基本类型

  • k均值算法:

    1. 在D中随机选择k个样本作为初始均值向量{ u 1 , u 2 , ⋯   , u k u_1,u_2,\cdots,u_k u1,u2,,uk}

    2. 计算每个样本 x j x_j xj与各个均值向量 u i ( 1 ≤ i ≤ k ) u_i(1\leq i \leq k) ui(1ik)的距离(无说明为欧式距离): d i j = ∣ ∣ x j − u i ∣ ∣ 2 d_{ij}=||x_j-u_i||_2 dij=xjui2

    3. 选出最短的距离,并将该样本并入向量 u i u_i ui确定的簇

    4. 当所有样本的计算一次后,对于每个簇,更新其新的均值向量

      u i , = 1 ∣ C l ∣ ∑ x ∈ C l x u_i^,=\frac{1}{|C_l|}\sum _{x\in C_l}x ui,=Cl1xClx

    5. 若有一个更新后的均值向量与原来的均值向量不同,则回到步骤2

    6. 若所有均值向量稳定后,则得到每个均值向量所确定的簇{ C 1 , C 2 , ⋯   , C k C_1,C_2,\cdots,C_k C1,C2,,Ck},对于每个簇当中的样本 x x x x x x与当前簇的均值向量距离最小

  • 二分k均值算法:(优化版k均值算法)

    1. 算出所有样本的均值向量,将所有样本初始归为一个簇

    2. 选出一个合适的均值向量,其对应的簇使用k均值算法,其中k=2,去掉原来的均值向量,加入两个新的均值向量

      如何选出合适的均值向量二分:选出二分后能使总样本方差减少最多的均值向量

    3. 当现有的均值向量小于k时,重复步骤2

    实现:

    from numpy import *
    import matplotlib.pyplot as plt
    
    def loadDataSet(fileName):
        dataMat = []
        fr = open(fileName)
        for line in fr.readlines():
            curLine = line.strip().split('\t')
            fltLine = map(float,curLine)
            dataMat.append(list(fltLine))
        return mat(dataMat)
    
    def distEclud(vecA,vecB):
        return sqrt(sum(power(vecA-vecB,2)))
    
    def randCenter(dataSet,k):
        n = shape(dataSet)[1] #每个样本是n维特征向量
        centroids = mat(zeros((k,n))) #初始化随机质心
        for j in range(n):
            minJ = min(dataSet[:,j])
            maxJ = max(dataSet[:,j])
            rangeJ = float(maxJ - minJ) #随机质心保证在数据集内部
            centroids[:,j] = minJ + rangeJ * random.rand(k,1) #随机矩阵0~1
        return centroids
    
    def KMeans(dataSet,k,distMeas=distEclud,createCent=randCenter):
        #第一个参数数据集,第二个参数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
            for cent in range(k):   #更新质心的位置
                ptsInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]]
                #取出该簇中所有的样本
                centroids[cent,:] = mean(ptsInClust,axis=0)
                #取均值,mean(x)返回一个实数,mean(x,axis=0)对每列取均值返回1*n矩阵
                #mean(x,axis=1)对每行取均值返回m*1矩阵
        return centroids,clusterAssment
    
    
    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
            #计算方差
        print(centList)
        while (len(centList) < k): #当质心序列小于k时
            lowestSSE = inf #记录最小方差
            for i in range(len(centList)): #遍历质心序列里所有质心,选出应该二分的质心
                pstInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A == i)[0],:] #属于该质心的数据集
                centroidMat,splitClustAss = KMeans(pstInCurrCluster,2,distMeas) #调用k均值算法函数,k=2
                SseSplit = sum(splitClustAss[:,1])  #计算分裂后的方差和
                SseNoSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A != i)[0],1]) #不属于该质心的数据集的方差
                print("SSE_split,SSE_Nosplit:",SseSplit,SseNoSplit)
                if (SseSplit + SseNoSplit) < lowestSSE:#整个数据集方差最小
                    bestCentTosplit = i     #记录分裂的质心下标
                    bestNewCents = centroidMat  #分裂后的两个质心
                    bestClusterAss = splitClustAss.copy()   #分裂后数据集的信息[属于哪个簇(只有0 1 两个值)因为k=2,方差]
                    lowestSSE = SseSplit +SseNoSplit            
                    print(lowestSSE)
            print(bestCentTosplit)
            bestClusterAss[nonzero(bestClusterAss[:,0].A == 1)[0],0] = \   
                len(centList) #属于分裂后0簇的数据集实际上属于len(centList)簇,更新其簇号
            bestClusterAss[nonzero(bestClusterAss[:,0].A == 0)[0],0] = \
                bestCentTosplit #分裂后1簇的数据集实际上属于原来分裂的那个簇的簇号
            centList[bestCentTosplit] = bestNewCents[0,:].tolist()[0]   #将分裂后的两个新质心添加到质心序列中
            centList.append(bestNewCents[1,:].tolist()[0])          #值得注意的是分裂的那个质心应该被覆盖掉
            clusterAssment[nonzero(clusterAssment[:,0].A == \
                bestCentTosplit)[0],:] = bestClusterAss #更新最终的样本信息
            print(centList)
        return mat(centList),clusterAssment
    
    
    
    def main():
        dataSet = loadDataSet('1.txt')
        k=3
        centroids,clusterAssment = biKMeans(dataSet,k)
        #centroids,clusterAssment = KMeans(dataSet,k)
        Color = ['b','g','r','y','black']
        for cent in range(k):
            subSet = dataSet[nonzero(clusterAssment[:,0] == cent)[0]]
            plt.scatter(subSet[:,0].tolist(),subSet[:,1].tolist(),color=Color[cent])
        plt.scatter(centroids[:,0].tolist(),centroids[:,1].tolist(),marker='+')
        plt.show()
    
    
    if __name__ == '__main__':
        main()
    

    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值