Skip to content

Add NDCG metric to rec_sys#3608

Merged
vfdev-5 merged 31 commits intopytorch:masterfrom
steaphenai:feat/NDGC-metric-steaphen
Apr 8, 2026
Merged

Add NDCG metric to rec_sys#3608
vfdev-5 merged 31 commits intopytorch:masterfrom
steaphenai:feat/NDGC-metric-steaphen

Conversation

@steaphenai
Copy link
Copy Markdown
Contributor

@steaphenai steaphenai commented Feb 26, 2026

Fixes #3581
Refs #3568 #3569 #3610

Description: Implements NDCG (Normalized Discounted Cumulative Gain) metric for recommendation systems and ranking evaluation.

Implementation Details

Mathematical Background

  • DCG: Discounted Cumulative Gain - rewards relevant items, penalizes low positions
  • IDCG: Ideal DCG - perfect ranking baseline
  • NDCG: DCG / IDCG - normalized score from 0.0 to 1.0

Formula: NDCG@K = Σ(2^rel_i - 1) / log2(i + 1)

Testing

  • 26 tests passing locally (verified against ranx)
  • Parametrized tests across multiple configurations
  • Edge cases covered (perfect prediction, zero relevance, graded labels)
  • Distributed training testedcs

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 Feb 26, 2026
@aaishwarymishra
Copy link
Copy Markdown
Collaborator

aaishwarymishra commented Feb 28, 2026

@steaphenai In docs/source/metrics.rst add rec_sys.NDCG in complete list of metrics to pass initial checks.

@steaphenai steaphenai force-pushed the feat/NDGC-metric-steaphen branch from 6e158d3 to e910463 Compare March 2, 2026 17:51
@github-actions github-actions bot added the docs label Mar 2, 2026
@steaphenai steaphenai force-pushed the feat/NDGC-metric-steaphen branch 2 times, most recently from a6edab5 to cbbc275 Compare March 3, 2026 12:09
@steaphenai steaphenai force-pushed the feat/NDGC-metric-steaphen branch 2 times, most recently from 4f66145 to 65d7557 Compare March 3, 2026 12:30
@steaphenai
Copy link
Copy Markdown
Contributor Author

@steaphenai In docs/source/metrics.rst add rec_sys.NDCG in complete list of metrics to pass initial checks.

@aaishwarymishra Thanks!!

Added ranx = pytest.importorskip(...) to make ranx optional
Wrapped ranx validation checks with if ranx is not None
Added pytest.skip() for tests that specifically require ranx

Tests now pass without ranx installed while still validating against it when available. The NDCG metric implementation itself has no ranx dependency.
Use try/except to handle missing ranx dependency instead of pytest.importorskip
@vfdev-5
Copy link
Copy Markdown
Collaborator

vfdev-5 commented Mar 6, 2026

@steaphenai I was checking your implementation compared to previous ones where contributors were blocked with vectorized computation of

discounted_gains = torch.tensor(
  [_tie_averaged_dcg(y_p, y_t, discount_cumsum, device) for y_p, y_t in zip(y_pred, y_true)], device=device
)

when ignore_ties is False. I can't find the same thing in your implementation, can you explain your implementation, what it based on?

@steaphenai
Copy link
Copy Markdown
Contributor Author

steaphenai commented Mar 6, 2026

@steaphenai I was checking your implementation compared to previous ones where contributors were blocked with vectorized computation of

discounted_gains = torch.tensor(
  [_tie_averaged_dcg(y_p, y_t, discount_cumsum, device) for y_p, y_t in zip(y_pred, y_true)], device=device
)

when ignore_ties is False. I can't find the same thing in your implementation, can you explain your implementation, what it based on?

@vfdev-5 My implementation is based on the standard NDCG formula and validated against the ranx library.
For tie handling, I use torch.argsort(..., stable=True) which breaks ties deterministically by index order (same as MRR). This ensures reproducible results and matches ranx's behavior.
I validated correctness by testing against ranx across various scenarios, all tests pass with matching results.

@steaphenai
Copy link
Copy Markdown
Contributor Author

@vfdev-5 Just to clarify on the vectorization part, my implementation is fully vectorized using PyTorch operations (no Python loops).

Instead of looping through each example like:

[_tie_averaged_dcg(y_p, y_t, ...) for y_p, y_t in zip(y_pred, y_true)]

I use batched PyTorch operations:

ranked_indices = torch.argsort(y_pred, dim=-1, descending=True, stable=True)[:, :max_k]
ranked_relevance = torch.gather(y_for_dcg, dim=-1, index=ranked_indices)

This processes all examples in the batch simultaneously, which should be more efficient than the loop-based approach.

@vfdev-5
Copy link
Copy Markdown
Collaborator

vfdev-5 commented Mar 6, 2026

To help with reviewing can you please run catalyst and ranx on some non-trivial examples involving ties etc and compare results.

Removed try/except import and conditional checks for ranx.
ranx is now a required dev dependency in requirements-dev.txt, so tests can use it directly without fallback logic.
Co-authored-by: vfdev <vfdev.5@gmail.com>
@vfdev-5 vfdev-5 marked this pull request as draft March 6, 2026 10:10
@steaphenai steaphenai requested a review from vfdev-5 March 6, 2026 19:53
@steaphenai
Copy link
Copy Markdown
Contributor Author

@vfdev-5 I added ignite.metrics.rec_sys.NDCG with support for top_k, ignore_zero_hits, and relevance_threshold. I also added a shared _get_ranked_items(...) helper for the ranking logic and added gain_function support (exp_rank and linear_rank). The NDCG docstring and docs were updated to clarify graded relevance and for validation, I added tests in tests/ignite/metrics/rec_sys/test_ndcg.py which parity checks against ranx and Catalyst, plus tie cases and edge cases (perfect ranking, zero relevance, threshold behavior, multi-batch accumulation, invalid inputs, and distributed integration).

Added a tutorial link for using the NDCG metric.
Added usage tutorial reference for NDCG metric.
steaphenai and others added 2 commits April 3, 2026 18:52
Co-authored-by: vfdev <vfdev.5@gmail.com>
Co-authored-by: vfdev <vfdev.5@gmail.com>
@vfdev-5
Copy link
Copy Markdown
Collaborator

vfdev-5 commented Apr 7, 2026

@steaphenai please check the failing CI job: https://github.com/pytorch/ignite/actions/runs/23947698404/job/70144090725?pr=3608

@steaphenai
Copy link
Copy Markdown
Contributor Author

steaphenai commented Apr 7, 2026

@steaphenai please check the failing CI job: https://github.com/pytorch/ignite/actions/runs/23947698404/job/70144090725?pr=3608

PS C:\ignite> pre-commit run --all-files nb-clean.................................................................Passed check toml...............................................................Passed check yaml...............................................................Passed fix end of files.........................................................Passed fix end of files.........................................................Passed fix end of files.........................................................Passed trim trailing whitespace.................................................Passed prettier.................................................................Passed ruff check...............................................................Passed ruff format..............................................................Passed

@vfdev-5 I verified with pre-commit run --all-files. All the tests were passed locally.

@steaphenai steaphenai requested a review from vfdev-5 April 7, 2026 21:06
Copy link
Copy Markdown
Collaborator

@vfdev-5 vfdev-5 left a comment

Choose a reason for hiding this comment

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

Thanks for updates @steaphenai
We almost there, just few updates

Updated NDCG test functions to use ranx for verification and adjusted imports.
@steaphenai
Copy link
Copy Markdown
Contributor Author

Thanks for updates @steaphenai We almost there, just few updates

@vfdev-5 Done! I've set the imports to the top of the file.

Copy link
Copy Markdown
Collaborator

@vfdev-5 vfdev-5 left a comment

Choose a reason for hiding this comment

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

Thanks a lot for the PR @steaphenai , LGTM

@vfdev-5 vfdev-5 enabled auto-merge April 8, 2026 12:14
@vfdev-5
Copy link
Copy Markdown
Collaborator

vfdev-5 commented Apr 8, 2026

@steaphenai still failing CI code style check, please fix it

@steaphenai
Copy link
Copy Markdown
Contributor Author

steaphenai commented Apr 8, 2026

@vfdev-5 I moved the Catalyst import to the top-level as requested, but TPU CI was failing during test collection because importing catalyst triggers a torch_xla linkage error in that environment. I am thinking it to update it to a guarded top-level import (try/except) and marked only the Catalyst-dependent tests with skip-if when Catalyst is unavailable. This keeps import at top while avoiding unrelated CI breakage. Is this approach okay for this PR?
Something like this

from ranx import Qrels, Run, evaluate
try:
    from catalyst.metrics.functional import ndcg as catalyst_ndcg_fn

    HAS_CATALYST = True
except Exception:  
    catalyst_ndcg_fn = None
    HAS_CATALYST = False
    ```

@vfdev-5
Copy link
Copy Markdown
Collaborator

vfdev-5 commented Apr 8, 2026

Catalyst is unmaintained now, so I was actually thinking if we can remove it from the tests and keep only ranx (which is also look a bit left aside).
We can simply remove all the tests involving catalyst. Other option is to do what you suggested.
What do you think?

@steaphenai
Copy link
Copy Markdown
Contributor Author

@vfdev-5 Makes sense. I agree we should remove Catalyst-based parity tests and keep ranx-based validation. I’ll drop Catalyst imports/helpers/tests and keep the ranx checks as the reference path

auto-merge was automatically disabled April 8, 2026 16:08

Head branch was pushed to by a user without write access

@vfdev-5 vfdev-5 enabled auto-merge April 8, 2026 19:58
@vfdev-5 vfdev-5 added this pull request to the merge queue Apr 8, 2026
Merged via the queue into pytorch:master with commit 6c3dfd9 Apr 8, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add NDCG (Normalized Discounted Cumulative Gain) metric to rec_sys

3 participants