Skip to content

feat: add class_names support to Precision, Recall, FBeta metrics - #3732

Open
aniketpandey05 wants to merge 15 commits into
pytorch:masterfrom
aniketpandey05:feature/class-names-precision-recall
Open

feat: add class_names support to Precision, Recall, FBeta metrics#3732
aniketpandey05 wants to merge 15 commits into
pytorch:masterfrom
aniketpandey05:feature/class-names-precision-recall

Conversation

@aniketpandey05

@aniketpandey05 aniketpandey05 commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Fixes #1466

Description:
Adds an optional class_names parameter to _BasePrecisionRecall, allowing compute() to return a labeled dict instead of an unnamed tensor when average=False or average=None. Useful for per-class metric tracking where knowing which score belongs to which class matters for logging and visualization.

Check list:

  • New tests are added (if a new feature is added)
  • New doc strings: description and/or example code are in RST format
  • Documentation is updated (if required)

@github-actions github-actions Bot added the module: metrics Metrics module label Apr 14, 2026
@aaishwarymishra

Copy link
Copy Markdown
Collaborator

ok so like wont this code break fbeta metric when precision and recall have average=False and they have class_names, we need to update the fbeta too to support this.

There can be more metrics that can benefit from this change. I am not sure though.

Comment thread ignite/metrics/precision.py Outdated
@aniketpandey05

Copy link
Copy Markdown
Contributor Author

@aaishwarymishra would raising errors for those condition a better option or should we add full class_name support to fbeta

@aaishwarymishra

Copy link
Copy Markdown
Collaborator

I am not sure, adding support for the class names in fbeta would be easier, but fbeta uses the MetricsLambda for overloading operator we can update them, I am not sure @vfdev-5 what you think would be appropriate?

@steaphenai

Copy link
Copy Markdown
Contributor

Could we please clarify Fbeta behavior when class_names is used with average=False/None

@vfdev-5

vfdev-5 commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

I am not sure, adding support for the class names in fbeta would be easier, but fbeta uses the MetricsLambda for overloading operator we can update them, I am not sure @vfdev-5 what you think would be appropriate?

If we can compute F1 score per class than we should propagate the dict structure until the output of the F1 otherwise, take .values() of the dict output from precision and recall.

@aniketpandey05

Copy link
Copy Markdown
Contributor Author

Hey @vfdev-5 , would adding a helper in metric.py to handle dict arithmetic in all operator overloads so the dict propagates through to the Fbeta output be a good approach? Also when average=True and class_names is set, should Fbeta return a scalar float or propagate the dict as is?

@vfdev-5

vfdev-5 commented May 3, 2026

Copy link
Copy Markdown
Collaborator

Hey @vfdev-5 , would adding a helper in metric.py to handle dict arithmetic in all operator overloads so the dict propagates through to the Fbeta output be a good approach? Also when average=True and class_names is set, should Fbeta return a scalar float or propagate the dict as is?

I do not know, what would you suggest? Let's try to make it the most simple possible and intuitively clear.

@aniketpandey05

aniketpandey05 commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

hey @vfdev-5 I think Fbeta should return a per-class dict if class_names is set, regardless of average.

@vfdev-5

vfdev-5 commented May 21, 2026

Copy link
Copy Markdown
Collaborator

@rogueslasher ok, let's implement that this way and see if this works as expected

@aniketpandey05

Copy link
Copy Markdown
Contributor Author

Hey @vfdev-5, would adding a _dict_aware_op helper in metric.py and updating all operator overloads to use it for element-wise dict arithmetic, and skipping average in fbeta.py when class_names is set be a good idea? It touches all operator overloads in metric.py which affects all metrics.

i am worried since it touches all operators overloads

@aniketpandey05

Copy link
Copy Markdown
Contributor Author

@aaishwarymishra ,would love your help in this can you look into it more please

@aaishwarymishra

Copy link
Copy Markdown
Collaborator

@rogueslasher well , I am not sure to be honest i wont touch the metric operators as it kind of affect every metric, for a case which can be only relevant for very few metrics, I think we should modify Fbeta directly.

@aniketpandey05
aniketpandey05 force-pushed the feature/class-names-precision-recall branch from 61e7680 to 7ea2eb5 Compare June 30, 2026 13:48
@aniketpandey05

Copy link
Copy Markdown
Contributor Author

@aaishwarymishra the header rules and other three preview checks are taking insane amount of time . is there any issue

@aaishwarymishra

Copy link
Copy Markdown
Collaborator

Documentation issue :)

@aniketpandey05

Copy link
Copy Markdown
Contributor Author

@aaishwarymishra

Comment thread ignite/metrics/recall.py Outdated
Comment thread ignite/metrics/recall.py
Comment thread tests/ignite/metrics/test_precision.py
@aniketpandey05
aniketpandey05 force-pushed the feature/class-names-precision-recall branch from 9c71695 to 817f33b Compare August 1, 2026 15:14
@aniketpandey05

Copy link
Copy Markdown
Contributor Author

@aaishwarymishra

Comment thread ignite/metrics/fbeta.py Outdated
Comment thread ignite/metrics/fbeta.py Outdated
Comment thread ignite/metrics/fbeta.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also there is no early check for if average is True and also the class name has been given

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this resolved?

Comment thread ignite/metrics/fbeta.py Outdated
@aniketpandey05
aniketpandey05 force-pushed the feature/class-names-precision-recall branch from 3a93092 to 7e12121 Compare August 3, 2026 10:49
@aniketpandey05

Copy link
Copy Markdown
Contributor Author

Hi @aaishwarymishra, thanks for the review! I've updated the implementation:

Comment thread ignite/metrics/fbeta.py Outdated
Comment thread ignite/metrics/fbeta.py Outdated
Comment thread ignite/metrics/fbeta.py Outdated
@aaishwarymishra

Copy link
Copy Markdown
Collaborator

Also try to reslove previous comments it gets hard to read the code :)

@aniketpandey05
aniketpandey05 force-pushed the feature/class-names-precision-recall branch from aebabb1 to 7b8ce31 Compare August 6, 2026 08:03
Comment thread ignite/metrics/fbeta.py Outdated
@aniketpandey05

Copy link
Copy Markdown
Contributor Author

@aaishwarymishra i think the ci failure are unrelated to the changed files

@aaishwarymishra

Copy link
Copy Markdown
Collaborator

@vfdev-5 looks good to me, you can check it too and merge :)

@vfdev-5 vfdev-5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I haven't yet checked the code itself but docstring needs some updates.

@aaishwarymishra resolve all open comments if they were addressed.

Comment thread ignite/metrics/fbeta.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this resolved?

Comment thread ignite/metrics/fbeta.py
device: specifies which device updates are accumulated on. Setting the metric's
device to be the same as your ``update`` arguments ensures the ``update`` method is non-blocking. By
default, CPU.
class_names: list of class name strings used to label per-class output. Default: ``None``.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Docstring should also contain .. version added tag for new args

Comment thread ignite/metrics/recall.py

@aaishwarymishra aaishwarymishra left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@aniketpandey05 can you update docstrings too :)

@vfdev-5 vfdev-5 changed the title feat: add class_names support to Precision and Recall metrics feat: add class_names support to Precision, Recall, FBeta metrics Aug 12, 2026
Comment thread ignite/metrics/fbeta.py
default, CPU.
class_names: list of class name strings used to label per-class output. Default: ``None``.

.. versionadded:: 0.6.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a wrong tag, check Contributing guide and also the indent size.

Comment thread ignite/metrics/fbeta.py
Comment on lines +185 to +189
target_class_names = class_names
if target_class_names is None and precision is not None:
target_class_names = precision._class_names
if target_class_names is None and recall is not None:
target_class_names = recall._class_names

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
target_class_names = class_names
if target_class_names is None and precision is not None:
target_class_names = precision._class_names
if target_class_names is None and recall is not None:
target_class_names = recall._class_names
if class_names is None and precision is not None:
class_names = precision._class_names
if class_names is None and recall is not None:
class_names = recall._class_names

Comment thread ignite/metrics/fbeta.py
output_transform=(lambda x: x) if output_transform is None else output_transform,
average=False,
device=cast(str | torch.device, recall._device if recall else device),
class_names=target_class_names,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
class_names=target_class_names,
class_names=class_names,

Comment thread ignite/metrics/fbeta.py
output_transform=(lambda x: x) if output_transform is None else output_transform,
average=False,
device=cast(str | torch.device, precision._device if precision else device),
class_names=target_class_names,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
class_names=target_class_names,
class_names=class_names,

Comment thread ignite/metrics/fbeta.py
Comment on lines +176 to +177
if any(m._average for m in active_metrics):
raise ValueError("Input precision and recall metrics should have average=False")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This new error message is not precise enough. We should explicitly tell which metric exactly should have average false and not give a generic message


@sync_all_reduce("_numerator", "_denominator")
def compute(self) -> torch.Tensor | float:
def compute(self) -> torch.Tensor | float | dict:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you make dict type hint more detailed: dict[str, <whatever type is here>]

name to its metric value instead of a tensor. Must match the number of classes inferred
from the data. Default: ``None``.

.. versionadded:: 0.6.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this is a wrong tag, it should not be added here.

Comment thread ignite/metrics/recall.py
name to its metric value instead of a tensor. Must match the number of classes inferred
from the data. Default: ``None``.

.. versionadded:: 0.6.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
.. versionadded:: 0.6.0

Fbeta(0.0)

with pytest.raises(ValueError, match=r"Input precision metric should have average=False"):
with pytest.raises(ValueError, match=r"Input precision and recall metrics should have average=False"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Keep this test as it was and update the code

Fbeta(1.0, precision=p)

with pytest.raises(ValueError, match=r"Input recall metric should have average=False"):
with pytest.raises(ValueError, match=r"Input precision and recall metrics should have average=False"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same here

with pytest.raises(ValueError, match="Input precision and recall metrics should have average=False"):
Fbeta(beta=1.0, average=False, precision=p_avg)

# Correct computation passing class_names directly to Fbeta

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I prefer that these test cases were added to existing tests as new parametrizations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module: metrics Metrics module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ability to add class name for metrics

4 participants