概述
聚类的评估的指标,大方向是分为内部指标和外部指标。
内部指标:包括轮廓系数、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\Cluster | Same Cluster | Different Cluster | SumU |
|---|---|---|---|
| Same Class | a | b | a+b |
| Different Class | c | d | c+d |
| SumV | a+c | b+d | a+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里面是没有封装的,估计是无法进行分布式计算,
在数据量大的情况下如何进行分布式计算,欢迎大家探讨指教!