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

MAINT Clean up deprecations for 1.5: in log_loss #28851

Merged

Conversation

jeremiedbb
Copy link
Member

  • Removed the deprecated eps param of log_loss.
  • log_loss now raises an error when predicted probas do not sum to 1.

Copy link

github-actions bot commented Apr 16, 2024

✔️ Linting Passed

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

Generated for commit: dac73bc. Link to the linter CI: here

Comment on lines -2650 to -2664
# binary case: check correct boundary values for eps = 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [0, 1], eps=0) == 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [0, 0], eps=0) == np.inf
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [1, 1], eps=0) == np.inf

# multiclass case: check correct boundary values for eps = 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1, 2], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], eps=0) == 0
with pytest.warns(FutureWarning):
assert (
log_loss([0, 1, 2], [[0, 0.5, 0.5], [0, 1, 0], [0, 0, 1]], eps=0) == np.inf
)
Copy link
Member Author

Choose a reason for hiding this comment

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

@lorentzenchr I'm a bit confused. Does removing eps (deprecated in #25299) means that now eps is always 0 or eps is always computed based on the dtype ? The previous "auto" seems to indicate the latter but in that case testing edge cases is no longer possible.

Copy link
Member

Choose a reason for hiding this comment

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

See #24515 (comment).
My opinion is that eps=0 is the correct behavior (who are we to judge and MODIFY uncalibrated predicted probabilities!). The consensus was more in the direction of dtype dependent.

Copy link
Member Author

Choose a reason for hiding this comment

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

At least, the clipping should not happen here to me. If y_true=0 and y_pred=0, the result should be exactly 0. xlogy(0, 0) = 0 (no warning).

The question is do we want to return inf when y_true != 0 and y_pred = 0, or a finite value. If the former, we should clip with eps=0, else we should clip the result of xlogy as suggested in #24515 (comment)

I would go with returning inf, but I don't know if we rely on it being finite (maybe if *SearchCV and co), and the warning message said that eps will be non-zero in 1.5, so maybe we should better keep it as is.

Copy link
Member

Choose a reason for hiding this comment

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

I have the same opinion as you.

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, I find the following weird:

>>> log_loss([0, 1, 2], [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
2.2204460492503136e-16

Shall to conditionally clip to eps only when one_hot_encode(y_true) > 0 and clip to 0 otherwise?

Copy link
Member

Choose a reason for hiding this comment

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

Actually whatever the decision on this, there should be a test to cover the case where log_loss reaches its minimum (perfect predictions), both in binary and multiclass settings.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd rather leave this discussion for a separate issue/PR to (try to) keep the focus of this PR on the deprecations clean-up.

I added a test for perfect predictions that only checks that the result is close to 0 for now.

sklearn/metrics/_classification.py Outdated Show resolved Hide resolved
sklearn/metrics/_classification.py Outdated Show resolved Hide resolved
sklearn/metrics/tests/test_classification.py Show resolved Hide resolved
Copy link
Member

@ogrisel ogrisel left a comment

Choose a reason for hiding this comment

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

Another pass of feedback:

sklearn/metrics/_classification.py Outdated Show resolved Hide resolved
sklearn/metrics/tests/test_classification.py Outdated Show resolved Hide resolved
sklearn/metrics/tests/test_classification.py Outdated Show resolved Hide resolved
sklearn/metrics/tests/test_common.py Outdated Show resolved Hide resolved
sklearn/metrics/tests/test_common.py Outdated Show resolved Hide resolved
sklearn/metrics/tests/test_common.py Outdated Show resolved Hide resolved
Comment on lines -2650 to -2664
# binary case: check correct boundary values for eps = 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [0, 1], eps=0) == 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [0, 0], eps=0) == np.inf
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [1, 1], eps=0) == np.inf

# multiclass case: check correct boundary values for eps = 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1, 2], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], eps=0) == 0
with pytest.warns(FutureWarning):
assert (
log_loss([0, 1, 2], [[0, 0.5, 0.5], [0, 1, 0], [0, 0, 1]], eps=0) == np.inf
)
Copy link
Member

Choose a reason for hiding this comment

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

Indeed, I find the following weird:

>>> log_loss([0, 1, 2], [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
2.2204460492503136e-16

Shall to conditionally clip to eps only when one_hot_encode(y_true) > 0 and clip to 0 otherwise?

sklearn/metrics/tests/test_classification.py Outdated Show resolved Hide resolved
Comment on lines -2650 to -2664
# binary case: check correct boundary values for eps = 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [0, 1], eps=0) == 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [0, 0], eps=0) == np.inf
with pytest.warns(FutureWarning):
assert log_loss([0, 1], [1, 1], eps=0) == np.inf

# multiclass case: check correct boundary values for eps = 0
with pytest.warns(FutureWarning):
assert log_loss([0, 1, 2], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], eps=0) == 0
with pytest.warns(FutureWarning):
assert (
log_loss([0, 1, 2], [[0, 0.5, 0.5], [0, 1, 0], [0, 0, 1]], eps=0) == np.inf
)
Copy link
Member

Choose a reason for hiding this comment

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

Actually whatever the decision on this, there should be a test to cover the case where log_loss reaches its minimum (perfect predictions), both in binary and multiclass settings.

@jeremiedbb jeremiedbb added this to the 1.5 milestone Apr 25, 2024
Co-authored-by: Guillaume Lemaitre <guillaume@probabl.ai>
Copy link
Member

@ogrisel ogrisel left a comment

Choose a reason for hiding this comment

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

LGTM as well. Thanks.

@ogrisel ogrisel merged commit 19c068f into scikit-learn:main Apr 29, 2024
30 checks passed
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.

None yet

4 participants