-
Notifications
You must be signed in to change notification settings - Fork 6
/
metrics.py
108 lines (75 loc) · 3.03 KB
/
metrics.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
from __future__ import division
import numpy as np
from utils import group_offsets
class Scorer(object):
def __init__(self, score_func, **kwargs):
self.score_func = score_func
self.kwargs = kwargs
def __call__(self, *args):
return self.score_func(*args, **self.kwargs)
# Precision
#
def _p_score(y_true, y_pred, k=None):
order = np.argsort(-y_pred)
y_true = np.take(y_true, order[:k])
return np.sum(y_true > 0) / len(y_true)
def p_score(y_true, y_pred, qid, k=None):
return np.array([_p_score(y_true[a:b], y_pred[a:b], k=k) for a, b in group_offsets(qid)])
class PScorer(Scorer):
def __init__(self, **kwargs):
super(PScorer, self).__init__(p_score, **kwargs)
# AP (Average Precision)
#
def _ap_score(y_true, y_pred):
order = np.argsort(-y_pred)
y_true = np.take(y_true, order)
pos = 1 + np.where(y_true > 0)[0]
n_rels = 1 + np.arange(len(pos))
return np.mean(n_rels / pos) if len(pos) > 0 else 0
def ap_score(y_true, y_pred, qid):
return np.array([_ap_score(y_true[a:b], y_pred[a:b]) for a, b in group_offsets(qid)])
class APScorer(Scorer):
def __init__(self):
super(APScorer, self).__init__(ap_score)
# DCG/nDCG (Normalized Discounted Cumulative Gain)
#
def _burges_dcg(y_true, y_pred, k=None):
# order = np.argsort(y_pred)[::-1]
order = np.argsort(-y_pred)
y_true = np.take(y_true, order[:k])
gain = 2 ** y_true - 1
discounts = np.log2(np.arange(len(gain)) + 2)
return np.sum(gain / discounts)
def _trec_dcg(y_true, y_pred, k=None):
order = np.argsort(-y_pred)
y_true = np.take(y_true, order[:k])
gain = y_true
discounts = np.log2(np.arange(len(gain)) + 2)
return np.sum(gain / discounts)
def _dcg_score(y_true, y_pred, qid, k=None, dcg_func=None):
assert dcg_func is not None
y_true = np.maximum(y_true, 0)
return np.array([dcg_func(y_true[a:b], y_pred[a:b], k=k) for a, b in group_offsets(qid)])
def _ndcg_score(y_true, y_pred, qid, k=None, dcg_func=None):
assert dcg_func is not None
y_true = np.maximum(y_true, 0)
dcg = _dcg_score(y_true, y_pred, qid, k=k, dcg_func=dcg_func)
idcg = np.array([dcg_func(np.sort(y_true[a:b]), np.arange(0, b - a), k=k)
for a, b in group_offsets(qid)])
assert (dcg <= idcg).all()
idcg[idcg == 0] = 1
return dcg / idcg
def dcg_score(y_true, y_pred, qid, k=None, version='burges'):
assert version in ['burges', 'trec']
dcg_func = _burges_dcg if version == 'burges' else _trec_dcg
return _dcg_score(y_true, y_pred, qid, k=k, dcg_func=dcg_func)
def ndcg_score(y_true, y_pred, qid, k=None, version='burges'):
assert version in ['burges', 'trec']
dcg_func = _burges_dcg if version == 'burges' else _trec_dcg
return _ndcg_score(y_true, y_pred, qid, k=k, dcg_func=dcg_func)
class DCGScorer(Scorer):
def __init__(self, **kwargs):
super(DCGScorer, self).__init__(dcg_score, **kwargs)
class NDCGScorer(Scorer):
def __init__(self, **kwargs):
super(NDCGScorer, self).__init__(ndcg_score, **kwargs)