Skip to content

fix(rewards): reject reversed, empty and non-finite boxes in bbox_giou_reward - #4872

Open
jimmy-holdout wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
jimmy-holdout:fix/bbox-giou-invalid-boxes
Open

jimmy-holdout wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
jimmy-holdout:fix/bbox-giou-invalid-boxes

Conversation

@jimmy-holdout

Copy link
Copy Markdown

What does this PR do ?

Return 0 from bbox_giou_reward when the response box has reversed corners, zero area, non-finite coordinates or coordinates large enough to overflow the area, so the reward stays in [-1, 1] for a well-formed ground truth.

Issues

Closes #4871

Usage

No API change. A response box must have finite coordinates with x1 < x2 and y1 < y2, as the refcoco prompt already asks.

from nemo_rl.environments.rewards import bbox_giou_reward

bbox_giou_reward("[400, 400, 700, 700]", "<answer>[1000, 1000, 0, 0]</answer>")  # was (11.11, True), now (0.0, False)
bbox_giou_reward("[400, 400, 700, 700]", "<answer>[400, 400, 700, 700]</answer>")  # still (1.0, True)

Behaviour change: the gold box written with its corners swapped ([x2, y2, x1, y1]) now scores 0 instead of 1. The prompt defines x1, y1 as the top left, so I treated reversed corners as a malformed answer rather than sorting them. Sorting would also keep the reward bounded, if you'd prefer that.

Rejected boxes get 0, the same as an unparseable answer today, which is above the GIoU of a badly placed well-formed box (down to -1). Returning -1 instead would remove that gap; I kept 0 for consistency with the existing parse-failure path.

Before your PR is "Ready for review"

Pre checks:

  • Make sure you read and followed Contributor guidelines
  • Did you write any new necessary tests?
  • Did you run the unit tests and functional tests locally? Visit our Testing Guide for how to run tests
  • Did you add or update any necessary documentation? Visit our Document Development Guide for how to write, build and test the docs.

Additional Information

  • Two new tests in tests/unit/rewards/test_rewards.py:

    • test_bbox_giou_reward_rejects_invalid_boxes covers reversed corners, zero width, NaN, inf and area overflow.
    • test_bbox_giou_reward_is_bounded checks 2,000 random response boxes against valid ground truth.
    • Both fail on main and pass here. tests/unit/rewards/test_rewards.py passes in full (9 tests); no functional test exercises this function. A fuzz run of 150,000 random response boxes (including magnitudes up to 1e300) against valid ground truths gave no reward outside [-1, 1]. ruff check and ruff format (v0.9.9, as pinned in pre-commit) are clean.
  • RefCOCO validation (3,811 items), each answer wrapped in a full <think>…</think> <answer>…</answer> response and scored through VLMVerifyWorker with the env.refcoco block of vlm_grpo_3B.yaml, before and after:

    answer main this PR
    gold box 1.0 1.0
    constant [1000, 1000, 0, 0] mean 7.03, max 29.65 0.1 (the format reward only)
    constant [nan, nan, nan, nan] mean 1.8e8 0.1
    wrong mirror box, corners swapped mean 0.41 (1.0 on 1,304 items) 0.1
    wrong mirror box, corners in order mean -0.18 mean -0.18 (unchanged)
  • Docs: no page covers the reward functions, so only the docstring is updated.

  • Unit tests only: no functional test exercises this reward function, so that checkbox is left unticked. Happy to share the RefCOCO scoring script.

…u_reward

bbox_giou_reward did not check the response box. With reversed corners the
"area" is positive and the intersection empty, so the hull can collapse to
the ground-truth box and the reward exceeds 1 (a constant [1000, 1000, 0, 0]
averages 7.03 over RefCOCO val). NaN or inf coordinates, or finite ones
large enough to overflow the areas, give NaN, inf or huge rewards. Return 0
for those boxes so the reward stays in [-1, 1].

Signed-off-by: jimmy-holdout <346065422+jimmy-holdout@users.noreply.github.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 23, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bbox_giou_reward is unbounded: reversed-corner or NaN boxes score far above 1 (RefCOCO env)

1 participant