forked from uncertainty-toolbox/uncertainty-toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrecalibration.py
101 lines (88 loc) · 3.38 KB
/
recalibration.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
"""
Recalibrating uncertainty estimates.
"""
import numpy as np
from sklearn.isotonic import IsotonicRegression
def get_q_idx(exp_props, q):
num_pts = exp_props.shape[0]
target_idx = None
for idx, x in enumerate(exp_props):
if idx + 1 == num_pts:
if round(q, 2) == round(float(exp_props[-1]), 2):
target_idx = exp_props.shape[0] - 1
break
if x <= q < exp_props[idx + 1]:
target_idx = idx
break
if target_idx is None:
raise ValueError("q must be within exp_props")
return target_idx
def iso_recal(exp_props, obs_props):
"""
Returns an isotonic regression model that maps from obs_props to exp_props
"""
# Flatten
exp_props = exp_props.flatten()
obs_props = obs_props.flatten()
min_obs = np.min(obs_props)
max_obs = np.max(obs_props)
iso_model = IsotonicRegression(increasing=True, out_of_bounds="clip")
# just need observed prop values between 0 and 1
# problematic if min_obs_p > 0 and max_obs_p < 1
if not (min_obs == 0.0) and (max_obs == 1.0):
print("Obs props not ideal: from {} to {}".format(min_obs, max_obs))
exp_0_idx = get_q_idx(exp_props, 0.0)
exp_1_idx = get_q_idx(exp_props, 1.0)
within_01 = obs_props[exp_0_idx : exp_1_idx + 1]
beg_idx, end_idx = None, None
# Handle beg_idx
if exp_0_idx != 0:
min_obs_below = np.min(obs_props[:exp_0_idx])
min_obs_within = np.min(within_01)
if min_obs_below < min_obs_within:
i = exp_0_idx - 1
while obs_props[i] > min_obs_below:
i -= 1
beg_idx = i
elif np.sum((within_01 == min_obs_within).astype(float)) > 1:
# multiple minima in within_01 ==> get last min idx
i = exp_1_idx - 1
while obs_props[i] > min_obs_within:
i -= 1
beg_idx = i
elif np.sum((within_01 == min_obs_within).astype(float)) == 1:
beg_idx = int(np.argmin(within_01) + exp_0_idx)
else:
raise RuntimeError(("Inspect input arrays, " "cannot set beginning index."))
else:
beg_idx = exp_0_idx
# Handle end_idx
if exp_1_idx < obs_props.shape[0] - 1:
max_obs_above = np.max(obs_props[exp_1_idx + 1 :])
max_obs_within = np.max(within_01)
if max_obs_above > max_obs_within:
i = exp_1_idx + 1
while obs_props[i] < max_obs_above:
i += 1
end_idx = i + 1
elif np.sum((within_01 == max_obs_within).astype(float)) > 1:
# multiple minima in within_01 ==> get last min idx
i = beg_idx
while obs_props[i] < max_obs_within:
i += 1
end_idx = i + 1
elif np.sum((within_01 == max_obs_within).astype(float)) == 1:
end_idx = int(exp_0_idx + np.argmax(within_01) + 1)
else:
raise RuntimeError("Inspect input arrays, cannot set ending index")
else:
end_idx = exp_1_idx + 1
if end_idx <= beg_idx:
raise RuntimeError("Ending index before beginning index")
filtered_obs_props = obs_props[beg_idx:end_idx]
filtered_exp_props = exp_props[beg_idx:end_idx]
try:
iso_model = iso_model.fit(filtered_obs_props, filtered_exp_props)
except Exception:
raise RuntimeError("Failed to fit isotonic regression model")
return iso_model