Skip to content

Feat: add ignore_index support to DiceLoss#8969

Open
qwepablo12 wants to merge 2 commits into
Project-MONAI:devfrom
qwepablo12:feature-ignore-index-losses
Open

Feat: add ignore_index support to DiceLoss#8969
qwepablo12 wants to merge 2 commits into
Project-MONAI:devfrom
qwepablo12:feature-ignore-index-losses

Conversation

@qwepablo12

Copy link
Copy Markdown

Description

Addresses #8734.

This PR introduces ignore_index support for DiceLoss. When ignore_index is specified, the target regions matching this index are masked out and completely excluded from the Dice coefficient computation (both numerator and denominator). This ensures that ignored regions/classes do not affect the loss values or gradients during training.

Types of changes

  • Non-breaking change (fix or new feature that would not break existing functionality).
  • Breaking change (fix or new feature that would cause existing functionality to change).
  • New tests added to cover the changes.
  • Integration tests passed locally by running ./runtests.sh -f -u --net --coverage.
  • Quick tests passed locally by running ./runtests.sh --quick --unittests --disttests.
  • In-line docstrings updated.
  • Documentation updated, tested make html command in the docs/ folder.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

DiceLoss gains an ignore_index constructor parameter. In forward, ignored target values are masked out for both index and one-hot targets before Dice computation. Class weights are applied to the per-class loss before reduction. Tests add coverage for cropped equivalence, index-versus-one-hot parity, and zero gradients on ignored pixels.

Changes

File Change Summary
monai/losses/dice.py Added ignore_index; built and applied a valid mask in forward; trimmed docstring; moved class-weight application before reduction
tests/losses/test_dice_loss.py Added ignore-index coverage for index targets, one-hot targets, and gradients

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant DiceLoss
  participant Reduction
  Caller->>DiceLoss: forward(input, target)
  alt ignore_index set
    DiceLoss->>DiceLoss: build valid_mask
    DiceLoss->>DiceLoss: mask input and target
  end
  DiceLoss->>DiceLoss: compute per-class loss
  alt class_weight set
    DiceLoss->>DiceLoss: apply class_weight to per-class loss
  end
  DiceLoss->>Reduction: reduce(loss)
  Reduction-->>Caller: return loss
Loading

Related Issues: Not specified.

Related PRs: Not specified.

Suggested labels: enhancement, losses

Suggested reviewers: Not specified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding ignore_index support to DiceLoss.
Description check ✅ Passed The description includes the required description and types-of-changes sections and covers the new feature and tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
monai/losses/dice.py (2)

98-102: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document raised exceptions in forward.

forward raises AssertionError (Line 143) and ValueError (Line 186) but the trimmed docstring only has Args. Add a Raises: section.

As per path instructions, docstrings should describe each raised exception in the appropriate Google-style section.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@monai/losses/dice.py` around lines 98 - 102, The forward docstring in
DiceLoss currently documents only Args, but the method can raise AssertionError
and ValueError. Update the docstring in DiceLoss.forward to add a Google-style
Raises section that names both exceptions and briefly states the conditions that
trigger them, keeping the docs aligned with the existing forward validation
logic.

Source: Path instructions


72-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Constructor docstring isn't Google-style.

Only the new ignore_index is documented; the rest defers to "standard MONAI DiceLoss". Per Google style each arg should be listed under Args:.

As per path instructions, docstrings should describe each variable in the appropriate Google-style section.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@monai/losses/dice.py` around lines 72 - 75, The constructor docstring for
DiceLoss is not in Google style and only mentions ignore_index, so update the
docstring in the DiceLoss initializer to use an Args: section that explicitly
documents every parameter instead of referring to “standard MONAI DiceLoss.”
Make sure each argument used by the DiceLoss constructor is listed with a brief
description, and include ignore_index in the same section alongside the existing
parameters.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@monai/losses/dice.py`:
- Around line 127-148: The ignore_index handling in the Dice loss is masking an
entire class channel in the one-hot path instead of only the ignored pixels, so
the mask logic in the Dice class should be changed to produce a spatial valid
mask from the ignored channel rather than zeroing the full channel. Update the
masking in the Dice loss flow so `valid_mask` reflects ignored voxels/pixels per
location and then apply it consistently to both `input` and `target`, keeping
one-hot and index targets equivalent while preserving the existing
`include_background` behavior.
- Around line 116-148: Add unit tests for DiceLoss ignore_index handling in both
index-format B1HW targets and one-hot BNHW targets, including the
include_background=False path and out-of-range ignore_index values. Extend the
existing DiceLoss test coverage to verify ignored regions do not contribute to
the computed loss or backpropagated gradients, and reference the DiceLoss
forward/masking logic when asserting the expected behavior.

---

Nitpick comments:
In `@monai/losses/dice.py`:
- Around line 98-102: The forward docstring in DiceLoss currently documents only
Args, but the method can raise AssertionError and ValueError. Update the
docstring in DiceLoss.forward to add a Google-style Raises section that names
both exceptions and briefly states the conditions that trigger them, keeping the
docs aligned with the existing forward validation logic.
- Around line 72-75: The constructor docstring for DiceLoss is not in Google
style and only mentions ignore_index, so update the docstring in the DiceLoss
initializer to use an Args: section that explicitly documents every parameter
instead of referring to “standard MONAI DiceLoss.” Make sure each argument used
by the DiceLoss constructor is listed with a brief description, and include
ignore_index in the same section alongside the existing parameters.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ed022536-cc87-49a6-861c-d8f8312c39e6

📥 Commits

Reviewing files that changed from the base of the PR and between 482d1d9 and e690fbd.

📒 Files selected for processing (1)
  • monai/losses/dice.py

Comment thread monai/losses/dice.py Outdated
Comment thread monai/losses/dice.py Outdated
Signed-off-by: qwepablo12 <grazava6@gmail.com>
@qwepablo12 qwepablo12 force-pushed the feature-ignore-index-losses branch from e690fbd to 7fdeae6 Compare July 5, 2026 15:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
monai/losses/dice.py (1)

118-131: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Sanitize ignore_index before one_hot valid_mask is built too late here; out-of-range ignored labels like 255 or -100 will still hit one_hot(...) and fail in scatter_. Neutralize masked labels before conversion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@monai/losses/dice.py` around lines 118 - 131, `DiceLoss` builds `valid_mask`
in the `ignore_index` path, but `one_hot(...)` still receives the raw target and
can fail on ignored labels like 255 or -100. Update the `DiceLoss`
target-preprocessing flow so masked labels are neutralized before calling
`one_hot`, using the existing `ignore_index`, `valid_mask`, and `to_onehot_y`
logic in `dice.py`. Keep the mask behavior intact while ensuring the labels
passed into `one_hot` are always in-range.
🧹 Nitpick comments (1)
monai/losses/dice.py (1)

98-102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Docstring lost its Returns/Raises sections. forward raises AssertionError (Line 142) and ValueError (Lines 170, 175, 186) and returns a tensor; document them in Google style.

As per path instructions: "Docstrings should be present for all definition which describe each variable, return value, and raised exception in the appropriate section of the Google-style of docstrings."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@monai/losses/dice.py` around lines 98 - 102, The Dice loss forward docstring
is missing the Google-style Returns and Raises sections even though `forward`
returns a tensor and can throw `AssertionError` and `ValueError`. Update the
docstring for `forward` in `dice.py` to document the returned tensor and list
the exceptions raised by the input validation paths, keeping the existing
argument descriptions and using the `forward` method as the anchor.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/losses/test_dice_loss.py`:
- Around line 244-247: The test `test_ignore_index_one_hot_target` uses
`one_hot` but never imports it, so the test will fail with a NameError. Add the
appropriate MONAI import for `one_hot` at the top of the test module, using the
existing test file’s import section and the `one_hot` helper referenced in
`test_ignore_index_one_hot_target`.

---

Outside diff comments:
In `@monai/losses/dice.py`:
- Around line 118-131: `DiceLoss` builds `valid_mask` in the `ignore_index`
path, but `one_hot(...)` still receives the raw target and can fail on ignored
labels like 255 or -100. Update the `DiceLoss` target-preprocessing flow so
masked labels are neutralized before calling `one_hot`, using the existing
`ignore_index`, `valid_mask`, and `to_onehot_y` logic in `dice.py`. Keep the
mask behavior intact while ensuring the labels passed into `one_hot` are always
in-range.

---

Nitpick comments:
In `@monai/losses/dice.py`:
- Around line 98-102: The Dice loss forward docstring is missing the
Google-style Returns and Raises sections even though `forward` returns a tensor
and can throw `AssertionError` and `ValueError`. Update the docstring for
`forward` in `dice.py` to document the returned tensor and list the exceptions
raised by the input validation paths, keeping the existing argument descriptions
and using the `forward` method as the anchor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0162add8-4953-424e-bb94-a4068c72c6b4

📥 Commits

Reviewing files that changed from the base of the PR and between e690fbd and a1beaef.

📒 Files selected for processing (2)
  • monai/losses/dice.py
  • tests/losses/test_dice_loss.py

Comment on lines +244 to +247
def test_ignore_index_one_hot_target(self):
input_tensor = torch.randn(2, 4, 8, 8)
target_idx = torch.randint(0, 4, (2, 1, 8, 8))
target_onehot = one_hot(target_idx, num_classes=4)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Missing one_hot import — test will raise NameError.

one_hot is used at Line 247 but never imported. It's a MONAI utility (monai.networks.one_hot / monai.networks.utils.one_hot), not a builtin.

🐛 Proposed fix
+from monai.networks import one_hot
🧰 Tools
🪛 Ruff (0.15.20)

[error] 247-247: Undefined name one_hot

(F821)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/losses/test_dice_loss.py` around lines 244 - 247, The test
`test_ignore_index_one_hot_target` uses `one_hot` but never imports it, so the
test will fail with a NameError. Add the appropriate MONAI import for `one_hot`
at the top of the test module, using the existing test file’s import section and
the `one_hot` helper referenced in `test_ignore_index_one_hot_target`.

Source: Linters/SAST tools

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant