|
11 | 11 | The model with the set of parameters that gives the best score is then used in |
12 | 12 | MAPIE to estimate the prediction intervals associated with the predictions. |
13 | 13 | A limitation of this method is that residuals used by MAPIE are computed on |
14 | | -the validation dataset, which can be subject to overfitting as far as hyperparameter |
15 | | -tuning is concerned. |
| 14 | +the validation dataset, which can be subject to overfitting as far as |
| 15 | +hyperparameter tuning is concerned. |
16 | 16 | This fools MAPIE into being slightly too optimistic with confidence intervals. |
17 | 17 |
|
18 | 18 | To solve this problem, an alternative option is to perform a nested |
19 | 19 | cross-validation parameter search directly within the MAPIE estimator on each |
20 | 20 | *out-of-fold* dataset. |
21 | | -For each testing fold used by MAPIE to store residuals, an internal cross-validation |
22 | | -occurs on the training fold, optimizing hyperparameters. |
23 | | -This ensures that residuals seen by MAPIE are never seen by the algorithm beforehand. |
24 | | -However, this method is much computationally heavier since it results in |
25 | | -:math:`N * P` calculations, where *N* is the number of *out-of-fold* |
26 | | -models and *P* the number of parameter search iterations, versus :math:`N + P` |
27 | | -for the non-nested approach. |
| 21 | +For each testing fold used by MAPIE to store residuals, an internal |
| 22 | +cross-validation occurs on the training fold, optimizing hyperparameters. |
| 23 | +This ensures that residuals seen by MAPIE are never seen by the algorithm |
| 24 | +beforehand. However, this method is much computationally heavier since |
| 25 | +it results in :math:`N * P` calculations, where *N* is the number of |
| 26 | +*out-of-fold* models and *P* the number of parameter search iterations, |
| 27 | +versus :math:`N + P` for the non-nested approach. |
28 | 28 |
|
29 | 29 | Here, we compare the two strategies on the Boston dataset. We use the Random |
30 | | -Forest Regressor as a base regressor for the CV+ strategy. For the sake of light |
31 | | -computation, we adopt a RandomizedSearchCV parameter search strategy with a low |
32 | | -number of iterations and with a reproducible random state. |
| 30 | +Forest Regressor as a base regressor for the CV+ strategy. For the sake of |
| 31 | +light computation, we adopt a RandomizedSearchCV parameter search strategy |
| 32 | +with a low number of iterations and with a reproducible random state. |
33 | 33 |
|
34 | | -The two approaches give slightly different predictions with the nested CV approach |
35 | | -estimating slightly larger prediction interval widths by a few percents at most (apart from a |
36 | | -handful of exceptions). |
| 34 | +The two approaches give slightly different predictions with the nested CV |
| 35 | +approach estimating slightly larger prediction interval widths by a |
| 36 | +few percents at most (apart from a handful of exceptions). |
37 | 37 |
|
38 | | -For this example, the two approaches result in identical scores and identical effective |
39 | | -coverages. |
| 38 | +For this example, the two approaches result in identical scores and identical |
| 39 | +effective coverages. |
40 | 40 |
|
41 | | -In the general case, the recommended approach is to use nested cross-validation, since it |
42 | | -does not underestimate residuals and hence prediction intervals. However, in this particular |
43 | | -example, effective coverages of both nested and non-nested methods are the same. |
| 41 | +In the general case, the recommended approach is to use nested |
| 42 | +cross-validation, since it does not underestimate residuals and hence |
| 43 | +prediction intervals. However, in this particular example, effective |
| 44 | +coverages of both nested and non-nested methods are the same. |
44 | 45 | """ |
45 | 46 |
|
46 | 47 | import matplotlib.pyplot as plt |
|
98 | 99 | mapie_non_nested.fit(X_train, y_train) |
99 | 100 | y_preds_non_nested = mapie_non_nested.predict(X_test)[:, :, 0] |
100 | 101 | widths_non_nested = y_preds_non_nested[:, 2] - y_preds_non_nested[:, 1] |
101 | | -coverage_non_nested = coverage_score(y_test, y_preds_non_nested[:, 1], y_preds_non_nested[:, 2]) |
102 | | -score_non_nested = mean_squared_error(y_test, y_preds_non_nested[:, 0], squared=False) |
| 102 | +coverage_non_nested = coverage_score( |
| 103 | + y_test, y_preds_non_nested[:, 1], y_preds_non_nested[:, 2] |
| 104 | +) |
| 105 | +score_non_nested = mean_squared_error( |
| 106 | + y_test, y_preds_non_nested[:, 0], squared=False |
| 107 | +) |
103 | 108 |
|
104 | 109 | # Nested approach with the CV+ strategy using the Random Forest model. |
105 | 110 | cv_obj = RandomizedSearchCV( |
|
123 | 128 | mapie_nested.fit(X_train, y_train) |
124 | 129 | y_preds_nested = mapie_nested.predict(X_test)[:, :, 0] |
125 | 130 | widths_nested = y_preds_nested[:, 2] - y_preds_nested[:, 1] |
126 | | -coverage_nested = coverage_score(y_test, y_preds_nested[:, 1], y_preds_nested[:, 2]) |
| 131 | +coverage_nested = coverage_score( |
| 132 | + y_test, y_preds_nested[:, 1], y_preds_nested[:, 2] |
| 133 | +) |
127 | 134 | score_nested = mean_squared_error(y_test, y_preds_nested[:, 0], squared=False) |
128 | 135 |
|
129 | 136 | # Print scores and effective coverages. |
130 | | -print("Scores and effective coverages for the CV+ strategy using the Random Forest model.") |
| 137 | +print( |
| 138 | + "Scores and effective coverages for the CV+ strategy using the " |
| 139 | + "Random Forest model." |
| 140 | +) |
131 | 141 | print( |
132 | 142 | "Score on the test set for the non-nested and nested CV approaches: ", |
133 | 143 | f"{score_non_nested: .3f}, {score_nested: .3f}" |
134 | 144 | ) |
135 | 145 | print( |
136 | | - "Effective coverage on the test set for the non-nested and nested CV approaches: ", |
| 146 | + "Effective coverage on the test set for the non-nested " |
| 147 | + "and nested CV approaches: ", |
137 | 148 | f"{coverage_non_nested: .3f}, {coverage_nested: .3f}" |
138 | 149 | ) |
139 | 150 |
|
|
146 | 157 | ax1.set_ylim([min_x, max_x]) |
147 | 158 | ax1.scatter(widths_nested, widths_non_nested) |
148 | 159 | ax1.plot([min_x, max_x], [min_x, max_x], ls="--", color="k") |
149 | | -ax2.set_xlabel("[width(non-nested CV) - width(nested CV)] / width(non-nested CV)") |
| 160 | +ax2.set_xlabel( |
| 161 | + "[width(non-nested CV) - width(nested CV)] / width(non-nested CV)" |
| 162 | +) |
150 | 163 | ax2.set_ylabel("Counts") |
151 | 164 | ax2.hist((widths_non_nested - widths_nested)/widths_non_nested, bins=15) |
152 | 165 | plt.show() |
0 commit comments