Skip to content

Error in d2_log_loss_score multiclass when one of the classes is missing in y_true. #30713

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

Closed
aperezlebel opened this issue Jan 24, 2025 · 4 comments · Fixed by #30903
Closed
Labels
Bug Needs Investigation Issue requires investigation

Comments

@aperezlebel
Copy link
Contributor

Describe the bug

Hello, I encountered an error with the d2_log_loss_score in the multiclass setting (i.e. when y_pred has shape (n, k) with k >= 3) when one of the classes is missing from the y_true labels, even when giving the labels through the labels argument. The error disappear when all the classes are present in y_true.

Steps/Code to Reproduce

from sklearn.metrics import d2_log_loss_score

y_true = [0, 1, 1]
y_pred = [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
labels = [0, 1, 2]

d2_log_loss_score(y_true, y_pred, labels=labels)

Expected Results

No error is thrown.

Actual Results

Traceback (most recent call last):
  File "minimal.py", line 7, in <module>
    d2_log_loss_score(y_true, y_pred, labels=labels)
  File ".../python3.12/site-packages/sklearn/utils/_param_validation.py", line 216, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File ".../python3.12/site-packages/sklearn/metrics/_classification.py", line 3407, in d2_log_loss_score
    denominator = log_loss(
                  ^^^^^^^^^
  File ".../python3.12/site-packages/sklearn/utils/_param_validation.py", line 189, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File ".../python3.12/site-packages/sklearn/metrics/_classification.py", line 3023, in log_loss
    raise ValueError(
ValueError: The number of classes in labels is different from that in y_pred. Classes found in labels: [0 1 2]

Versions

System:
    python: 3.12.8 | packaged by conda-forge | (main, Dec  5 2024, 14:19:53) [Clang 18.1.8 ]
executable: /Users/alexandreperez/dev/lib/miniforge3/envs/test/bin/python
   machine: macOS-15.2-arm64-arm-64bit

Python dependencies:
      sklearn: 1.6.1
          pip: 24.3.1
   setuptools: 75.8.0
        numpy: 2.2.2
        scipy: 1.15.1
       Cython: None
       pandas: None
   matplotlib: None
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: openmp
   internal_api: openmp
    num_threads: 14
         prefix: libomp
       filepath: .../miniforge3/envs/test/lib/python3.12/site-packages/sklearn/.dylibs/libomp.dylib
        version: None
@aperezlebel aperezlebel added Bug Needs Triage Issue requires triage labels Jan 24, 2025
@lesteve lesteve removed the Needs Triage Issue requires triage label Jan 24, 2025
@lesteve
Copy link
Member

lesteve commented Jan 24, 2025

Indeed, I can reproduce, looks like an oversight, but more investigation is needed.

@lesteve lesteve added Needs work Needs Investigation Issue requires investigation and removed Needs work labels Jan 24, 2025
@ogrisel
Copy link
Member

ogrisel commented Feb 3, 2025

Thanks for the report @aperezlebel. Are you interested in opening on a PR yourself?

@aperezlebel
Copy link
Contributor Author

I would be, but I am afraid I won't have time in the near future.

@vmargonis
Copy link
Contributor

Hello, I think I have identified the issue here:

  • inside d2_log_loss_score, two log losses are calculated, the log loss of the fitted model aka numerator and the log loss of the null model aka denominator.
  • both losses are calculated with the log_loss function in which the labels variable is passed down to.
  • For numerator, y_pred is equal to the y_pred passed by the user. y_pred has already the "correct" dimension (n, k=3) with respect to the number of labels -- 3 labels given, 3 columns are present in y_pred.
  • The issue arises in the computation of denominator, where y_true is supposed to be transformed into a 2D array of dimension (n, k). I will try to break it down below:
   # >>> labels = [0, 1, 2]
   # >>> y_true = [0, 1, 1]

    _, y_value_indices = np.unique(y_true, return_inverse=True)
    # >>> y_value_indices = [0, 1, 1]
   
    counts = np.bincount(y_value_indices, weights=weights)
    # >>> counts = [1, 2] 
    # At this point counts should be [1, 2, 0] but labels are not taken into consideration
    # this is also why the error does not appear when all labels are present in y_true

    y_prob = counts / weights.sum()
    # y_prob has the same size as counts, i.e. 2

    y_pred_null = np.tile(y_prob, (len(y_true), 1))
    # y_pred_null is now an array of (n, 2) instead of (n, 3)

    # log loss of the null model
    denominator = log_loss(
        y_true=y_true,
        y_pred=y_pred_null,
        normalize=False,
        sample_weight=sample_weight,
        labels=labels,
    )
    # exception is thrown inside log_loss because labels has size 3 and y_pred has 2 columns

I think that computing counts by appropriately taking labels into consideration will fix the issue.

@ogrisel I will open a PR myself within the next days. Excuse me for my long response and if I did not follow protocol. This is my very first attempt at contributing to open source and I am still familiarizing with the workflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Needs Investigation Issue requires investigation
Projects
None yet
4 participants