Skip to content

Commit 7d1d968

Browse files
FEA add temperature scaling to CalibratedClassifierCV (scikit-learn#31068)
Co-authored-by: Christian Lorentzen <lorentzen.ch@gmail.com>
1 parent e8ab263 commit 7d1d968

5 files changed

Lines changed: 501 additions & 81 deletions

File tree

doc/modules/calibration.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,35 @@ probabilities, the calibrated probabilities for each class
276276
are predicted separately. As those probabilities do not necessarily sum to
277277
one, a postprocessing is performed to normalize them.
278278

279+
On the other hand, temperature scaling naturally supports multiclass
280+
predictions by working with logits and finally applying the softmax function.
281+
282+
Temperature Scaling
283+
^^^^^^^^^^^^^^^^^^^
284+
285+
For a multi-class classification problem with :math:`n` classes, temperature scaling
286+
[9]_, `method="temperature"`, produces class probabilities by modifying the softmax
287+
function with a temperature parameter :math:`T`:
288+
289+
.. math::
290+
\mathrm{softmax}\left(\frac{z}{T}\right) \,,
291+
292+
where, for a given sample, :math:`z` is the vector of logits for each class as predicted
293+
by the estimator to be calibrated. In terms of scikit-learn's API, this corresponds to
294+
the output of :term:`decision_function` or to the logarithm of :term:`predict_proba`.
295+
Probabilities are converted to logits by first adding a tiny positive constant to avoid
296+
numerical issues with logarithm of zero, and then applying the natural logarithm.
297+
298+
The parameter :math:`T` is learned by minimizing :func:`~sklearn.metrics.log_loss`,
299+
i.e. cross-entropy loss, on a hold-out (calibration) set. Note that :math:`T` does not
300+
affect the location of the maximum in the softmax output. Therefore, temperature scaling
301+
does not alter the accuracy of the calibrating estimator.
302+
303+
The main advantage of temperature scaling over other calibration methods is that it
304+
provides a natural way to obtain (better) calibrated multi-class probabilities with
305+
just one free parameter in contrast to using a "One-vs-Rest" scheme that adds more
306+
parameters for each single class.
307+
279308
.. rubric:: Examples
280309

281310
* :ref:`sphx_glr_auto_examples_calibration_plot_calibration_curve.py`
@@ -324,3 +353,7 @@ one, a postprocessing is performed to normalize them.
324353
:doi:`"Statistical Foundations of Actuarial Learning and its Applications"
325354
<10.1007/978-3-031-12409-9>`
326355
Springer Actuarial
356+
357+
.. [9] `On Calibration of Modern Neural Networks
358+
<https://proceedings.mlr.press/v70/guo17a/guo17a.pdf>`_,
359+
C. Guo, G. Pleiss, Y. Sun, & K. Q. Weinberger, ICML 2017.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Added temperature scaling in :class:`calibration.CalibratedClassifierCV`.
2+
By :user:`Virgil Chan <virchan>`.

sklearn/_loss/loss.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,20 @@ def constant_to_optimal_zero(self, y_true, sample_weight=None):
457457
"""Calculate term dropped in loss.
458458
459459
With this term added, the loss of perfect predictions is zero.
460+
461+
Parameters
462+
----------
463+
y_true : array-like of shape (n_samples,)
464+
Observed, true target values.
465+
466+
sample_weight : None or array of shape (n_samples,), default=None
467+
Sample weights.
468+
469+
Returns
470+
-------
471+
constant : ndarray of shape (n_samples,)
472+
Constant value to be added to raw predictions so that the loss
473+
of perfect predictions becomes zero.
460474
"""
461475
return np.zeros_like(y_true)
462476

@@ -982,8 +996,16 @@ class HalfMultinomialLoss(BaseLoss):
982996
classes: If the full hessian for classes k and l and sample i is H_i_k_l,
983997
we calculate H_i_k_k, i.e. k=l.
984998
985-
Reference
986-
---------
999+
Parameters
1000+
----------
1001+
sample_weight : {None, ndarray}
1002+
If sample_weight is None, the hessian might be constant.
1003+
1004+
n_classes : {None, int}
1005+
The number of classes for classification, else None.
1006+
1007+
References
1008+
----------
9871009
.. [1] :arxiv:`Simon, Noah, J. Friedman and T. Hastie.
9881010
"A Blockwise Descent Algorithm for Group-penalized Multiresponse and
9891011
Multinomial Regression".
@@ -1015,6 +1037,19 @@ def fit_intercept_only(self, y_true, sample_weight=None):
10151037
10161038
This is the softmax of the weighted average of the target, i.e. over
10171039
the samples axis=0.
1040+
1041+
Parameters
1042+
----------
1043+
y_true : array-like of shape (n_samples,)
1044+
Observed, true target values.
1045+
1046+
sample_weight : None or array of shape (n_samples,), default=None
1047+
Sample weights.
1048+
1049+
Returns
1050+
-------
1051+
raw_prediction : numpy scalar or array of shape (n_classes,)
1052+
Raw predictions of an intercept-only model.
10181053
"""
10191054
out = np.zeros(self.n_classes, dtype=y_true.dtype)
10201055
eps = np.finfo(y_true.dtype).eps

0 commit comments

Comments
 (0)