排序比较指标

衡量两个次序的差异的指标

1. Fitness

\[ F = \frac{1}{Z} \sum_j \frac{w_j}{\alpha (|p_i - q_j|+1) + (1-\alpha) p_i} \]

其中:

  • $p_j$: 待排序中第 $j$ 条结果的位置
  • $q_j$: 待测排序的第 $j$ 条结果在标准排序中的位置
  • $w_j$: 标准排序中位置 $j$ 上的权重
  • $Z$: 归一化因子

2. Kendall tau distance (wikipedia)

\[ K = \frac{2}{N(N-1)} \sum_{i=1}^{N-1} \sum_{j<i}^N k(i,j) \]

where 

\[ k(i,j) = \begin{cases} 0 & \text{(if $i$, $j$ is in same order)} \\ 1 & \text{(if $i$, $j$ is not in same order)} \end{cases} \]

    $K$ 的取值范围是 $[0,1]$,两种顺序完全相同时为 $0$,完全相反时为 $1$.

3. NDCG (wikipedia)

两种定义(第一个有改动):

\[ NDCG = \frac{1}{Z} \sum_{j = 1}^N \frac{r_j}{\log_2 (1+j)} \]

\[ NDCG = \frac{1}{Z} \sum_{j = 1}^N \frac{2^{r_j}-1}{\log_2 (1+j)} \]

  • $r_j$: 第 $j$ 条结果的评分等级
  • $Z$: 归一化因子,理想最大值

4. AUC(wikipedia)

 数据样例:

  • $y$: 正负样本标记,预测目标, $ y \in \{0, 1\} $
  • $s$: 预测分数 score  
  • $r$:按 score 排序后的顺序标记 rank
  • $m, n$:正负样本个数
score 0.86 0.81 0.73 0.66 0.52 0.43 0.36 0.31 0.26
rank 9 8 7 6 5 4 3 2 1
y 1 1 0 1 1 0 1 0 0

 

ROC曲线:

  • ROC:receiver operating characteristic curve。
  • 横坐标:FP (false positive),纵坐标:TF(True positive)。
  • 遍历分类器阈值,可得到一系列不同的 (FP,TP) ,这些点构成的曲线即为ROC。
  • 等效于,在排序好的数据列表中,从左向右扫过score。若将当前扫过的位置作为分类线,当前 score 作为分类阈值,则每个位置对应一个 (FP, TP) 值对。FP、TP 分别等于当前位置左侧的 $y=1, y=0$ 的个数。 

\[(0,0), (0,1), (0,2), (1,2), (1,3), (1,4), (2, 4), (2,5), (3,5), (4,5)\]

  

AUC

  AUC 即 ROC 曲线下的面积。三种计算方式:

  1,根据定义:遍历排好序的样本,每个样本画出ROC上的一个点。每遇一个正样本,纵轴升一个(TP++);每遇一个负样本,横轴加一个(FP++),同时累积面积增加TP。遍历完后,TP = $m$, FP = $n$,所得面积除以总面积 ($m \cdot n$) 即为AUC。

  只需遍历一次,复杂度为 $O(N)$

  2,在按 score 排序的样本序列中,每个正样本之后的负样本的个数的和,除以 $m \cdot n$。

  复杂度为 $O(N^2)$。

    $$ AUC = \frac{\sum_{i_+} | \{ s_{j_-} |s_{j_-} < s_{i_+}\}|} {m \cdot n} $$

  3,方法2换个角度看,就是把正负样本之间两两组合,其中正样本 score 大于负样本 score 的组合的个数(score相等的算0.5) 。结果再除以 $m \cdot n$

    $$ AUC = \frac{|\{(s_{i_+}, s_{j_-}) | s_{i_+} > s_{j_-}\}|}{m \cdot n} $$

  4,计算正样本的 rank 的和,然后按下式计算

    \[ AUC = \frac{\sum r_i y_i - m (m + 1) / 2}{m \cdot n}  \]

  5,区块方法。以上方法都是对单例计算的,无法计算 AUC_UP。将值相近的 score 聚合成区块,用一个得分 $t_i$ 表示,统计每个区块 $i$ 中的正负例个数 $m_i, n_i$,形成压缩的新数据元:$(t_i, m_i, n_i)$。按 $t_i$ 排序后,再按 1 中的方法计算累积面积(用梯形面积近似,所以每次增加面积是 $(TP + m_i/2) \cdot n_i$)。每次纵轴、横轴增加的量分别为 $m_i, n_i$。

    计算 AUC_UP 时,只需将序列按新数据元的经验概率 $m_i/(m_i+n_i)$ 排序,再按相同方法计算 AUC。

   

  2->1 方法的等效性:

  方法 2 中,每个正例之后的负例的个数,等于在 ROC 曲线中,该正例点向右的长度。即每个正例对 ROC 下的面积的贡献,是其对应的点向右的宽度为1的横带。这是因为正例的出现只使 ROC 曲线在纵向上增长。

  2->4 方法的等效性:

  对于第一个正例,其后的负样本的个数为 $9-m$。对于第 $i_+$ 个正例,其后共有 $r_{i+} - 1$ 个样本,其中正例有 $m-i$ 个,所以其后的负例个数是 $r_{i_+} - 1 - (m-i_+)$。所以求和过程做以下整理后,即与方法 3 相同。

$$ \sum_{i_+ = 1}^m [ r_{i+} - 1 - (m-i_+) ]
= \sum_{i_+ = 1}^m r_{i+} - m - m^2 + \sum_{i_+ = 1}^m  i_+ \\
\qquad = \sum_{i_+ = 1}^m r_{i+} - m (m+1)/2 \\
\qquad = \sum_i r_i y_i - m (m+1)/2 $$ 

  5->1 方法的等效性:

  单数据元中,正负例 $y_i=0,1$ 分别对应batch方法中 $(m_i, n_i)$ 等于 $(1, 0), (0,1)$。所以方法 1 是 4 的特例,可从实现代码中看出。

 

示例代码:

 1 #!/usr/bin/env python
 2 #!encoding:utf-8
 3 
 4 s = [0.86, 0.81, 0.73, 0.66, 0.52, 0.43, 0.36, 0.31, 0.26]
 5 y = [1,    1,    0,    1,    1,    0,    1,    0,    0]
 6 
 7 ll = [[s[i], y[i]] for i in range(len(s)) ]
 8 
 9 def auc_1(ll):
10     m, n, area = 0, 0, 0
11     for s, y in ll:
12         if y == 1:
13             m += 1
14         if y == 0:
15             n += 1
16             area += m
17     return 1. * area / (m * n)
18 
19 def auc_2(ll):
20     m, n, cnt = 0, 0, 0. 
21     for i, (s, y) in enumerate(ll): 
22         if y == 0: 
23             n += 1
24             continue 
25         if y == 1: 
26             m += 1
27             for j in range(i, len(ll)): 
28                 if ll[j][1] == 0: 
29                     cnt += 1
30     return cnt / (m * n) 
31 
32 def auc_3(ll):
33     m, n, cnt = 0, 0, 0
34     for i in range(len(ll)):
35         if ll[i][1] == 1:
36             m += 1
37         elif ll[i][1] == 0:
38             n += 1
39         for j in range(i+1, len(ll)):
40             if ll[i][0] == ll[j][0]:
41                     cnt += 0.5
42             elif ll[i][0] > ll[j][0] and ll[i][1] > ll[j][1]:
43                 cnt += 1
44             if ll[i][0] < ll[j][0] and ll[i][1] < ll[j][1]:
45                 cnt += 1
46     return 1. * cnt / (m * n)
47 
48 def auc_4(ll):
49     m, n, q = 0, 0, 0
50     for i, (s, y) in enumerate(ll):
51         if y == 1:
52             m += 1
53             q += len(ll) - i
54         if y == 0:
55             n += 1
56     auc = 1. * ( q - m * (m + 1) / 2) / (m * n)
57     return auc
58 
59 def for_auc_5(score_np): 
60     m, n, area = 0, 0, 0.  
61     for t, [mi, ni] in score_np: 
62         area += (m + mi /2.)*ni
63         m += mi
64         n += ni
65     return area / (m * n)  
66 
67 def auc_5(ll):
68     score_block = {}      
69     for s, y in ll: 
70         t = round(s, 5)   # 粗粒化。省略则与方法1相同。粒度越粗AUC_UP越低
71         if t in score_block:
72             score_block[t][0] += y
73             score_block[t][1] += 1-y
74         else:
75             score_block[t] = [y, 1-y]
76     score_np = sorted(score_block.items(), key = lambda x: -x[0])
77     auc = for_auc_5(score_np) 
78     score_np = sorted(score_block.items(), key = lambda x: -x[1][0]/(x[1][0]+x[1][1]))
79     auc_up = for_auc_5(score_np) 
80     return auc, auc_up 
81 
82 ll.sort(key = lambda x: -x[0]) 
83 
84 print "auc1:", auc_1(ll)
85 print "auc2:", auc_2(ll)
86 print "auc3:", auc_3(ll)
87 print "auc4:", auc_4(ll)
88 print "auc5:", auc_5(ll)
View Code

 

posted on 2015-01-16 12:12  bingoe  阅读(827)  评论(0编辑  收藏  举报

导航