Thanks to visit codestin.com
Credit goes to github.com

Skip to content

FIX accept multilabel-indicator in _get_response_values - #27002

Merged
adrinjalali merged 39 commits into
scikit-learn:mainfrom
glemaitre:is/26817
Sep 17, 2023
Merged

FIX accept multilabel-indicator in _get_response_values#27002
adrinjalali merged 39 commits into
scikit-learn:mainfrom
glemaitre:is/26817

Conversation

@glemaitre

Copy link
Copy Markdown
Member

closes #26817

Accept "multilabel-indicator" in _get_response_values.

@github-actions

github-actions Bot commented Aug 3, 2023

Copy link
Copy Markdown

✔️ Linting Passed

All linting checks passed. Your pull request is in excellent shape! ☀️

Generated for commit: 8aeb8b4. Link to the linter CI: here

@glemaitre

Copy link
Copy Markdown
Member Author

pinging @adrinjalali @thomasjpfan @betatim

I think this is ready for a review with a much shorter diff only for the initial bug.

@glemaitre glemaitre added this to the 1.3.1 milestone Aug 3, 2023

@thomasjpfan thomasjpfan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Comment thread sklearn/utils/_response.py Outdated
Comment thread sklearn/utils/_response.py Outdated
Comment thread sklearn/utils/_response.py Outdated
Comment thread sklearn/utils/_response.py Outdated
if pos_label == classes[0]:
y_pred *= -1
elif target_type == "multilabel-indicator" and isinstance(y_pred, list):
y_pred = np.vstack([p for p in y_pred]).T

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, this is the same story. However, I don't recall any estimator that would return a list currently.

Comment thread sklearn/utils/_response.py Outdated

@adrinjalali adrinjalali left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like there are things in your head which are not expressed here, and they would help me review this PR better 😁

@@ -72,15 +73,15 @@ def _get_response_values(
if is_classifier(estimator):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think this whole block could be simplified / explained? I have a hard time following all these different code paths, and trying to figure out if they actually cover all possible input types, and realized this is kind of a symptom of how this is written.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure where to put the cursor here. Mainly, the issue is that we need a compressed format (n_samples,) or (n_samples, n_outputs) (i.e. at least our metrics need to). So we leverage pos_label to provide the correct array.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created new function and try to better explain what we intend to do.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOVE the new implementation.

@jeremiedbb jeremiedbb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the refactoring into smaller dedicated functions and their description. It makes the code a lot easier to follow imo. I just have a few nitpicks, otherwise LGTM.

Comment thread sklearn/metrics/tests/test_score_objects.py Outdated
Comment thread sklearn/utils/_response.py Outdated
Comment thread sklearn/utils/_response.py Outdated
Comment thread doc/whats_new/v1.3.rst Outdated
Comment thread sklearn/cluster/tests/test_bisect_k_means.py
Comment thread sklearn/utils/_response.py Outdated

@adrinjalali adrinjalali left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a really nice refactor now. LGTM other than the small points.

Comment on lines +44 to +46
y_pred : ndarray of shape (n_samples,), (n_samples, n_classes) or \
(n_samples, n_output)
Compressed predictions format as requested by the metrics.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kinda wondering why this is not our output in the fist place 😁

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, I think this is dependent on the estimator and does it naturally or not output values.

Comment on lines +48 to +53
if target_type == "binary" and y_pred.shape[1] < 2:
# We don't handle classifiers trained on a single class.
raise ValueError(
f"Got predict_proba of shape {y_pred.shape}, but need "
"classifier with two classes."
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can imagine this happening in cases where we do cross validation or grid search, I feel like we shouldn't be raising.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall that this code was actually here in previous iteration before the refactoring. Looking a bit more, I don't think that all estimators support this one in scikit-learn (e.g. LogisticRegression does not for instance).

In terms of predict_proba, I could potentially make a PR that handle this case: we need to returns the probability in regards of pos_label to be consistent with estimator.classes_.

In short, we should have: y_pred.ravel() if pos_label == estimator.classes_[0] else np.abs(1 - y_pred.ravel()).

Comment thread sklearn/utils/_response.py Outdated
Comment on lines +71 to +72
In the binary case, it should invert the sign of the score if the positive label
is not `classes[1]`. In the multi-label case, it should stack the predictions if

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this inversion of the sign is making me very uncomfortable, will need some time to see if it makes things explode in other places. Can you help me here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the sign inversion and selection selection of the columns is indeed part of bug fixes that we got since a couple of release now :).

You you change your pos_label then you flip the hyperplane. In terms of binary probability, it comes to take the other columns and therefore the 1 - y_proba. Both, are consistent.

Comment thread sklearn/utils/_response.py Outdated
Comment on lines +109 to +110
# returns an array of shape `(n_samples, n_outputs)`.
# We could remove this code in the future?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that means this code is never run? then why do we have it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we wrote some tests for it and thus silently supporting it.
We would not have tests, I would have been inclined removing it.
Here, we could potentially break code. It might not be worth it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I would simply remove it. It's not a documented behavior anyway.

@@ -72,15 +73,15 @@ def _get_response_values(
if is_classifier(estimator):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOVE the new implementation.

@adrinjalali adrinjalali left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much simplified

@adrinjalali
adrinjalali merged commit 3c9495c into scikit-learn:main Sep 17, 2023
glemaitre added a commit to glemaitre/scikit-learn that referenced this pull request Sep 18, 2023
…#27002)

Co-authored-by: Thomas J. Fan <[email protected]>
Co-authored-by: Jérémie du Boisberranger <[email protected]>
Co-authored-by: Stefanie Senger <[email protected]>
Co-authored-by: Vladimir Fokow <[email protected]>
Co-authored-by: jeremie du boisberranger <[email protected]>
Co-authored-by: Xuefeng Xu <[email protected]>
Co-authored-by: Tim Head <[email protected]>
Co-authored-by: Raphael <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
Co-authored-by: Sayed Qaiser Ali <[email protected]>
Co-authored-by: Loïc Estève <[email protected]>
Co-authored-by: Xiao Yuan <[email protected]>
glemaitre added a commit to glemaitre/scikit-learn that referenced this pull request Sep 19, 2023
…#27002)

Co-authored-by: Thomas J. Fan <[email protected]>
Co-authored-by: Jérémie du Boisberranger <[email protected]>
Co-authored-by: Stefanie Senger <[email protected]>
Co-authored-by: Vladimir Fokow <[email protected]>
Co-authored-by: jeremie du boisberranger <[email protected]>
Co-authored-by: Xuefeng Xu <[email protected]>
Co-authored-by: Tim Head <[email protected]>
Co-authored-by: Raphael <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
Co-authored-by: Sayed Qaiser Ali <[email protected]>
Co-authored-by: Loïc Estève <[email protected]>
Co-authored-by: Xiao Yuan <[email protected]>
jeremiedbb added a commit that referenced this pull request Sep 20, 2023
Co-authored-by: Thomas J. Fan <[email protected]>
Co-authored-by: Jérémie du Boisberranger <[email protected]>
Co-authored-by: Stefanie Senger <[email protected]>
Co-authored-by: Vladimir Fokow <[email protected]>
Co-authored-by: jeremie du boisberranger <[email protected]>
Co-authored-by: Xuefeng Xu <[email protected]>
Co-authored-by: Tim Head <[email protected]>
Co-authored-by: Raphael <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
Co-authored-by: Sayed Qaiser Ali <[email protected]>
Co-authored-by: Loïc Estève <[email protected]>
Co-authored-by: Xiao Yuan <[email protected]>
REDVM pushed a commit to REDVM/scikit-learn that referenced this pull request Nov 16, 2023
…#27002)

Co-authored-by: Thomas J. Fan <[email protected]>
Co-authored-by: Jérémie du Boisberranger <[email protected]>
Co-authored-by: Stefanie Senger <[email protected]>
Co-authored-by: Vladimir Fokow <[email protected]>
Co-authored-by: jeremie du boisberranger <[email protected]>
Co-authored-by: Xuefeng Xu <[email protected]>
Co-authored-by: Tim Head <[email protected]>
Co-authored-by: Raphael <[email protected]>
Co-authored-by: Olivier Grisel <[email protected]>
Co-authored-by: Sayed Qaiser Ali <[email protected]>
Co-authored-by: Loïc Estève <[email protected]>
Co-authored-by: Xiao Yuan <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scorer not working on ClassifierChain