兰德指数计算

机器算法验证 聚类
2022-02-03 10:35:31

我试图弄清楚如何计算集群算法的兰德指数,但我被困在如何计算真假阴性这一点上。

目前,我正在使用《信息检索简介》(Manning, Raghavan & Schütze, 2009)一书中的示例。在第 359 页,他们讨论了如何计算兰德指数。对于此示例,他们使用三个集群,并且集群包含以下对象。

  1. 啊啊啊
  2. bbbbc
  3. aaccc

我替换了对象(将原始符号替换为字母,但想法和数量保持不变)。我将给出书中的确切单词,以了解他们在说什么:

我们首先计算TP+FP。这三个聚类分别包含 6、6 和 5 个点,因此同一聚类中的“正面”或文档对的总数为:

TP + FP = (62) + (62) + (52) = 15 + 15+ 10 = 40

其中,集群 1 中的 a 对、集群 2 中的 b 对、集群 3 中的 c 对和集群 3 中的 a 对是真阳性:

TP = (52) + (42) + (32) + (22) = 10 + 6 + 3 + 1 = 20

因此,FP = 40 - 20 = 20。

直到这里计算很清楚,如果我举其他例子,我会得到相同的结果,但是当我想计算假阴性和真阴性时,曼宁等人。陈述如下:

FN 和 TN 的计算方法类似,得到以下列联表:

列联表如下所示:

+--------+--------+
| TP: 20 | FN: 24 |
+--------+--------+
| FP: 20 | TN: 72 |
+--------+--------+

我不清楚这句话:“FN 和 TN 的计算方式相似”,我不明白计算 TN 和 FN 需要哪些数字。我可以通过执行以下操作来计算表格的右侧:

TP + FP + FN + TN = (n2) = (172) = 136

资料来源:http ://en.wikipedia.org/wiki/Rand_index

因此,FN + TN = 136 - TP + FP = 136 - 40 = 96,但这并不能真正帮助我弄清楚如何分别计算变量。特别是当作者说:“FN 和 TN 的计算方式相似”。我不明白怎么做。此外,当我查看其他示例时,他们通过查看每一对来计算列联表的每个单元格。

例如:http ://www.otlet-institute.org/wikics/Clustering_Problems.html#toc-Subsection-4.1

我的第一个问题,基于 Manning et al (2009) 的例子,如果你只知道 TP & NP,是否可以计算 TN 和 FN?如果是这样,根据给定的示例,类似的计算看起来如何?

4个回答

我也在思考同样的问题,我就这样解决了。假设您有一个共现矩阵/列联表,其中行是地面实况聚类,列是聚类算法找到的聚类。

因此,对于书中的示例,它看起来像:

  | 1 | 2 | 3
--+---+---+---
x | 5 | 1 | 2
--+---+---+---
o | 1 | 4 | 0
--+---+---+---
◊ | 0 | 1 | 3

现在,您可以通过获取每列的总和并在所有这些值上“选择 2”来非常轻松地计算 TP + FP。所以总和是 [6, 6, 5] 并且您执行“6 选择 2”+“6 选择 2”+“5 选择 2”。

现在,实际上,类似地,您可以通过对行求和来获得 TP + FN(因此,在上面的示例中为 [8, 5, 4]),对所有这些值应用“选择 2”,然后取总和。

TP 本身可以通过将“选择 2”应用于矩阵中的每个单元格并取所有内容的总和来计算(假设“1 选择 2”为 0)。

事实上,这里有一些 Python 代码正是这样做的:

import numpy as np
from scipy.misc import comb

# There is a comb function for Python which does 'n choose k'                                                                                            
# only you can't apply it to an array right away                                                                                                         
# So here we vectorize it...                                                                                                                             
def myComb(a,b):
  return comb(a,b,exact=True)

vComb = np.vectorize(myComb)

def get_tp_fp_tn_fn(cooccurrence_matrix):
  tp_plus_fp = vComb(cooccurrence_matrix.sum(0, dtype=int),2).sum()
  tp_plus_fn = vComb(cooccurrence_matrix.sum(1, dtype=int),2).sum()
  tp = vComb(cooccurrence_matrix.astype(int), 2).sum()
  fp = tp_plus_fp - tp
  fn = tp_plus_fn - tp
  tn = comb(cooccurrence_matrix.sum(), 2) - tp - fp - fn

  return [tp, fp, tn, fn]

if __name__ == "__main__":
  # The co-occurrence matrix from example from                                                                                                           
  # An Introduction into Information Retrieval (Manning, Raghavan & Schutze, 2009)                                                                       
  # also available on:                                                                                                                                   
  # http://nlp.stanford.edu/IR-book/html/htmledition/evaluation-of-clustering-1.html                                                                     
  #                                                                                                                                                      
  cooccurrence_matrix = np.array([[ 5,  1,  2], [ 1,  4,  0], [ 0,  1,  3]])

  # Get the stats                                                                                                                                        
  tp, fp, tn, fn = get_tp_fp_tn_fn(cooccurrence_matrix)

  print "TP: %d, FP: %d, TN: %d, FN: %d" % (tp, fp, tn, fn)

  # Print the measures:                                                                                                                                  
  print "Rand index: %f" % (float(tp + tn) / (tp + fp + fn + tn))

  precision = float(tp) / (tp + fp)
  recall = float(tp) / (tp + fn)

  print "Precision : %f" % precision
  print "Recall    : %f" % recall
  print "F1        : %f" % ((2.0 * precision * recall) / (precision + recall))

如果我运行它,我会得到:

$ python testCode.py
TP: 20, FP: 20, TN: 72, FN: 24
Rand index: 0.676471
Precision : 0.500000
Recall    : 0.454545
F1        : 0.476190

我实际上没有检查除此之外的任何其他示例,所以我希望我做对了.... ;-)

在研究了这个线程中的其他答案之后,这是我的 Python 实现,它将数组作为输入,sklearn-style:

import numpy as np
from scipy.misc import comb

def rand_index_score(clusters, classes):

    tp_plus_fp = comb(np.bincount(clusters), 2).sum()
    tp_plus_fn = comb(np.bincount(classes), 2).sum()
    A = np.c_[(clusters, classes)]
    tp = sum(comb(np.bincount(A[A[:, 0] == i, 1]), 2).sum()
             for i in set(clusters))
    fp = tp_plus_fp - tp
    fn = tp_plus_fn - tp
    tn = comb(len(A), 2) - tp - fp - fn
    return (tp + tn) / (tp + fp + fn + tn)

In [319]: clusters
Out[319]: [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2]

In [320]: classes
Out[320]: [0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 2, 1, 0, 2, 2, 2, 0]

In [321]: rand_index_score(clusters, classes)
Out[321]: 0.67647058823529416

我自己也不太确定,但这就是我计算 TN 值的方式:
TN=(7 2) (10 2) (4 2)

(7 2) – 集群 1 – 测试说“x”,所以计算那些不是 x 的(并且正确地聚集在集群 2 和 3 中)

即 4 'o's + 3 'd's (钻石) =(7 2)

(10 2) – 聚类 2,计算那些不是“o”并且正确聚类在聚类 1 和 3 中的那些,

即 5'x' +(2'x'+ 3'd') = (10 2)

(4 2) – 聚类 3,计算那些正确聚类在聚类 1 和 2 中的非“x”和非“d”(菱形元素)。

即集群2中的4'o's。=(4 2)

TN = (7 2) + (10 2) + (4 2) =72。

那么 FN 是:

FN = (17 2) - (TP+FP) - TN = 136 - 40 -72 = 24. ---> (17= 文件总数)

以另一个问题为例:

  | 1 | 2 | 3
--+---+---+---
x | 5 | 1 | 2
--+---+---+---
o | 1 | 4 | 0
--+---+---+---
◊ | 0 | 1 | 3

FN 的合理答案:

FN = (c(8,2)-c(5,2)-c(2,2))+(c(5,2)-c(4,2))+(c(4,2)-c(3,2))=24  

解释:

  • (c(8,2)-c(5,2)-c(2,2))

    从 8 中选择 2 表示 'x'(a) 相同集群中同一类的组合(集群 1 的 c(5,2) 和集群 3 的 c(2,2) ),

  • (c(5,2)-c(4,2))

    从 5 'o'(b) 中选择 2 减去相同集群中相同类的组合(集群 2 的 c(4,2) )

  • (c(4,2)-c(3,2)

    从 4 中选择 2 表示 '◇'(c) 减去同一集群中同一类的组合(集群 3 的 c(3,2) )

我是这样推导出来的。