Skip to content

Commit 1abc9ad

Browse files
authored
MAINT replace obj and grad in sigmoid calibration with private loss module (scikit-learn#27185)
1 parent 8ed0270 commit 1abc9ad

File tree

4 files changed

+30
-21
lines changed

4 files changed

+30
-21
lines changed

bench_calibration.py

Whitespace-only changes.

doc/whats_new/v1.4.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ Changelog
8585
produce large prediction scores. Before it was numerically unstable.
8686
:pr:`26913` by :user:`Omar Salman <OmarManzoor>`.
8787

88+
- |Enhancement| The internal objective and gradient of the `sigmoid` method
89+
of :class:`calibration.CalibratedClassifierCV` have been replaced by the
90+
private loss module. :pr:`27185` by :user:`Omar Salman <OmarManzoor>`.
91+
8892
:mod:`sklearn.cluster`
8993
............................
9094

sklearn/calibration.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
from numbers import Integral, Real
1515

1616
import numpy as np
17-
from scipy.optimize import fmin_bfgs
18-
from scipy.special import expit, xlogy
17+
from scipy.optimize import minimize
18+
from scipy.special import expit
1919

2020
from sklearn.utils import Bunch
2121

22+
from ._loss import HalfBinomialLoss
2223
from .base import (
2324
BaseEstimator,
2425
ClassifierMixin,
@@ -885,29 +886,32 @@ def _sigmoid_calibration(
885886
T = np.zeros_like(y, dtype=np.float64)
886887
T[y > 0] = (prior1 + 1.0) / (prior1 + 2.0)
887888
T[y <= 0] = 1.0 / (prior0 + 2.0)
888-
T1 = 1.0 - T
889889

890-
def objective(AB):
891-
# From Platt (beginning of Section 2.2)
892-
P = expit(-(AB[0] * F + AB[1]))
893-
loss = -(xlogy(T, P) + xlogy(T1, 1.0 - P))
894-
if sample_weight is not None:
895-
return (sample_weight * loss).sum()
896-
else:
897-
return loss.sum()
890+
bin_loss = HalfBinomialLoss()
898891

899-
def grad(AB):
900-
# gradient of the objective function
901-
P = expit(-(AB[0] * F + AB[1]))
902-
TEP_minus_T1P = T - P
903-
if sample_weight is not None:
904-
TEP_minus_T1P *= sample_weight
905-
dA = np.dot(TEP_minus_T1P, F)
906-
dB = np.sum(TEP_minus_T1P)
907-
return np.array([dA, dB])
892+
def loss_grad(AB):
893+
l, g = bin_loss.loss_gradient(
894+
y_true=T,
895+
raw_prediction=-(AB[0] * F + AB[1]),
896+
sample_weight=sample_weight,
897+
)
898+
loss = l.sum()
899+
grad = np.array([-g @ F, -g.sum()])
900+
return loss, grad
908901

909902
AB0 = np.array([0.0, log((prior0 + 1.0) / (prior1 + 1.0))])
910-
AB_ = fmin_bfgs(objective, AB0, fprime=grad, disp=False)
903+
904+
opt_result = minimize(
905+
loss_grad,
906+
AB0,
907+
method="L-BFGS-B",
908+
jac=True,
909+
options={
910+
"gtol": 1e-6,
911+
"ftol": 64 * np.finfo(float).eps,
912+
},
913+
)
914+
AB_ = opt_result.x
911915

912916
# The tuned multiplicative parameter is converted back to the original
913917
# input feature scale. The offset parameter does not need rescaling since

sklearn/tests/test_docstring_parameters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"sklearn.pipeline.make_union",
5757
"sklearn.utils.extmath.safe_sparse_dot",
5858
"sklearn.utils._joblib",
59+
"HalfBinomialLoss",
5960
]
6061

6162
# Methods where y param should be ignored if y=None by default

0 commit comments

Comments
 (0)