33Tutorial for set prediction
44===========================
55
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"
88method on two-dimensional dataset.
99
1010Throughout this tutorial, we will answer the following questions:
1111
1212- How does the number of classes in the prediction sets vary according to
13- the significance level ?
13+ the confidence level?
1414
15- - Is the conformal method well calibrated ?
15+ - Is the conformal method well calibrated?
1616
1717- What are the pros and cons of the set prediction for binary classification
18- in MAPIE ?
18+ in MAPIE?
1919
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.
2222Instead, we recommend the use of calibration (see more details in the
2323Calibration section of the documentation or by using the
2424:class:`~sklearn.calibration.CalibratedClassifierCV` proposed by sklearn
3434from sklearn .naive_bayes import GaussianNB
3535
3636from 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
3939from mapie .metrics .classification import (
40- classification_coverage_score ,
40+ classification_coverage_score_v2 ,
4141 classification_mean_width_score ,
4242)
4343
4747#
4848# We will use MAPIE to estimate a prediction set such that
4949# 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.
5251# We start by using the softmax score output by the base
5352# classifier as the conformity score on a toy two-dimensional dataset.
5453# We estimate the prediction sets as follows :
5554#
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
5756# is fitted in the training set.
5857#
5958# * We set the conformal score ``Sᵢ = 𝑓̂(Xᵢ)ᵧᵢ``
6059# from the softmax output of the true class for each sample
61- # in the calibration set.
60+ # in the conformity set.
6261#
6362# * Then we define ``q̂`` as being the
6463# ``(n + 1) (1 - α) / n``
8786 ]
8887)
8988y = 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 )
9394
9495xx , yy = np .meshgrid (
9596 np .arange (x_min , x_max , step ), np .arange (x_min , x_max , step )
122123# :class:`~sklearn.calibration.CalibratedClassifierCV` proposed by sklearn
123124# so that scores can be interpreted as probabilities
124125# (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
127128# 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.
131132
132- clf = GaussianNB ().fit (X_train , y_train )
133+ clf = GaussianNB ()
134+ clf .fit (X_train , y_train )
133135y_pred = clf .predict (X_test )
134136y_pred_proba = clf .predict_proba (X_test )
135137y_pred_proba_max = np .max (y_pred_proba , axis = 1 )
136138
139+ confidence_level = [0.8 , 0.9 , 0.95 ]
140+
137141calib = CalibratedClassifierCV (
138142 estimator = clf , method = 'sigmoid' , cv = 'prefit'
139143)
140144calib .fit (X_c1 , y_c1 )
141145
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
144148)
145- mapie_clf .fit (X_c2 , y_c2 )
149+ mapie_clf .conformalize (X_c2 , y_c2 )
146150
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 )
151152
152153
153154##############################################################################
160161# conformity score.
161162#
162163# 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.
164165
165166
166167def plot_scores (
167- alphas : List [float ],
168+ confidence_levels : List [float ],
168169 scores : NDArray ,
169170 quantiles : NDArray ,
170- method : str ,
171+ conformity_score : str ,
171172 ax : plt .Axes ,
172173) -> None :
173174 colors = {0 : "#1f77b4" , 1 : "#ff7f0e" , 2 : "#2ca02c" }
@@ -180,25 +181,25 @@ def plot_scores(
180181 ymax = 100 ,
181182 color = colors [i ],
182183 linestyles = "dashed" ,
183- label = f"alpha = { alphas [i ]} " ,
184+ label = f"confidence_level = { confidence_levels [i ]} " ,
184185 )
185186 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 " )
187188 ax .legend ()
188189 ax .set_xlabel ("scores" )
189190 ax .set_ylabel ("count" )
190191
191192
192193fig , 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 )
196197plt .show ()
197198
198199
199200##############################################################################
200201# We will now compare the differences between the prediction sets of the
201- # different values of alpha .
202+ # different values of confidence level .
202203
203204def plot_prediction_decision (y_pred_mapie : NDArray , ax ) -> None :
204205 y_pred_col = list (map (colors .get , y_pred_mapie ))
@@ -221,7 +222,7 @@ def plot_prediction_decision(y_pred_mapie: NDArray, ax) -> None:
221222 ax .set_title ("Predicted labels" )
222223
223224
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 :
225226 tab10 = plt .cm .get_cmap ("Purples" , 4 )
226227 y_pi_sums = y_ps .sum (axis = 1 )
227228 num_labels = ax .scatter (
@@ -243,22 +244,22 @@ def plot_prediction_set(y_ps: NDArray, alpha_: float, ax) -> None:
243244 s = 10 ,
244245 edgecolor = "k" ,
245246 )
246- ax .set_title (f"Number of labels for alpha= { alpha_ } " )
247+ ax .set_title (f"Number of labels for confidence_level = { confidence_level_ } " )
247248 plt .colorbar (num_labels , ax = ax )
248249
249250
250251def 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
252253) -> None :
253254 _ , [[ax1 , ax2 ], [ax3 , ax4 ]] = plt .subplots (2 , 2 , figsize = (10 , 10 ))
254255 axs = {0 : ax1 , 1 : ax2 , 2 : ax3 , 3 : ax4 }
255256 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 ])
258259 plt .show ()
259260
260261
261- plot_results (alpha , y_pred_mapie , y_ps_mapie )
262+ plot_results (confidence_level , y_pred_mapie , y_ps_mapie )
262263
263264
264265##############################################################################
@@ -284,81 +285,99 @@ def plot_results(
284285
285286##############################################################################
286287# 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.
288289
289- alpha_ = np .arange (0.02 , 0.98 , 0.02 )
290+ confidence_level_ = np .arange (0.02 , 0.98 , 0.02 )
290291
291292calib = CalibratedClassifierCV (
292293 estimator = clf , method = 'sigmoid' , cv = 'prefit'
293294)
294295calib .fit (X_c1 , y_c1 )
295296
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
302303)
304+ mapie_clf .conformalize (X_c2 , y_c2 )
305+ _ , y_ps_mapie = mapie_clf .predict_set (X )
303306
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 )
308308mean_width = classification_mean_width_score (y_ps_mapie )
309309
310310
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_
313313 _ , axs = plt .subplots (1 , 3 , figsize = (15 , 5 ))
314- axs [0 ].set_xlabel ("1 - alpha " )
314+ axs [0 ].set_xlabel ("Confidence level " )
315315 axs [0 ].set_ylabel ("Quantile" )
316- axs [0 ].scatter (1 - alpha , quantiles , label = method )
316+ axs [0 ].scatter (confidence_level , quantiles , label = conformity_score )
317317 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 " )
320320 axs [1 ].set_ylabel ("Coverage score" )
321321 axs [1 ].plot ([0 , 1 ], [0 , 1 ], label = "x=y" , color = "black" )
322322 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 " )
325325 axs [2 ].set_ylabel ("Average size of prediction sets" )
326326 axs [2 ].legend ()
327327 plt .show ()
328328
329329
330- plot_coverages_widths (alpha_ , coverage , mean_width , 'lac' )
330+ plot_coverages_widths (confidence_level_ , coverage , mean_width , 'lac' )
331331
332332
333333##############################################################################
334334# 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.
336341
337- alpha_ = np .arange (0.02 , 0.16 , 0.01 )
342+ confidence_level_ = np .arange (0.99 , 0.85 , - 0.01 )
338343
339344calib = CalibratedClassifierCV (
340345 estimator = clf , method = 'sigmoid' , cv = 'prefit'
341346)
342347calib .fit (X_c1 , y_c1 )
343348
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
350351)
352+ mapie_clf .conformalize (X_c2 , y_c2 )
353+ _ , y_ps_mapie = mapie_clf .predict_set (X_test )
351354
352355non_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
354357)
355358idx = np .argwhere (non_empty < 1 )[0 , 0 ]
356359
357360_ , axs = plt .subplots (1 , 3 , figsize = (15 , 5 ))
358361plot_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+ )
363370
364371plt .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