8000 FIX Uses self.scoring for score function (#11192) · scikit-learn/scikit-learn@c75bccf · GitHub
[go: up one dir, main page]

Skip to content

Commit c75bccf

Browse files
thomasjpfanqinhanmin2014
authored andcommitted
FIX Uses self.scoring for score function (#11192)
1 parent 56dc374 commit c75bccf

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

doc/whats_new/v0.20.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,11 @@ Classifiers and regressors
332332
returning incorrect probabilities in the case of binary outcomes.
333333
:issue:`9939` by :user:`Roger Westover <rwolst>`.
334334

335+
- Fixed a bug in :class:`linear_model.LogisticRegressionCV` where the
336+
``score`` method always computes accuracy, not the metric given by
337+
the ``scoring`` parameter.
338+
:issue:`10998` by :user:`Thomas Fan <thomasjpfan>`.
339+
335340
- Fixed a bug in :class:`linear_model.OrthogonalMatchingPursuit` that was
336341
broken when setting ``normalize=False``.
337342
:issue:`10071` by `Alexandre Gramfort`_.

sklearn/linear_model/logistic.py

Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
from ..utils.fixes import logsumexp
3030
from ..utils.optimize import newton_cg
3131
from ..utils.validation import check_X_y
32-
from ..exceptions import NotFittedError, ConvergenceWarning
32+
from..exceptions import (NotFittedError, ConvergenceWarning,
33+
ChangedBehaviorWarning)
3334
from ..utils.multiclass import check_classification_targets
3435
from ..externals.joblib import Parallel, delayed
3536
from ..model_selection import check_cv
@@ -1789,3 +1790,37 @@ def fit(self, X, y, sample_weight=None):
17891790

17901791
self.C_ = np.asarray(self.C_)
17911792
return self
1793+
1794+
def score(self, X, y, sample_weight=None):
1795+
"""Returns the score using the `scoring` option on the given
1796+
test data and labels.
1797+
1798+
Parameters
1799+
----------
1800+
X : array-like, shape = (n_samples, n_features)
1801+
Test samples.
1802+
1803+
y : array-like, shape = (n_samples,)
1804+
True labels for X.
1805+
1806+
sample_weight : array-like, shape = [n_samples], optional
1807+
Sample weights.
1808+
1809+
Returns
1810+
-------
1811+
score : float
1812+
Score of self.predict(X) wrt. y.
1813+
1814+
"""
1815+
1816+
if self.scoring is not None:
1817+
warnings.warn("The long-standing behavior to use the "
1818+
"accuracy score has changed. The scoring "
1819+
"parameter is now used. "
1820+
"This warning will disappear in version 0.22.",
1821+
ChangedBehaviorWarning)
1822+
scoring = self.scoring or 'accuracy'
1823+
if isinstance(scoring, six.string_types):
1824+
scoring = get_scorer(scoring)
1825+
1826+
return scoring(self, X, y, sample_weight=sample_weight)
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from sklearn.utils.testing import assert_warns_message
2323

2424
from sklearn.exceptions import ConvergenceWarning
25+
from sklearn.exceptions import ChangedBehaviorWarning
2526
from sklearn.linear_model.logistic import (
2627
LogisticRegression,
2728
logistic_regression_path, LogisticRegressionCV,
@@ -92,6 +93,49 @@ def test_error():
9293
assert_raise_message(ValueError, msg, LR(max_iter="test").fit, X, Y1)
9394

9495

96+
def test_logistic_cv_mock_scorer():
97+
98+
class MockScorer(object):
99+
def __init__(self):
100+
self.calls = 0
101+
self.scores = [0.1, 0.4, 0.8, 0.5]
102+
103+
def __call__(self, model, X, y, sample_weight=None):
104+
score = self.scores[self.calls % len(self.scores)]
105+
self.calls += 1
106+
return score
107+
108+
mock_scorer = MockScorer()
109+
Cs = [1, 2, 3, 4]
110+
cv = 2
111+
112+
lr = LogisticRegressionCV(Cs=Cs, scoring=mock_scorer, cv=cv)
113+
lr.fit(X, Y1)
114+
115+
# Cs[2] has the highest score (0.8) from MockScorer
116+
assert lr.C_[0] == Cs[2]
117+
118+
# scorer called 8 times (cv*len(Cs))
119+
assert mock_scorer.calls == cv * len(Cs)
120+
121+
# reset mock_scorer
122+
mock_scorer.calls = 0
123+
with pytest.warns(ChangedBehaviorWarning):
124+
custom_score = lr.score(X, lr.predict(X))
125+
126+
assert custom_score == mock_scorer.scores[0]
127+
assert mock_scorer.calls == 1
128+
129+
130+
def test_logistic_cv_score_does_not_warn_by_default():
131+
lr = LogisticRegressionCV(cv=2)
132+
lr.fit(X, Y1)
133+
134+
with pytest.warns(None) as record:
135+
lr.score(X, lr.predict(X))
136+
assert len(record) == 0
137+
138+
95139
def test_lr_liblinear_warning():
96140
n_samples, n_features = iris.data.shape
97141
target = iris.target_names[iris.target]