Skip to content

Commit 6e6f11a

Browse files
Update plot_main-tutorial-binary-classification.py to v1 (#684)
1 parent 1035b27 commit 6e6f11a

File tree

1 file changed

+98
-79
lines changed

1 file changed

+98
-79
lines changed

examples/classification/4-tutorials/plot_main-tutorial-binary-classification.py

Lines changed: 98 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
Tutorial 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"
88
method on two-dimensional dataset.
99
1010
Throughout 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.
2222
Instead, we recommend the use of calibration (see more details in the
2323
Calibration section of the documentation or by using the
2424
:class:`~sklearn.calibration.CalibratedClassifierCV` proposed by sklearn
@@ -34,10 +34,10 @@
3434
from sklearn.naive_bayes import GaussianNB
3535

3636
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
3939
from mapie.metrics.classification import (
40-
classification_coverage_score,
40+
classification_coverage_score_v2,
4141
classification_mean_width_score,
4242
)
4343

@@ -47,18 +47,17 @@
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``
@@ -87,9 +86,11 @@
8786
]
8887
)
8988
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)
9394

9495
xx, yy = np.meshgrid(
9596
np.arange(x_min, x_max, step), np.arange(x_min, x_max, step)
@@ -122,32 +123,32 @@
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)
133135
y_pred = clf.predict(X_test)
134136
y_pred_proba = clf.predict_proba(X_test)
135137
y_pred_proba_max = np.max(y_pred_proba, axis=1)
136138

139+
confidence_level = [0.8, 0.9, 0.95]
140+
137141
calib = CalibratedClassifierCV(
138142
estimator=clf, method='sigmoid', cv='prefit'
139143
)
140144
calib.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
##############################################################################
@@ -160,14 +161,14 @@
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

166167
def 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

192193
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)
196197
plt.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

203204
def 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

250251
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
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

291292
calib = CalibratedClassifierCV(
292293
estimator=clf, method='sigmoid', cv='prefit'
293294
)
294295
calib.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)
308308
mean_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

339344
calib = CalibratedClassifierCV(
340345
estimator=clf, method='sigmoid', cv='prefit'
341346
)
342347
calib.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

352355
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
354357
)
355358
idx = np.argwhere(non_empty < 1)[0, 0]
356359

357360
_, axs = plt.subplots(1, 3, figsize=(15, 5))
358361
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+
)
363370

364371
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

Comments
 (0)