3
3
Tutorial for set prediction
4
4
===========================
5
5
6
- In this tutorial , we propose set prediction for binary classification
7
- estimated by :class:`~mapie .classification._MapieClassifier ` with the "lac"
6
+ In this example , we propose set prediction for binary classification
7
+ estimated by :class:`~mapie_v1 .classification.SplitConformalClassifier ` with the "lac"
8
8
method on two-dimensional dataset.
9
9
10
10
Throughout this tutorial, we will answer the following questions:
11
11
12
12
- How does the number of classes in the prediction sets vary according to
13
- the significance level ?
13
+ the confidence level?
14
14
15
- - Is the conformal method well calibrated ?
15
+ - Is the conformal method well calibrated?
16
16
17
17
- What are the pros and cons of the set prediction for binary classification
18
- in MAPIE ?
18
+ in MAPIE?
19
19
20
- PLEASE NOTE: we don't recommend using set prediction in MAPIE, even though
21
- we offer this tutorial for those who might be interested.
20
+ PLEASE NOTE: we don't recommend using set prediction in binary classification settings,
21
+ even though we offer this tutorial for those who might be interested.
22
22
Instead, we recommend the use of calibration (see more details in the
23
23
Calibration section of the documentation or by using the
24
24
:class:`~sklearn.calibration.CalibratedClassifierCV` proposed by sklearn
34
34
from sklearn .naive_bayes import GaussianNB
35
35
36
36
from numpy .typing import NDArray
37
- from mapie .classification import _MapieClassifier
38
- from mapie .conformity_scores import LACConformityScore
37
+ from mapie .classification import SplitConformalClassifier
38
+ from mapie .utils import train_conformalize_test_split
39
39
from mapie .metrics .classification import (
40
- classification_coverage_score ,
40
+ classification_coverage_score_v2 ,
41
41
classification_mean_width_score ,
42
42
)
43
43
47
47
#
48
48
# We will use MAPIE to estimate a prediction set such that
49
49
# the probability that the true label of a new test point is included in the
50
- # prediction set is always higher than the target confidence level :
51
- # ``1 - α``.
50
+ # prediction set is always higher than the target confidence level.
52
51
# We start by using the softmax score output by the base
53
52
# classifier as the conformity score on a toy two-dimensional dataset.
54
53
# We estimate the prediction sets as follows :
55
54
#
56
- # * First we generate a dataset with train, calibration and test, the model
55
+ # * First we generate a dataset with train, conformalization and test, the model
57
56
# is fitted in the training set.
58
57
#
59
58
# * We set the conformal score ``Sᵢ = 𝑓̂(Xᵢ)ᵧᵢ``
60
59
# from the softmax output of the true class for each sample
61
- # in the calibration set.
60
+ # in the conformity set.
62
61
#
63
62
# * Then we define ``q̂`` as being the
64
63
# ``(n + 1) (1 - α) / n``
87
86
]
88
87
)
89
88
y = np .hstack ([np .full (n_samples , i ) for i in range (n_classes )])
90
- X , X_val , y , y_val = train_test_split (X , y , test_size = 0.5 )
91
- X_train , X_cal , y_train , y_cal = train_test_split (X , y , test_size = 0.3 )
92
- X_c1 , X_c2 , y_c1 , y_c2 = train_test_split (X_cal , y_cal , test_size = 0.5 )
89
+ (X_train , X_conf , X_val ,
90
+ y_train , y_conf , y_val ) = train_conformalize_test_split (
91
+ X , y , train_size = 0.35 , conformalize_size = 0.15 , test_size = 0.5
92
+ )
93
+ X_c1 , X_c2 , y_c1 , y_c2 = train_test_split (X_conf , y_conf , test_size = 0.5 )
93
94
94
95
xx , yy = np .meshgrid (
95
96
np .arange (x_min , x_max , step ), np .arange (x_min , x_max , step )
122
123
# :class:`~sklearn.calibration.CalibratedClassifierCV` proposed by sklearn
123
124
# so that scores can be interpreted as probabilities
124
125
# (see documentation for more information).
125
- # Then we apply :class:`~mapie .classification._MapieClassifier` in the
126
- # calibration data with the LAC conformity_score
126
+ # Then we apply :class:`~mapie_v1 .classification.SplitConformalClassifier` on the
127
+ # conformity data with the LAC conformity score
127
128
# to the estimator indicating that it has already been fitted with
128
- # `cv=" prefit" `.
129
- # We then estimate the prediction sets with differents alpha values with a
130
- # ``fit `` and ``predict`` process.
129
+ # `prefit=True `.
130
+ # We then estimate the prediction sets with different confidence level values with a
131
+ # ``conformalize `` and ``predict`` process.
131
132
132
- clf = GaussianNB ().fit (X_train , y_train )
133
+ clf = GaussianNB ()
134
+ clf .fit (X_train , y_train )
133
135
y_pred = clf .predict (X_test )
134
136
y_pred_proba = clf .predict_proba (X_test )
135
137
y_pred_proba_max = np .max (y_pred_proba , axis = 1 )
136
138
139
+ confidence_level = [0.8 , 0.9 , 0.95 ]
140
+
137
141
calib = CalibratedClassifierCV (
138
142
estimator = clf , method = 'sigmoid' , cv = 'prefit'
139
143
)
140
144
calib .fit (X_c1 , y_c1 )
141
145
142
- mapie_clf = _MapieClassifier (
143
- estimator = calib , cv = ' prefit' , random_state = 42
146
+ mapie_clf = SplitConformalClassifier (
147
+ estimator = calib , confidence_level = confidence_level , prefit = True , random_state = 42
144
148
)
145
- mapie_clf .fit (X_c2 , y_c2 )
149
+ mapie_clf .conformalize (X_c2 , y_c2 )
146
150
147
- alpha = [0.2 , 0.1 , 0.05 ]
148
- y_pred_mapie , y_ps_mapie = mapie_clf .predict (
149
- X_test , alpha = alpha ,
150
- )
151
+ y_pred_mapie , y_ps_mapie = mapie_clf .predict_set (X_test )
151
152
152
153
153
154
##############################################################################
160
161
# conformity score.
161
162
#
162
163
# Let's now visualize the distribution of the conformity scores with the two
163
- # methods with the calculated quantiles for the three alpha values.
164
+ # methods with the calculated quantiles for the three confidence level values.
164
165
165
166
166
167
def plot_scores (
167
- alphas : List [float ],
168
+ confidence_levels : List [float ],
168
169
scores : NDArray ,
169
170
quantiles : NDArray ,
170
- method : str ,
171
+ conformity_score : str ,
171
172
ax : plt .Axes ,
172
173
) -> None :
173
174
colors = {0 : "#1f77b4" , 1 : "#ff7f0e" , 2 : "#2ca02c" }
@@ -180,25 +181,25 @@ def plot_scores(
180
181
ymax = 100 ,
181
182
color = colors [i ],
182
183
linestyles = "dashed" ,
183
- label = f"alpha = { alphas [i ]} " ,
184
+ label = f"confidence_level = { confidence_levels [i ]} " ,
184
185
)
185
186
i = i + 1
186
- ax .set_title (f"Distribution of scores for '{ method } ' method " )
187
+ ax .set_title (f"Distribution of scores for '{ conformity_score } ' conformity score " )
187
188
ax .legend ()
188
189
ax .set_xlabel ("scores" )
189
190
ax .set_ylabel ("count" )
190
191
191
192
192
193
fig , axs = plt .subplots (1 , 1 , figsize = (10 , 5 ))
193
- conformity_scores = mapie_clf .conformity_scores_
194
- quantiles = mapie_clf .conformity_score_function_ .quantiles_
195
- plot_scores (alpha , conformity_scores , quantiles , 'lac' , axs )
194
+ conformity_scores = mapie_clf ._mapie_classifier . conformity_scores_
195
+ quantiles = mapie_clf ._mapie_classifier . conformity_score_function_ .quantiles_
196
+ plot_scores (confidence_level , conformity_scores , quantiles , 'lac' , axs )
196
197
plt .show ()
197
198
198
199
199
200
##############################################################################
200
201
# We will now compare the differences between the prediction sets of the
201
- # different values of alpha .
202
+ # different values of confidence level .
202
203
203
204
def plot_prediction_decision (y_pred_mapie : NDArray , ax ) -> None :
204
205
y_pred_col = list (map (colors .get , y_pred_mapie ))
@@ -221,7 +222,7 @@ def plot_prediction_decision(y_pred_mapie: NDArray, ax) -> None:
221
222
ax .set_title ("Predicted labels" )
222
223
223
224
224
- def plot_prediction_set (y_ps : NDArray , alpha_ : float , ax ) -> None :
225
+ def plot_prediction_set (y_ps : NDArray , confidence_level_ : float , ax ) -> None :
225
226
tab10 = plt .cm .get_cmap ("Purples" , 4 )
226
227
y_pi_sums = y_ps .sum (axis = 1 )
227
228
num_labels = ax .scatter (
@@ -243,22 +244,22 @@ def plot_prediction_set(y_ps: NDArray, alpha_: float, ax) -> None:
243
244
s = 10 ,
244
245
edgecolor = "k" ,
245
246
)
246
- ax .set_title (f"Number of labels for alpha= { alpha_ } " )
247
+ ax .set_title (f"Number of labels for confidence_level = { confidence_level_ } " )
247
248
plt .colorbar (num_labels , ax = ax )
248
249
249
250
250
251
def plot_results (
251
- alphas : List [float ], y_pred_mapie : NDArray , y_ps_mapie : NDArray
252
+ confidence_levels : List [float ], y_pred_mapie : NDArray , y_ps_mapie : NDArray
252
253
) -> None :
253
254
_ , [[ax1 , ax2 ], [ax3 , ax4 ]] = plt .subplots (2 , 2 , figsize = (10 , 10 ))
254
255
axs = {0 : ax1 , 1 : ax2 , 2 : ax3 , 3 : ax4 }
255
256
plot_prediction_decision (y_pred_mapie , axs [0 ])
256
- for i , alpha_ in enumerate (alphas ):
257
- plot_prediction_set (y_ps_mapie [:, :, i ], alpha_ , axs [i + 1 ])
257
+ for i , confidence_level_ in enumerate (confidence_levels ):
258
+ plot_prediction_set (y_ps_mapie [:, :, i ], confidence_level_ , axs [i + 1 ])
258
259
plt .show ()
259
260
260
261
261
- plot_results (alpha , y_pred_mapie , y_ps_mapie )
262
+ plot_results (confidence_level , y_pred_mapie , y_ps_mapie )
262
263
263
264
264
265
##############################################################################
@@ -284,81 +285,99 @@ def plot_results(
284
285
285
286
##############################################################################
286
287
# Let's now compare the effective coverage and the average of prediction set
287
- # widths as function of the ``1 - α `` target coverage.
288
+ # widths as function of the ``confidence_level `` target coverage.
288
289
289
- alpha_ = np .arange (0.02 , 0.98 , 0.02 )
290
+ confidence_level_ = np .arange (0.02 , 0.98 , 0.02 )
290
291
291
292
calib = CalibratedClassifierCV (
292
293
estimator = clf , method = 'sigmoid' , cv = 'prefit'
293
294
)
294
295
calib .fit (X_c1 , y_c1 )
295
296
296
- mapie_clf = _MapieClassifier (
297
- estimator = calib , conformity_score = LACConformityScore (), cv = 'prefit' , random_state = 42
298
- )
299
- mapie_clf . fit ( X_c2 , y_c2 )
300
- _ , y_ps_mapie = mapie_clf . predict (
301
- X , alpha = alpha_
297
+ mapie_clf = SplitConformalClassifier (
298
+ estimator = calib ,
299
+ confidence_level = confidence_level_ ,
300
+ conformity_score = "lac" ,
301
+ prefit = True ,
302
+ random_state = 42
302
303
)
304
+ mapie_clf .conformalize (X_c2 , y_c2 )
305
+ _ , y_ps_mapie = mapie_clf .predict_set (X )
303
306
304
- coverage = np .array ([
305
- classification_coverage_score (y , y_ps_mapie [:, :, i ])
306
- for i , _ in enumerate (alpha_ )
307
- ])
307
+ coverage = classification_coverage_score_v2 (y , y_ps_mapie )
308
308
mean_width = classification_mean_width_score (y_ps_mapie )
309
309
310
310
311
- def plot_coverages_widths (alpha , coverage , width , method ):
312
- quantiles = mapie_clf .conformity_score_function_ .quantiles_
311
+ def plot_coverages_widths (confidence_level , coverage , width , conformity_score ):
312
+ quantiles = mapie_clf ._mapie_classifier . conformity_score_function_ .quantiles_
313
313
_ , axs = plt .subplots (1 , 3 , figsize = (15 , 5 ))
314
- axs [0 ].set_xlabel ("1 - alpha " )
314
+ axs [0 ].set_xlabel ("Confidence level " )
315
315
axs [0 ].set_ylabel ("Quantile" )
316
- axs [0 ].scatter (1 - alpha , quantiles , label = method )
316
+ axs [0 ].scatter (confidence_level , quantiles , label = conformity_score )
317
317
axs [0 ].legend ()
318
- axs [1 ].scatter (1 - alpha , coverage , label = method )
319
- axs [1 ].set_xlabel ("1 - alpha " )
318
+ axs [1 ].scatter (confidence_level , coverage , label = conformity_score )
319
+ axs [1 ].set_xlabel ("Confidence level " )
320
320
axs [1 ].set_ylabel ("Coverage score" )
321
321
axs [1 ].plot ([0 , 1 ], [0 , 1 ], label = "x=y" , color = "black" )
322
322
axs [1 ].legend ()
323
- axs [2 ].scatter (1 - alpha , width , label = method )
324
- axs [2 ].set_xlabel ("1 - alpha " )
323
+ axs [2 ].scatter (confidence_level , width , label = conformity_score )
324
+ axs [2 ].set_xlabel ("Confidence level " )
325
325
axs [2 ].set_ylabel ("Average size of prediction sets" )
326
326
axs [2 ].legend ()
327
327
plt .show ()
328
328
329
329
330
- plot_coverages_widths (alpha_ , coverage , mean_width , 'lac' )
330
+ plot_coverages_widths (confidence_level_ , coverage , mean_width , 'lac' )
331
331
332
332
333
333
##############################################################################
334
334
# It is seen that the method gives coverages close to the target coverages,
335
- # regardless of the ``α`` value.
335
+ # regardless of the ``confidence_level`` value.
336
+
337
+ ##############################################################################
338
+ # Lastly, let us explore how the prediction sets change as a function of different
339
+ # significance levels in identifying a specific range where the prediction
340
+ # sets transition from containing at least one element to being potentially empty.
336
341
337
- alpha_ = np .arange (0.02 , 0.16 , 0.01 )
342
+ confidence_level_ = np .arange (0.99 , 0.85 , - 0.01 )
338
343
339
344
calib = CalibratedClassifierCV (
340
345
estimator = clf , method = 'sigmoid' , cv = 'prefit'
341
346
)
342
347
calib .fit (X_c1 , y_c1 )
343
348
344
- mapie_clf = _MapieClassifier (
345
- estimator = calib , cv = 'prefit' , random_state = 42
346
- )
347
- mapie_clf .fit (X_c2 , y_c2 )
348
- _ , y_ps_mapie = mapie_clf .predict (
349
- X , alpha = alpha_
349
+ mapie_clf = SplitConformalClassifier (
350
+ estimator = calib , confidence_level = confidence_level_ , prefit = True , random_state = 42
350
351
)
352
+ mapie_clf .conformalize (X_c2 , y_c2 )
353
+ _ , y_ps_mapie = mapie_clf .predict_set (X_test )
351
354
352
355
non_empty = np .mean (
353
- np .any (mapie_clf .predict (X_test , alpha = alpha_ )[1 ], axis = 1 ), axis = 0
356
+ np .any (mapie_clf .predict_set (X_test )[1 ], axis = 1 ), axis = 0
354
357
)
355
358
idx = np .argwhere (non_empty < 1 )[0 , 0 ]
356
359
357
360
_ , axs = plt .subplots (1 , 3 , figsize = (15 , 5 ))
358
361
plot_prediction_decision (y_pred_mapie , axs [0 ])
359
- _ , y_ps = mapie_clf .predict (X_test , alpha = alpha_ [idx - 1 ])
360
- plot_prediction_set (y_ps [:, :, 0 ], np .round (alpha_ [idx - 1 ], 3 ), axs [1 ])
361
- _ , y_ps = mapie_clf .predict (X_test , alpha = alpha_ [idx + 1 ])
362
- plot_prediction_set (y_ps [:, :, 0 ], np .round (alpha_ [idx + 1 ], 3 ), axs [2 ])
362
+ _ , y_ps = mapie_clf .predict_set (X_test )
363
+ plot_prediction_set (
364
+ y_ps [:, :, idx - 1 ], np .round (confidence_level_ [idx - 1 ], 3 ), axs [1 ]
365
+ )
366
+ _ , y_ps = mapie_clf .predict_set (X_test )
367
+ plot_prediction_set (
368
+ y_ps [:, :, idx + 1 ], np .round (confidence_level_ [idx + 1 ], 3 ), axs [2 ]
369
+ )
363
370
364
371
plt .show ()
372
+
373
+ ##############################################################################
374
+ # In this section, we adjust the confidence level around the model's accuracy to
375
+ # observe the changes in the sizes of the prediction sets. When the confidence level
376
+ # matches the model's accuracy, we see a shift from potentially empty prediction sets
377
+ # to sets that always contain at least one element.
378
+ # The two plots on the right-hand side illustrate the size of the prediction sets for
379
+ # each test sample just before and after this transition point. In our example, the
380
+ # transition occurs at a confidence_level of 0.89 (i.e., the accuracy of the model).
381
+ # This means that for confidence levels above 0.89, all prediction sets contain at
382
+ # least one element. Conversely, for confidence levels below 0.89, some test samples
383
+ # may have empty prediction sets.
0 commit comments