-
-
Notifications
You must be signed in to change notification settings - Fork 25.8k
ENH add a parameter pos_label in roc_auc_score #17594
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
Conversation
Arfff, this is breaking the backward compatibility. I would consider the current behaviour as a bug because the way the positive class is inferred is really opaque. Here, it depends on the result of Any thoughts @ogrisel @thomasjpfan @amueller |
Ooops, I realised I merged the wrong PR. I wanted to merge #17569 instead but I was on the wrong tab. Let me review this a posteriori... |
I agree we should ask for an explicit |
This is good strategy to get PR merged ;) |
I am afraid we will have to revert this bug fix because it breaks the ability to run a GridSearchCV with
when running this script with this PR: https://gist.github.com/ogrisel/bfadf2dffef144d401b6e7c52d744219 This used to work before so this would be a regression for the model selection use case. |
I think we need |
Fair enough. So we revert the change and I reopen a PR. We should probably add a test for the case mentioned earlier |
But I am not so sure this is a good solution in general. For ROC AUC swapping the positive and negative classes is fine as the score is symmetric, but this is not the case for other binary classification metrics such as average precision on imbalanced data for instance. Here is what I mean by "symmetric": import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, average_precision_score
X, y = load_breast_cancer(return_X_y=True)
idx_positive = np.flatnonzero(y == 1)
idx_negative = np.flatnonzero(y == 0)
idx_selected = np.hstack([idx_negative, idx_positive[:25]])
X, y = X[idx_selected], y[idx_selected]
X, y = shuffle(X, y, random_state=42)
X = X[:, :2]
y = np.array(
["cancer" if c == 1 else "not cancer" for c in y], dtype=object
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, stratify=y, random_state=0,
)
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
assert classifier.classes_.tolist() == ["cancer", "not cancer"]
y_pred = classifier.predict_proba(X_test)
def compute_score(classifier, score_func, X_test, y_test, pos_label):
pos_idx = classifier.classes_.tolist().index(pos_label)
score = score_func(y_test, y_pred[:, pos_idx], pos_label=pos_label)
print(f"{score_func.__name__} with {pos_label=}: {score=:.4f}")
compute_score(classifier, roc_auc_score, X_test, y_test, "cancer")
compute_score(classifier, roc_auc_score, X_test, y_test, "not cancer")
compute_score(classifier, average_precision_score, X_test, y_test, "cancer")
compute_score(classifier, average_precision_score, X_test, y_test, "not cancer") output:
|
So I believe the only option would be to make it possible to pass Furthermore, we should find a way to let |
…17594)" (scikit-learn#17703) This reverts commit fde9212.
Good point
Can this be extended to passing a "scorer_kwargs" that gets passed down to the metric? Concretely: gs = GridSearchCV(..., scorer_kwargs={'pos_label': 'cancer'},
scorer='average_precision_scorer') |
#12385 proposes to solve the issue of users needing to pass things like
labels and pos_label in a different manner. It basically means we have a
function which, given a dataset, proposes a range of relevant scorers in a
way that it's then easy for users to choose a subset, and ideally giving an
unambiguous name and definition to each.
|
And I've previously noted that the symmetry of roc_auc means that the
default heuristic is fine for scoring with it without specifying pos_label.
|
Oh you mean in the grid-search probably. |
…17594)" (scikit-learn#17703) This reverts commit fde9212.
closes #17572
Add a parameter
pos_label
to be able to specify the positive class in the case of binary classification.