KMeans、LVQ、GaussianMixture几种聚类方法的Python实现以及标签映射(Kuhn-Munkres匈牙利算法)问题的解决(详细并附完整代码)

一、概述

KMeans、LVQ、GaussianMixture这几种方法都是非常经典的聚类算法,在机器学习中具有重要的地位。最近由于在做机器学的小作业,要求实现这几种方法,由于各种原因,不想去亲自挨个实现每种算法,发现Python中有个sklearn的库,里面包含了大量的机器学习相关的方法,其中就包括了Kmeans和GaussianMixture这两种,所以我只实现了一个LVQ方法。
值得注意的是,在对每个算法实现之后,要对每个算法进行精确度的评价,这时候就用到了ACC和NMI这两个评价标准(具体是什么见后文),在评价时就需要用到标签的信息,但是在对数据进行聚类时,标签分配的规律与真实标签并不一定一致,所以就需要利用Kuhn-Munkres算法(匈牙利算法)来对预测的标签重排,而Python中也集成了Kuhn-Munkres算法,所以在一开始用MATLAB,后来用C++,再最后选择了用Python来实现。

二、思想解释与细节描述

由于KMeans和GaussianMixture方法在Python中都有集成,Kmeans较为容易理解,GaussianMixture我也没看,所以这两个方法在这里不细说。

2.1 LVQ算法实现

其实LVQ方法的思想也比较简单,就是根据样本已有标签让对应的原型向量向该样本逐渐靠近,最终达到收敛,所以说这是一种有监督的学习方法。首先,需要初始化一组原型向量 { p 1 , p 2 , ⋯   , p q } \{p_1,p_2,\cdots,p_q\} {p1,p2,,pq},我们需要分多少类,就初始化多少个原型向量,如果需要分k类,则q=k。然后从样本中随机抽取样本,计算样本与每一个原型向量的距离,找到距离最近的一个原型向量,然后判断这个原型向量的标签与随机抽取的样本的标签是否一致,如果一致,则该原型向量向该样本靠拢,否则远离该样本。一直重复这些操作,直到满足停止条件,一般可以设置为达到了某个迭代次数停止。算法流程如下:

首先,利用利用Python的numpy来定义一个用于计算样本到原型向量之间的距离函数

def dis(x,y):
    return np.sqrt(np.sum(np.power(x[:]-y[:],2)))

这个函数的意思就是将向量x与向量y中的每个分量分别相减,然后平方求和再开方,这是numpy的一个非常便捷的功能,感叹一下numpy的强大。
然后初始化一组原型向量

    for i in range(k):
        q[i] = random.choice(data[labels[:] == i+1])

这里又得感叹一下Python的强大,这一行代码就实现了对某一个标签的所有数据中随机抽取一个样本作为其中一个原型向量的功能,k是样本标签种类的数量,这一行代码如果用C++实现的话,怎么也得十几二十行吧。首先需要找到每种标签对应的所有数据有哪些,然后对每个标签对应的每组数据再进行随机抽取,一共随机抽取了k个样本作为原型向量,这里的i+1就是对应的原型向量的标签。
然后就是在所有数据中随机抽取一个样本,

x = random.randint(0,len(data)-1)

这里抽取的是样本的索引(在数据中的排列序号),然后找到距离该样本的最近原型向量:

        for i in range(k):
            distance=dis(data[x],q[i])
            if distance<min_dis:
                min_dis=distance
                index=i

再判断该样本标签与最近的原型向量的标签是否一致:

        if labels[x] == index+1:
            q[index] = q[index] + lr * (data[x] - q[index])
        else:
            q[index] = q[index] - lr * (data[x] - q[index])

这里的lr为学习率, 0 < l r < 1 0<lr<1 0<lr<1
这样一直重复以上操作,直到满足停止条件,然后返回最终的一组原型向量 { q 1 , q 2 , ⋯   , q k } \{q_1,q_2,\cdots,q_k\} {q1,q2,,qk}即可。
但是,一般来说,利用LVQ算法分类后,具有使用价值的不仅仅是一组最终的原型向量,肯定还需要利用到最后每个数据经过分类之后的标签,正好在本次小作业中还需要根据标签来评价算法精确度,所以,最后需要根据每个数据与每个原型向量的距离来确定每个数据属于哪个类(即打上什么标签),如下:

newLable = []
    for item in data:
        min_dis = np.inf
        index = 0
        for i in range(k):
            distance = dis(item, q[i])
            if distance < min_dis:
                min_dis = distance
                index = i
        newLable.append(index)

2.2 利用Kuhn-Munkres算法映射标签

假设 { 1 , 1 , 1 , 2 , 2 , 3 , 4 , 4 , ⋯   , 7 , 7 , 7 , 9 , 9 } \{1,1,1,2,2,3,4,4,\cdots,7,7,7,9,9\} {1,1,1,2,2,3,4,4,,7,7,7,9,9}为真实的数据标签,而利用上述算法对数据聚类之后的标签可能是 { 3 , 3 , 3 , 1 , 1 , 4 , 6 , 6 , ⋯   , 9 , 9 , 7 , 7 , 7 } \{3,3,3,1,1,4,6,6,\cdots,9,9,7,7,7\} {3,3,3,1,1,4,6,6,,9,9,7,7,7},就算在最理想的状态下,把所有该在一个簇中的数据都聚类到一个簇中,但是由于标签的规则不一致,所以每个簇给定的标号不一样。这时候就需要利用到Kuhn-Munkres算法来对标签进行重新标号。
重新标号的主要思路就是,将聚类之后的每一种标签(称为预测标签)与每一种真实标签一一对比,计算出所有标签组合(i,j)情况的数据重合数量,这样就会形成一个“代价矩阵”,基于这个“代价矩阵”,就可以利用Kuhn-Munkres算法来找出代价最低的分配方式,也就是预测标签与真实标签之间的映射关系。
例如,现在具有以下数据(所有数据及代码下载方式见下文),
在这里插入图片描述
对于数据lung.mat,具有5种标签,分别为 { 1 , 2 , 3 , 4 , 5 } \{1,2,3,4,5\} {1,2,3,4,5},所以预测标签与真实标签组合之后就会形成一个 5 × 5 5\times5 5×5的矩阵,矩阵中的每个值就代表该标签组合情况下数据的重合数量,例如
在这里插入图片描述
其中第1行第2列数字为38,就代表真实标签为1的数据与预测标签为2的数据重合的数量有38个,以此类推,计算此矩阵代码如下:

    for i in range(nClass1):
        ind_cla1 = L1 == Label1[i]
        ind_cla1 = ind_cla1.astype(float)
        for j in range(nClass2):
            ind_cla2 = L2 == Label2[j]
            ind_cla2 = ind_cla2.astype(float)
            G[i, j] = np.sum(ind_cla2*ind_cla1)

这里的代码如果对于Python不熟悉的话可能是比较难以理解的,但是这一部分的思想是真的巧妙,我也不知道怎么去详细解释,如果不懂的话就仔细看看Python中矩阵相关操作吧,尤其是numpy的了解非常重要。
上面我们得到了标签组合矩阵,但是Kuhn-Munkres算法是用来计算最低代价的分配方式的,(不了解Kuhn-Munkres算法的话建议去看这篇博客Kuhn-Munkres算法将聚类后的预测标签映射为真实标签,如果不需要深究具体思路的,只需要了解这个算法能够达到的目的即可),而我们需要的是所有标签对应的数据重复数量最多的映射关系,所以我们可以在这个矩阵前加个负号,就能由求最多变成求最少,如图
在这里插入图片描述

然后利用Python集成的Munkres方法计算出标签对应关系为:
在这里插入图片描述
意思就是 ,预测标签为1的所有数据的标签需要改成3,以此类推。

2.3 ACC与NMI评价

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
NMI看起来比较复杂,但是在Python中已经集成了,而ACC很简单,实现如下:
Acc.py文件 :

import numpy as np

def acc(L1, L2):
    sum = np.sum(L1[:]==L2[:])
    return sum/len(L2)

而NMI的调用为:
NMI.py文件:

from sklearn import metrics

def nmi(L1, L2):
    return metrics.normalized_mutual_info_score(L1, L2)

这里L1为真实标签数组,L2为预测标签数组,都是横向量,在本次实验的数据中,标签数据为列向量,只需要使用 L2.T 即可将其转置。

三、实验结果及代码下载

这里只用lung.mat数据检验结果。
使用KMeans方法聚类后标签结果为:
在这里插入图片描述
一共5类标签,只需要在此基础上+1即可。
映射关系为:
在这里插入图片描述
使用Kuhn-Munkres重排标签后结果为:
在这里插入图片描述
真实标签为:
在这里插入图片描述
计算出的ACC和NMI分别为:

ACC: 0.7487684729064039

NMI: 0.6325405876805188

使用百度网盘下载代码链接:https://pan.baidu.com/s/1hUXIpTkQexRI57rS_pLuHQ
提取码:0w14
提示:在运行代码之前需要安装好各种需要的Package,如果不知道如何安装相关包,请看Python+Selenium多线程基础微博爬虫这篇文章。

最后,非常感谢https://blog.csdn.net/u013927464/article/details/82953854和https://www.cnblogs.com/lunge-blog/p/11666563.html这两篇文章,我的代码都是基于这两篇博主的代码进行修改实现的。

  • 9
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值