Skip to content

Commit ed5f530

Browse files
lakrishLakshmi Krishnanjeremiedbb
authored
FIX OneVsRestClassifier to ensure that predict == argmax(decision_function) (scikit-learn#15504)
Co-authored-by: Lakshmi Krishnan <lakshmik@graphcore.ai> Co-authored-by: Jérémie du Boisberranger <jeremie@probabl.ai>
1 parent 91486d6 commit ed5f530

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Fix tie-breaking behavior in :class:`multiclass.OneVsRestClassifier` to match
2+
`np.argmax` tie-breaking behavior.
3+
By :user:`Lakshmi Krishnan <lakrish>`.

sklearn/multiclass.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,10 +499,12 @@ def predict(self, X):
499499
maxima = np.empty(n_samples, dtype=float)
500500
maxima.fill(-np.inf)
501501
argmaxima = np.zeros(n_samples, dtype=int)
502-
for i, e in enumerate(self.estimators_):
502+
n_classes = len(self.estimators_)
503+
# Iterate in reverse order to match np.argmax tie-breaking behavior
504+
for i, e in enumerate(reversed(self.estimators_)):
503505
pred = _predict_binary(e, X)
504506
np.maximum(maxima, pred, out=maxima)
505-
argmaxima[maxima == pred] = i
507+
argmaxima[maxima == pred] = n_classes - i - 1
506508
return self.classes_[argmaxima]
507509
else:
508510
thresh = _threshold_for_binary_predict(self.estimators_[0])

sklearn/tests/test_multiclass.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,25 @@ def test_check_classification_targets():
8282
check_classification_targets(y)
8383

8484

85+
def test_ovr_ties():
86+
"""Check that ties-breaking matches np.argmax behavior
87+
88+
Non-regression test for issue #14124
89+
"""
90+
91+
class Dummy(BaseEstimator):
92+
def fit(self, X, y):
93+
return self
94+
95+
def decision_function(self, X):
96+
return np.zeros(len(X))
97+
98+
X = np.array([[0], [0], [0], [0]])
99+
y = np.array([0, 1, 2, 3])
100+
clf = OneVsRestClassifier(Dummy()).fit(X, y)
101+
assert_array_equal(clf.predict(X), np.argmax(clf.decision_function(X), axis=1))
102+
103+
85104
def test_ovr_fit_predict():
86105
# A classifier which implements decision_function.
87106
ovr = OneVsRestClassifier(LinearSVC(random_state=0))

0 commit comments

Comments
 (0)