『ML』用Python实现聚类效果的评估(轮廓系数、互信息)

  好的聚类:类内凝聚度高,类间分离度高。

  本文介绍两种聚类评估方法,轮廓系数(Silhouette Coefficient)以及标准化互信息(NMI),并且用Python实现。

  关于K-Means聚类请看 利用K-Means聚类算法对未标注数据分组


效果评估综述

  这里直接贴上 聚类算法初探(七)聚类分析的效果评测

  它摘自于中国科学院计算技术研究所周昭涛的硕士论文《文本聚类分析效果评价及文本表示研究》的第三章。建议先看看原文,可以对聚类评估有一个很好的了解。

  综合来说,我们希望最终的聚类结果是:同一个簇内的点是紧密的,而不同簇之间的距离是较远的;同时,它也要与我们人工的判断相一致。

  接下来介绍两种聚类评估方法:轮廓系数和标准化互信息。前者是无需数据标注,判断聚类的 类内距 类间距 ;而后者是需要对于数据进行 标注 ,判断聚类的 准确性


轮廓系数

  在聚类时,如果 类别未知 时,可以选择轮廓系数作为聚类性能的评估指标。从 百度百科 摘取的轮廓系数的介绍:

  轮廓系数是聚类效果好坏的一种评价方式。它结合内聚度分离度两种因素。可以用来在相同原始数据的基础上用来评价不同算法、或者算法不同运行方式对聚类结果所产生的影响。

  以K-Means举例。由于 k k k 的值是需要事先给定的。如果我们不太清楚数据分几个簇比较好,我们可以尝试用不同的 k k k 值来计算轮廓系数,从而选择 最优 的一个 k k k 值。计算过程如下:

  • 对于一个簇中的一个点 i i i
  • a a a ( i i i ) = a v e r a g e average average ( i i i 向量到所有它属于的簇中其它点的距离 )
  • b b b ( i i i ) = m i n min min ( i i i 向量到与它相邻最近的一簇内的所有点的平均距离 )
  • 那么 i i i 的轮廓系数为 S ( i ) = ( b ( i ) − a ( i ) ) / m a x { a ( i ) , b ( i ) } S(i)=( b(i)-a(i) ) / max\{ a(i),b(i) \} S(i)=(b(i)a(i))/max{ a(i),b(i)}
  • 将所有点的轮廓系数求平均,就是该聚类结果总的轮廓系数。

  其中, a a a ( i i i ) 可以理解为一点与簇内其他点不相似性的平均值,即类内的 凝聚度 b b b ( i i i ) 可以理解为一点与最近簇的不相似性的平均值,即类间的 分离度

  我们也可以看到, S ( i ) S(i) S(i) 是介于[-1, 1]之间的。 a a a ( i i i ) 越小, b b b ( i i i ) 越大,说明聚类效果越好,轮廓系数 S ( i ) S(i) S(i) 就越接近1

  那么,如果用Python来实现呢?给出它的伪代码:

输入:数据的个数m,k的取值范围
输出:每个k所对应的轮廓系数

在取值范围内的每一个k:
	对于每一个簇:
		对于每一个点i:
			计算i与簇内其他点距离之和的平均值,记为a(i)
			计算i与其他簇每个点距离之和的平均值,选择最小的那一个,记为b(i)
			s(i) = (b(i)-a(i)) / max(a(i), b(i))
	加总每一个点的轮廓系数,记为 sum
	轮廓系数 s = sum / m			

  接下来用代码实现,K-Means 具体细节可以参考 利用K-Means聚类算法对未标注数据分组

  因此,运行K-Means的代码我就不贴啦,我就直接附上轮廓系数的代码。代码中的一些细节在上面的博客中得到了解释,有什么疑问可以先看看那篇博文哦。

  首先,我的数据集为testSet.txt,运行 K-Means 的代码为biKMeans(data, k) d a t a data data 为传入的数据集矩阵, k k k 是簇的个数。这个函数返回两个值,一个是中心点,另一个是clusterAssment,它一共有 m m m 行( m m m 为数据集的行数),两列,第一列保存这一点所在簇的索引,第二列保存到簇中心距离的平方。

if __name__ == '__main__':
    data = np.mat(loadDataSet('testSet.txt'))
    m = np.shape(data)[0]  # 一共有m行数据

    for k in range(2, 10):  # 簇的个数取值在2到9之间
    
        clusterAssment = biKMeans(data, k)[1]  # 进行二分类,返回保存簇索引的矩阵
        s_sum = 0  # 所有簇的s值
        cluster_s = 0  # 一个簇所有点的的s值
        
        for cent in range(k):  # 对于每一个簇
            category = np.nonzero(clusterAssment[:, 0] == cent)[0]  # 得到簇索引为cent的值的位置,形式类似为[1, 4 ,6]
            clusterNum = len(category)  # 该簇中点的个数
            s = 0
            for index, lineNum in enumerate(category):  # 对于簇中的每一个点,index为索引,lineNum为点所在的行数

                # 计算该点到簇内其他点的距离之和的平均值
                innerSum = 0
                for i in range(clusterNum):
                    if i == index:
                        continue  # 若为当前该点,则跳出本次循环
                    dis = distCal(data[category[i]], data[lineNum])  # 若二者为不同点,计算二者之间的距离
                    innerSum += dis  # 将之保存到内部距离
                a = innerSum / clusterNum

                # 计算该点到其他簇所有点距离之和的最小平均值
                minDis = np.inf  # 设定初始最小值为无穷大
                for other_cent in range(k):  # 对于每一个簇
                    if other_cent != cent:  # 如果和上面给定的簇不一样
                        other_category = np.nonzero(clusterAssment[:, 0] == other_cent)[0]  # 得到簇里面点对应的行数
                        other_clusterNum = len(other_category)  # 该簇中点的个数
                        other_sum = 0
                        for other_lineNum in other_category:  # 对于簇中的每一个点
                            other_dis = distCal(data[other_lineNum], data[lineNum])
                            other_sum += other_dis
                        other_sum = other_sum / other_clusterNum  # 求平均
                        if other_sum < minDis:
                            minDis = other_sum  # 如果一个点距离另外一个簇所有点的距离小于当前最小值,则更新
                b = minDis

                s += (b-a) / max(a, b)  # 每一个点的轮廓系数
            cluster_s += s  # 每一个簇的s值
        s_sum = cluster_s / m  # 取平均
        
        print("当前k的值为:%d" % k)
        print("轮廓系数为:%s" % str(s_sum))
        print('***' * 20)

  我的数据集一共有80行,可以看到我的 k k k 取值范围在[2, 10),最终打印的结果如下:

在这里插入图片描述
  可以看到,当 k k k 取4时效果是比较好的。

  注意:对于簇结构为凸的数据轮廓系数值高,而对于簇结构非凸需要使用DBSCAN进行聚类的数据,轮廓系数值低,因此,轮廓系数 不应该 用来评估 不同聚类算法 之间的优劣,比如Kmeans聚类结果与DBSCAN聚类结果之间的比较。

  画出图来,发现的确是 k = 4 k=4 k=4 的效果比较好!

在这里插入图片描述
  画图的代码是用sklearn做的,请看原文博主:sklearn之聚类评估指标—轮廓系数

  还是贴出代码吧!sklearn有现成的函数可以调用,就不用写轮廓系数的代码啦!很方便的!


                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值