Skip to content

dataset_nuscenes: give each sample a COCO image id that survives a new process - #7

Open
egeboy35 wants to merge 1 commit into
lkk688:mainfrom
egeboy35:fix/nuscenes-image-id-is-reproducible
Open

dataset_nuscenes: give each sample a COCO image id that survives a new process#7
egeboy35 wants to merge 1 commit into
lkk688:mainfrom
egeboy35:fix/nuscenes-image-id-is-reproducible

Conversation

@egeboy35

Copy link
Copy Markdown

dataset_nuscenes.py:433 builds every target's COCO image id as

'image_id': torch.tensor([hash(sample_token) % 1000000]),

and cocoevaluator.py keys both halves of the evaluation on it: the ground truth at convert_to_coco_api line 300, the predictions at res[image_id] = out on line 370.

The id is not the same twice

hash() on a str is salted per interpreter (PEP 456). One token, six fresh interpreters:

PYTHONHASHSEED=0        -> 489194
PYTHONHASHSEED=1        -> 812316
PYTHONHASHSEED=2        -> 270356
PYTHONHASHSEED=random   -> 473704
PYTHONHASHSEED=random   -> 887793
PYTHONHASHSEED=random   -> 561686

Six ids for one sample. A prediction file written in one run cannot be scored against ground truth rebuilt in another, and the two processes of a distributed run disagree about which image is which. This is the half that does not shrink with a bigger machine or a smaller dataset.

The 1e6 range is the milder half, and I would rather give its honest size

Over the 34,149 keyframes of v1.0-trainval a 1e6 range loses about 555 ids to collisions — "about", because the exact count depends on the salt: two runs here gave 564 and 594. The instability shows up even in the measurement of the collision rate.

Run through this repository's own convert_to_coco_api and CocoEvaluator, a detector whose predictions are the ground truth scores:

ids mAP@[.5:.95]
one id per sample (control) 1.0000
drawn from 1e6, 4,000 samples 0.9901  (0.99% low)
every id shared by two samples 0.5050  (49.5% low)

The last row is not the real rate — it is an exaggeration that isolates the mechanism, since res[image_id] = out keeps only the last of a colliding pair while the ground truth keeps both. At realistic rates the collision costs about a point. The reproducibility is the reason to change the line.

The change

stable_image_id takes 13 hex digits of sha256: identical in every process, and 52 bits keeps every id an exact JavaScript integer for COCO JSON consumers while putting collisions out of reach — 0 over 1,000,000 tokens, measured.

Considered and rejected: crc32 (stable, but starts colliding above ~100k), and passing the sample index (unique by construction, but it changes get_target's signature and makes the id depend on --max-samples rather than on the sample).

Tests

Adds detection/test_nuscenes_image_id.py — 12 tests, no download, no GPU: 12 passed.

One thing this suite cannot do, and I would rather say so than imply otherwise: it does not run red against main. stable_image_id is new, so against the current file the suite fails to import and pytest reports a collection error rather than behavioural failures. What stands in for that is test_the_builtin_hash_really_is_unstable_here, which measures hash() across seeds directly and would fail if the premise of this change were wrong — plus the mAP figures above, which come from your own evaluator.

cv2 is stubbed in the test only when genuinely absent: dataset_nuscenes imports it at module scope and no code under test touches it. A test reports whether the stub was used, so a stubbed run cannot be mistaken for a clean one.

pytest DeepDataMiningLearning/detection/test_nuscenes_image_id.py

@gitguardian

gitguardian Bot commented Aug 30, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@egeboy35
egeboy35 force-pushed the fix/nuscenes-image-id-is-reproducible branch 2 times, most recently from 0cd452c to 3ee963f Compare September 1, 2026 12:28
…w process

dataset_nuscenes.py:433 builds every target's COCO image id as

    'image_id': torch.tensor([hash(sample_token) % 1000000]),

and cocoevaluator.py keys both halves of the evaluation on it: the ground
truth at convert_to_coco_api line 300, the predictions at `res[image_id] =
out` on line 370.

`hash()` on a str is salted per interpreter (PEP 456). One token, six fresh
interpreters:

    PYTHONHASHSEED=0        -> 489194
    PYTHONHASHSEED=1        -> 812316
    PYTHONHASHSEED=2        -> 270356
    PYTHONHASHSEED=random   -> 473704
    PYTHONHASHSEED=random   -> 887793
    PYTHONHASHSEED=random   -> 561686

Six ids for one sample. So a prediction file written in one run cannot be
scored against ground truth rebuilt in another, and the two processes of a
distributed run disagree about which image is which. That is the part of this
that does not get smaller with a bigger machine or a smaller dataset.

The 1e6 range is the second, milder half. Over the 34,149 keyframes of
v1.0-trainval, a 1e6 range loses about 555 ids to collisions -- "about",
because the exact count depends on the salt: two runs here gave 564 and 594.
The instability shows up even in the measurement of the collision rate.

I would rather give the honest size of that half than the dramatic one. Run
through this repository's own convert_to_coco_api and CocoEvaluator, a
detector whose predictions ARE the ground truth scores

    one id per sample (control)              mAP 1.0000
    ids drawn from 1e6, 4,000 samples        mAP 0.9901   (0.99% low)
    every id shared by two samples           mAP 0.5050   (49.5% low)

The last row is not the real rate -- it is an exaggeration that isolates the
mechanism, since `res[image_id] = out` keeps only the last of a colliding pair
while the ground truth keeps both. At realistic rates the collision costs about
a point; the reproducibility is the reason to change the line.

`stable_image_id` takes 13 hex digits of sha256: identical in every process,
and 52 bits keeps every id an exact JavaScript integer for COCO JSON consumers
while putting collisions out of reach -- 0 over 1,000,000 tokens, measured.
Considered and rejected: crc32 (stable, but starts colliding above ~100k), and
passing the sample index (unique by construction, but it changes get_target's
signature and makes the id depend on --max-samples rather than on the sample).

Adds detection/test_nuscenes_image_id.py -- 12 tests, no download, no GPU:
12 passed.

One thing this suite cannot do, and I would rather say so than imply
otherwise: it does not run red against main. `stable_image_id` is new, so
against the current file the suite fails to import and pytest reports a
collection error rather than a set of behavioural failures. What stands in for
that is test_the_builtin_hash_really_is_unstable_here, which measures `hash()`
across seeds directly and would fail if the premise of this change were wrong,
and the mAP figures above, which come from the repository's own evaluator.

`cv2` is stubbed in the test only when genuinely absent -- dataset_nuscenes
imports it at module scope and no code under test touches it. A test reports
whether the stub was used, so a stubbed run cannot be mistaken for a clean one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@egeboy35
egeboy35 force-pushed the fix/nuscenes-image-id-is-reproducible branch from 3ee963f to 702af04 Compare September 1, 2026 12:43
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