v1.9.2: Fixed Roboflow COCO & perf. optim
RF-DETR 1.9.2 is a fix-and-performance release: one breaking change, no new public APIs. Hierarchical COCO datasets — most commonly Roboflow exports carrying a synthetic unannotated grouping category — now get a correct, shared label mapping across train/valid/test, closing a case where per-class metrics could silently corrupt on the smaller split. The matcher's compact cost-matrix path is faster and lower-memory on real batches, training-resume from BestModelCallback's lightweight checkpoints now restores per-callback state instead of restarting cold, and a handful of training/eval fixes round it out.
✨ Spotlights / highlights
Breaking: hierarchical COCO datasets get a correct, shared label space
Roboflow COCO exports prepend a synthetic root category (unannotated, supercategory: "none") that every real class lists as its own supercategory. It never carried annotations but still consumed a label slot — training such a dataset built an N+1-class head instead of N.
from rfdetr.datasets.coco import filter_parent_categories, annotated_category_ids
categories = [
{"id": 0, "name": "project-root", "supercategory": "none"},
{"id": 1, "name": "car", "supercategory": "project-root"},
{"id": 2, "name": "truck", "supercategory": "project-root"},
]
anns = {"annotations": [{"category_id": 1}, {"category_id": 2}]}
kept = filter_parent_categories(categories, annotated_category_ids(anns))
[c["name"] for c in kept]
# -> ['car', 'truck'] — the unannotated root no longer takes a label slotFor datasets where a parent category does carry its own annotations, label indices are now derived once from the train split and shared into valid/test, so a grouping category annotated in one split but not another no longer shifts that split's label indices independently of the others.
⚠️ Checkpoints trained before 1.9.2 keep their old head width and label ordering. Evaluating one against a re-filtered dataset misaligns per-class metrics (an existingUserWarningfires). See the migration notes below.
Matcher: faster and lower-memory on real COCO batches
HungarianMatcher's detection-only cost matrix is now built padded to each batch's max(T_i) target count instead of the cross-image sum(T_i), when a fast eligibility check passes — identical results on eligible batches, automatic full-computation fallback otherwise.
| before | after | change | |
|---|---|---|---|
| Matcher time | — | — | ~51% faster |
| Peak CUDA memory | — | — | ~73–76% lower |
| Training step (A100) | 288.364 ms | 232.457 ms | — |
The saving scales with target-count evenness across a batch — a batch where nearly all targets sit in one image sees little to no improvement.
Training resume restores per-callback state
Resuming from one of BestModelCallback's four lightweight checkpoints (checkpoint_best_regular.pth, checkpoint_best_ema.pth, checkpoint_best_total.pth, last_ema.pth) previously restarted best-score tracking, EMA, and early-stopping cold, silently. It now restores that state:
model.train(
dataset_dir="my_dataset",
resume="output/checkpoint_best_ema.pth",
output_dir="output", # must match the original run for best-score restore
)📝 Notable changes
🌱 Changed
HungarianMatchercompact-path speedup — detection-only cost matrix padded tomax(T_i)instead ofsum(T_i); ~51% faster matcher, ~73–76% lower peak CUDA memory on real COCO batches. Compact path also copies only diagonal cost blocks to CPU and batches its safety-gate sweeps into one sync. (#1297, #1281, #1312)- Deterministic-algorithm coverage —
seed_all()now also enablestorch.use_deterministic_algorithms(True, warn_only=True); ops without a deterministic kernel warn at execution time instead of raising. (#1307) predict()— pins CPU image tensors before the CUDA transfer. (#1313)- Two-stage query selection — avoids materialising repeated top-k gather indices. (#1278)
- Evaluation matching — counts labels on host instead of device. (#1276)
- Keypoint postprocessing — skips redundant CUDA presence checks. (#1282)
🔧 Fixed
BestModelCallbackresume — resuming from a lightweight checkpoint (checkpoint_best_*.pth,last_ema.pth) now restores per-callback state instead of restarting best-score tracking, EMA, and early-stopping cold; a warning distinguishes checkpoints that intentionally omit optimizer state from ones that predate callback-state persistence entirely. (#1318)- Rich epoch progress bar — no longer corrupted or duplicated by training-time log calls under
RichProgressBar(leave=True). (#1316) _kp_active_maskfalse-positive warning — loading a pre-keypoint-support checkpoint no longer warns about a deterministic schema buffer as a missing parameter. (#1302)- Deferred CUDA-device move — an index-less
torch.device("cuda")is normalised to the current device index before comparison, fixing spurious re-moves of every parameter on every call. (#1311) - Legacy query-embedding fallback — only warns when it actually truncates weights. (#1301)
eval_ema_only— no longer logs an empty validation pass when the base metric is empty; EMA metrics (val/ema_mAP_50_95,val/ema_mAP_50,val/ema_mAR, per-class AP,val (ema)summary table) are computed instead, andval/F1is no longer silently dropped. (#1289)ModelContext.reinitialize_detection_head— raises a clearRuntimeErrorinstead ofAttributeError: 'NoneType'afterRFDETR.inference(inplace=True)clears the weights. (#1283)evaluate()— builds its datamodule from the resolution-override config. (#1280)
⚠️ Breaking Changes
- Hierarchical COCO label filtering — unannotated grouping categories no longer consume a label slot;
train/valid/testnow share one label mapping derived fromtrain. Checkpoints trained before this change need retraining against the new label space — see Migration guide. (#1303)
🏆 Contributors
- @JESUSROYETH — matcher perf (padded cost matrix, diagonal-only CPU copy, batched safety-gate sync),
BestModelCallbackresume-state restore, Rich progress-bar fix,eval_ema_onlyfix, and five other training/inference fixes - @atikulmunna (LinkedIn) — guarded
ModelContext.reinitialize_detection_headagainst cleared weights; strict-typedrfdetr.detranddatasets.yolomodule boundaries - @Jonathan-Jesni — strict-typed
rfdetr.datasets.save_gridsmodule boundary - @Borda (LinkedIn) — hierarchical COCO label-filtering fix and release maintenance
Full changelog: 1.9.1...1.9.2