Coverage for src/bob/measure/calibration.py: 12%

42 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-06 21:23 +0100

1#!/usr/bin/env python 

2# vim: set fileencoding=utf-8 : 

3# Thu May 16 11:41:49 CEST 2013 

4 

5"""Measures for calibration""" 

6 

7import math 

8 

9import numpy 

10 

11 

12def cllr(negatives, positives): 

13 """Cost of log likelihood ratio as defined by the Bosaris toolkit 

14 

15 Computes the 'cost of log likelihood ratio' (:math:`C_{llr}`) measure as 

16 given in the Bosaris toolkit 

17 

18 

19 Parameters: 

20 

21 negatives (array): 1D float array that contains the scores of the 

22 "negative" (noise, non-class) samples of your classifier. 

23 

24 positives (array): 1D float array that contains the scores of the 

25 "positive" (signal, class) samples of your classifier. 

26 

27 

28 Returns: 

29 

30 float: The computed :math:`C_{llr}` value. 

31 

32 """ 

33 sum_pos, sum_neg = 0.0, 0.0 

34 for pos in positives: 

35 sum_pos += math.log(1.0 + math.exp(-pos), 2.0) 

36 for neg in negatives: 

37 sum_neg += math.log(1.0 + math.exp(neg), 2.0) 

38 return (sum_pos / len(positives) + sum_neg / len(negatives)) / 2.0 

39 

40 

41def min_cllr(negatives, positives): 

42 """Minimum cost of log likelihood ratio as defined by the Bosaris toolkit 

43 

44 Computes the 'minimum cost of log likelihood ratio' (:math:`C_{llr}^{min}`) 

45 measure as given in the bosaris toolkit 

46 

47 

48 Parameters: 

49 

50 negatives (array): 1D float array that contains the scores of the 

51 "negative" (noise, non-class) samples of your classifier. 

52 

53 positives (array): 1D float array that contains the scores of the 

54 "positive" (signal, class) samples of your classifier. 

55 

56 

57 Returns: 

58 

59 float: The computed :math:`C_{llr}^{min}` value. 

60 

61 """ 

62 

63 # first, sort both scores 

64 neg = sorted(negatives) 

65 pos = sorted(positives) 

66 N = len(neg) 

67 P = len(pos) 

68 II = N + P 

69 # now, iterate through both score sets and add a 0 for negative and 1 for 

70 # positive scores 

71 n, p = 0, 0 

72 ideal = numpy.zeros(II) 

73 neg_indices = [0] * N 

74 pos_indices = [0] * P 

75 for i in range(II): 

76 if p < P and (n == N or neg[n] > pos[p]): 

77 pos_indices[p] = i 

78 p += 1 

79 ideal[i] = 1 

80 else: 

81 neg_indices[n] = i 

82 n += 1 

83 

84 # compute the pool adjacent violaters method on the ideal LLR scores 

85 ghat = numpy.ndarray(ideal.shape, dtype=numpy.float) 

86 raise NotImplementedError("No pavx implementation") 

87 pavx(ideal, ghat) # noqa: F821 

88 

89 # disable runtime warnings for a short time since log(0) will raise a warning 

90 old_warn_setup = numpy.seterr(divide="ignore") 

91 # ... compute logs 

92 posterior_log_odds = numpy.log(ghat) - numpy.log(1.0 - ghat) 

93 log_prior_odds = math.log(float(P) / float(N)) 

94 # ... activate old warnings 

95 numpy.seterr(**old_warn_setup) 

96 

97 llrs = posterior_log_odds - log_prior_odds 

98 

99 # some weired addition 

100 # for i in range(II): 

101 # llrs[i] += float(i)*1e-6/float(II) 

102 

103 # unmix positive and negative scores 

104 new_neg = numpy.zeros(N) 

105 for n in range(N): 

106 new_neg[n] = llrs[neg_indices[n]] 

107 new_pos = numpy.zeros(P) 

108 for p in range(P): 

109 new_pos[p] = llrs[pos_indices[p]] 

110 

111 # compute cllr of these new 'optimal' LLR scores 

112 return cllr(new_neg, new_pos)