The Wayback Machine - https://web.archive.org/web/20210824062120/https://github.com/scikit-learn/scikit-learn/pull/18114
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX select the probability estimates or transform the decision values when pos_label is provided #18114

Merged
merged 32 commits into from Oct 9, 2020

Conversation

@glemaitre
Copy link
Contributor

@glemaitre glemaitre commented Aug 7, 2020

Solve a bug where the appropriate column of the probability estimates was not selected or that the decision values where not inversed when pos_label is passed and that it does not correspond to clf.classes_[1].

@glemaitre
Copy link
Contributor Author

@glemaitre glemaitre commented Aug 7, 2020

@jnothman @thomasjpfan OK so I think this is the only fixes required.
The tests are failing in master. It would have involved that someone creating a scorer based on average position with pos_label and string labels with an ordering where the positive label would have been clf.classes_[0] would have get something wrong.
In a GridSearchCV, you would have potentially optimized the opposite of what you would expect.

Most probably, nobody encountered this issue :)

@glemaitre
Copy link
Contributor Author

@glemaitre glemaitre commented Aug 7, 2020

Once this PR is merged, I plan to make another one with either an entry in the user guide or an example to show how to pass the pos_label and what could be the drawback if you don't do it.

@glemaitre
Copy link
Contributor Author

@glemaitre glemaitre commented Aug 7, 2020

ping @ogrisel as well.

Copy link
Member

@thomasjpfan thomasjpfan left a comment

This PR would handle the case where there is a mismatch between pos_label and estimator.classes_[1].

I keep on wondering if this behavior of selecting the column would be surprising to a user.

sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
Copy link
Member

@ogrisel ogrisel left a comment

We might also have a similar problem with non-symmetric scorers that take hard class predictions such as f1 score for instance, no?

sklearn/metrics/tests/test_score_objects.py Outdated Show resolved Hide resolved
@glemaitre
Copy link
Contributor Author

@glemaitre glemaitre commented Aug 11, 2020

We might also have a similar problem with non-symmetric scorers that take hard class predictions such as f1 score for instance, no?

Since we don't need to select a column or inverse the decision, we should be safe. But I will add a test.

Copy link
Member

@ogrisel ogrisel left a comment

Some more review comments:

sklearn/metrics/tests/test_score_objects.py Outdated Show resolved Hide resolved
doc/whats_new/v0.24.rst Outdated Show resolved Hide resolved
sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
# provided.
raise ValueError(err_msg)
elif not support_multi_class:
raise ValueError(err_msg)

This comment has been minimized.

@ogrisel

ogrisel Aug 11, 2020
Member

I do not understand this. In both cases it returns the same message.

Also, what happens when support_multi_class and y_pred.shape[1] != 1?

This comment has been minimized.

@glemaitre

glemaitre Aug 18, 2020
Author Contributor

It was what was intended in the past: https://github.com/scikit-learn/scikit-learn/pull/18114/files#diff-e907207584273858caf088b432e38d04L243-L247

Also, what happens when support_multi_class and y_pred.shape[1] != 1

It is a case where y_true is encoded as binary but that the y_pred` is multi-class. It is a case supported by ROC-AUC apparently and I needed to keep it like this for backward compatibility.

I am not sure that there is a better way of inferring the exact encoding in this case.

This comment has been minimized.

@glemaitre

glemaitre Sep 2, 2020
Author Contributor

I added an example to be more specific. I think that we should investigate if we can pass labels to type_of_target which could be an optional argument given when we are using it in the metrics.

sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
Copy link
Member

@thomasjpfan thomasjpfan left a comment

Another pass

sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
Copy link
Member

@thomasjpfan thomasjpfan left a comment

LGTM

sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
Copy link
Member

@ogrisel ogrisel left a comment

I am still very much confused by the multiclass case. Could you please add tests for multiclass classification problem with proba and threshold scorers with string labels? Maybe that would clear the confusion and help me complete this review.

sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
sklearn/metrics/tests/test_score_objects.py Outdated Show resolved Hide resolved
# [0. , 0.2 , 0.8 ]])
# roc_auc_score(
# y_true, y_score, labels=[0, 1, 2], multi_class='ovo'
# )

This comment has been minimized.

@ogrisel

ogrisel Sep 9, 2020
Member

I still do not understand this condition. This comment refers to the metric function outside of the scorer API.

I assume that this is related to to roc_auc_ovo_scorer which is a _ProbaScorer instance and this condition is about raising a ValueError when y_pred.shape[1] == 1 for some reason but I really do not see how this relates to the example you give here as y_pred has 3 columns in this example so it does not match the case of the condition.

This comment has been minimized.

@thomasjpfan

thomasjpfan Sep 9, 2020
Member

I think this is trying to keep the logic from:

elif y_pred.shape[1] == 1: # not multiclass

which I added in #15274. This was added because we can have a binary y_true with an estimator trained on > 2 classes.

y_pred.shape[1] == 1 was use to mean y_pred came from a classifier with only one class. The check for the shape of y_pred was added here: 94db3d9

This comment has been minimized.

@glemaitre

glemaitre Oct 5, 2020
Author Contributor

But in scikit-learn we cannot have a classifier train with a single class, isn't it?

This comment has been minimized.

@thomasjpfan

thomasjpfan Oct 5, 2020
Member

It is strange, but it can happen:

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_blobs
import numpy as np

X, y = make_blobs(random_state=0, centers=2)
clf = DecisionTreeClassifier()
clf.fit(X, np.zeros_like(y))

clf.classes_
# array([0])

This comment has been minimized.

@glemaitre

glemaitre Oct 5, 2020
Author Contributor

Uhm. Then, I am confused with check_classifiers_one_label :) I have to check. Anyway, I think that the last change make everything more explicit.

This comment has been minimized.

@glemaitre

glemaitre Oct 5, 2020
Author Contributor

Oh I see a must_pass in the raises. It would be much more explicit to have tag for that I think: accept_single_label for instance.

sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
sklearn/metrics/_scorer.py Outdated Show resolved Hide resolved
@rth rth self-requested a review Sep 23, 2020
Copy link
Member

@thomasjpfan thomasjpfan left a comment

Otherwise LGTM

sklearn/metrics/tests/test_score_objects.py Outdated Show resolved Hide resolved
@ogrisel
ogrisel approved these changes Oct 9, 2020
Copy link
Member

@ogrisel ogrisel left a comment

The code is much better organized that previous versions of this PR and the tests are great. Thanks very much for the fix @glemaitre.

@ogrisel ogrisel merged commit 193670c into scikit-learn:master Oct 9, 2020
20 checks passed
20 checks passed
@github-actions
triage
Details
@lgtm-com
LGTM analysis: C/C++ No code changes detected
Details
@lgtm-com
LGTM analysis: JavaScript No code changes detected
Details
@lgtm-com
LGTM analysis: Python No new or fixed alerts
Details
ci/circleci: deploy Your tests passed on CircleCI!
Details
ci/circleci: doc Your tests passed on CircleCI!
Details
@circleci-artifacts-redirector
ci/circleci: doc artifact Link to 0/doc/_changed.html
Details
ci/circleci: doc-min-dependencies Your tests passed on CircleCI!
Details
ci/circleci: lint Your tests passed on CircleCI!
Details
@azure-pipelines
scikit-learn.scikit-learn Build #20201008.8 succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Linting) Linting succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Linux py36_conda_openblas) Linux py36_conda_openblas succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Linux py36_ubuntu_atlas) Linux py36_ubuntu_atlas succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Linux pylatest_pip_openblas_pandas) Linux pylatest_pip_openblas_pandas succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Linux32 py36_ubuntu_atlas_32bit) Linux32 py36_ubuntu_atlas_32bit succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Linux_Runs pylatest_conda_mkl) Linux_Runs pylatest_conda_mkl succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Windows py36_pip_openblas_32bit) Windows py36_pip_openblas_32bit succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (Windows py37_conda_mkl) Windows py37_conda_mkl succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (macOS pylatest_conda_mkl) macOS pylatest_conda_mkl succeeded
Details
@azure-pipelines
scikit-learn.scikit-learn (macOS pylatest_conda_mkl_no_openmp) macOS pylatest_conda_mkl_no_openmp succeeded
Details
amrcode added a commit to amrcode/scikit-learn that referenced this pull request Oct 19, 2020
jayzed82 added a commit to jayzed82/scikit-learn that referenced this pull request Oct 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

4 participants