聚类模型评估综述-兰德指数的公式理解及scala版本实现

概述

聚类的评估的指标,大方向是分为内部指标和外部指标。

内部指标:包括轮廓系数、Calinski-Harabaz 指数 等,内部指标是在开发阶段用的,一般用来选择聚类的个数。

外部指标:

分为两种:

有标签的结果评价:包括 兰德指数、纯度、互信息、v-measure 

无标签结果评估:于聚类中心的平均距离等

 

兰德指数

给定nn个对象集合S={O1,O2,....,On}S={O1,O2,....,On},假设U={u1,...,uR}U={u1,...,uR}和V={v1,...,vC}V={v1,...,vC}表示S的两个不同划分并且满足⋃Ri=1ui=S=⋃Cj=1vj⋃i=1Rui=S=⋃j=1Cvj , ui⋂ui∗=∅=vj⋂vj∗ui⋂ui∗=∅=vj⋂vj∗,其中1≤i≠i∗≤R1≤i≠i∗≤R,1≤j≠j∗≤C1≤j≠j∗≤C。

假设UU是外部评价标准即true_label,而VV是聚类结果。设定四个统计量:

  • aa为在UU中为同一类且在VV中也为同一类别的数据点对数
  • bb为在UU中为同一类但在VV中却隶属于不同类别的数据点对数
  • cc为在UU中不在同一类但在VV中为同一类别的数据点对数
  • dd为在UU中不在同一类且在VV中也不属于同一类别的数据点对数
Class\ClusterSame ClusterDifferent ClusterSumU
Same Classaba+b
Different Classcdc+d
SumVa+cb+da+b+c+d

此时,兰德系数为:

RI=a+da+b+c+dRI=a+da+b+c+d

兰德系数的值在[0,1]之间,当聚类结果完美匹配时,兰德系数为1。

 

详细解释,可能看到上面的一个公式还是比较蒙圈,有的文章里面会和分类任务的TP、FP、TN、FN来说,其实是不对的。

这里的兰德系数计算前提的两两比较,从真实的标签里面任意选择两个进行比较,从预测的标签里面相同的顺序里面选择出来做比较。

如果真实标签里面样本标签一致,而且预测标签选取的两个里面样本一致,则代表的是TP

如果真实标签里面样本标签不一致,而且预测标签选取的两个里面样本不一致,则代表的是TN

则兰德指数 RI= (TP+TN)/TP+FP+TN+FN

这个看起来像不像是准确率的公式,要注意这里的前提并不一样

这里的TP+FP+TN+FN 并不是所有样本的总数,而是在所有的样本里面选择两个,根据排列组合:Cn2= n*(n-1)/2

TP、TN就自己进行计算了

接下来就算一个算法提了,没有想到什么好的方法,我们使用最暴力的循环进行计算。

附录scala实现:

package com.tiger

import com.alibaba.fastjson.JSONObject
import org.apache.spark.sql.SparkSession


class clusterEva(sessionApp:SparkSession, paramJson:JSONObject) extends EmmpBaseEva(paramJson) {


    def evaModel(): String = {
      try {
        val (scoreAndLable,totalCount,validCount,sql) = new DataUtil(sessionApp, paramJson).getClusterPredictAndLable()
        if (validCount==0){
          throw  new Exception("根据参数获取到的数据为空:sql:"+sql)
        }
        val list=scoreAndLable.collect().toList
        var a,b=0
        for(i <- 0 until list.length){
          for(j <- i+1 until list.length){
              if(list(i)._1==list(j)._1 && list(i)._2==list(j)._2){
                a+=1
              }
              if(list(i)._1!=list(j)._1 && list(i)._2!=list(j)._2){
                b+=1
            }
          }
        }
        val landValue=1.0*(a+b)/(validCount*(validCount-1)/2)
        val dataJson=new JSONObject()
        dataJson.put("landValue", landValue)
        resultJson.put("data",dataJson)
        resultJson.put("totalCount",totalCount)
        resultJson.put("validCount",validCount)
        evaSuccess
      }catch {
        case ex:Exception => evaError(ex)
      }
      resultJson.toJSONString
    }

}

 

兰德指数在spark mlib里面是没有封装的,估计是无法进行分布式计算,

在数据量大的情况下如何进行分布式计算,欢迎大家探讨指教!


版权声明:本文为xiefu5hh原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。