Repository navigation
v1.11.1: Correctness Fixes for LoRA, TensorRT & Crowd mAP
RF-DETR 1.11.1 is a correctness patch. It turns silent wrong results into loud, correct ones:
- LoRA backbone —
backbone_lora=Truecheckpoints trained on a random encoder, no error. Now loads correctly. - TensorRT inference — wrong-device/dtype/non-contiguous inputs returned wrong detections, no error. Now refused.
- Crowd-region mAP — val/test mAP overcounted false positives on COCO crowd regions. Now matches pycocotools.
Most users need no action. 4 of the 5 "breaking" changes only affect code that was already getting a wrong result or an uncaught KeyError — this release just stops hiding it. See the Migration guide for exact before/after code.
The one real API change: RFDETRModelModule.clip_gradients → configure_gradient_clipping. Only affects code that calls clip_gradients directly, or relies on super().clip_gradients(...) inside an override.
Everything else is a bug fix or documentation update.
✨ Spotlights
backbone_lora=True checkpoints silently trained a random encoder — now fixed
RFDETR.from_checkpoint(), RFDETRSmall(pretrain_weights=...), and train() all loaded a LoRA checkpoint before wrapping the encoder with LoRA, so none of the stored PEFT-named weights matched — the encoder silently stayed at random initialization, and evaluate() scored near zero with no error.
# Before (1.11.0): silent — encoder never actually reloads
model = RFDETRLarge(pretrain_weights="checkpoint_best_total.pth", backbone_lora=True)
model.evaluate() # mAP50:95 ≈ 0.0001 — looks broken, no exception
# After (1.11.1): encoder is wrapped before load, weights match
model = RFDETRLarge(pretrain_weights="checkpoint_best_total.pth", backbone_lora=True)
model.evaluate() # mAP50:95 ≈ 0.6221 — same as the in-memory trained modelMeasured: RF-DETR Large @640, LoRA-tuned on 262 COCO person images. (#1543)
last.ckpt and interval checkpoints now carry enough to reload
# Before (1.11.0)
RFDETR.from_checkpoint("output/last.ckpt") # KeyError: 'args'
# After (1.11.1)
model = RFDETR.from_checkpoint("output/last.ckpt") # rebuilds trained resolution/architectureFiles written by 1.11.0 and earlier can't be repaired in place — from_checkpoint now raises a named ValueError pointing at checkpoint_best_ema.pth and training_config.json instead. (#1561)
Val/test mAP now matches pycocotools on COCO crowd regions
Detections inside iscrowd=1 regions no longer count as false positives. Numbers shift on crowd-labelled data — Nano COCO val2017: 0.4802 → 0.4842 mAP. Training is unchanged. (#1532)
TRTInference refuses inputs TensorRT would misread
# Before (1.11.0): silent wrong output
trt_model(image_tensor.to(memory_format=torch.channels_last)) # 13 detections → 0, no error
# After (1.11.1): explicit refusal
trt_model(image_tensor.to(memory_format=torch.channels_last))
# ValueError: Input 'input' is not contiguous (strides (...)), and TensorRT reads it as dense
# row-major memory. Pass tensor.contiguous().(#1546)
RFDETRModelModule.clip_gradients renamed → configure_gradient_clipping (Breaking)
# Before (1.11.0)
class MyModule(RFDETRModelModule):
def clip_gradients(self, optimizer, gradient_clip_val=None, gradient_clip_algorithm=None): ...
# After (1.11.1)
class MyModule(RFDETRModelModule):
def configure_gradient_clipping(self, optimizer, gradient_clip_val=None, gradient_clip_algorithm=None): ...Moves the keypoint fp16 clipping fix onto the Lightning hook meant for it — clip_gradients itself now reverts to Lightning's base implementation. Narrower than it looks: configure_gradient_clipping's non-fused branch calls self.clip_gradients(...) (dynamic dispatch, not super()), so a subclass overriding only clip_gradients with self-contained logic still runs unchanged. Actually affected: code calling clip_gradients directly, a subclass override relying on super().clip_gradients(...) for RF-DETR's old fused-aware logic, and any clip_gradients override on the fused-AdamW path (never reached there, in 1.11.0 or 1.11.1 — see MIGRATION.md for the full breakdown). (#1537, #1549)
🔄 Migration guide
5 breaking changes — 1 API rename, 3 stricter-validation changes, 1 exception-type change. All are covered in full, with before/after code, in MIGRATION.md; none were previously documented in docs/getting-started/migration.md.
A new overridable classmethod, Exporter.check_dependencies (src/rfdetr/export/base.py), lets a custom exporter subclass judge whether the host has what it needs to build, separately from _check_capabilities judging the request. Not documented yet in docs/exports/blueprint.md — only relevant to third-party Exporter subclasses, no action needed for RFDETR.export() callers. (#1542)
📝 Notable changes
🚀 Added
rfdetr.export._benchmark(private):measure_latencyandmeasure_memoryhelpers shared by the new per-hardware export cookbooks (CUDA-event timing on GPU, wall-clock/RSS otherwise). Replaces the ONNX-onlyrfdetr.export._onnx.inference._onnx_runtime, which is removed. (#1530)decode_pil_image/decode_pil_image_bytes(rfdetr.datasets.io_utils) — decode straight to a PIL image under the same decoder policy as the array-out decoders, without the NumPy round trip. (#1551)CrowdRegion/crowd_regions_from_coco(rfdetr.datasets.coco) — readsiscrowd=1annotations back from a COCO API for evaluation metrics that need to score them. (#1532)resize_masks_nearest(rfdetr.evaluation.matching) — nearest-neighbor mask resize used by the new crowd-region evaluation path. (#1532)rfdetr.utilities.package(new module):is_installed(name)— checks whether a package is importable without importing it, used by the new fail-fast TensorRT dependency check. (#1542)Exporter.check_dependencies(src/rfdetr/export/base.py) — new overridable classmethod hook; see Migration guide. (#1542)
⚠️ Breaking Changes
RFDETRModelModule.clip_gradientsis renamed toconfigure_gradient_clipping;clip_gradientsreverts to Lightning's base implementation. Only affects direct callers ofclip_gradients, asuper().clip_gradients(...)call inside an override, or an override on the fused-AdamW path — a self-containedclip_gradientsoverride still runs on the non-fused path viaself.clip_gradients(...)dispatch. SeeMIGRATION.md. (#1537, #1549)TRTInferencenow raisesValueErrorfor an input on another device, of another dtype, or non-contiguous, instead of returning wrong detections. (#1546)TensorRTExporter.build_enginenow raisesValueErroron adynamic_batch=TrueONNX unless itsTensorRTConfigsetsdynamic_batch=Trueandmax_batch_size.RFDETR.export()is unaffected. (#1542)RFDETR.from_checkpoint()on a checkpoint with no"args"now raisesValueErrorinstead ofKeyError: 'args'. (#1561)- Loading a
backbone_lora=Truecheckpoint withoutpeftinstalled now raisesImportErrorinstead of silently loading a random encoder. (#1543)
🌱 Changed
- Export cookbooks restructured per hardware target (
export-cuda/export-cpu/export-mobile/export-apple), replacing 4 per-format ones; adds first coverage for ONNX, OpenVINO, LiteRT, CoreAI. (#1530) RFDETR.from_checkpoint()no longer restoresdevicefrommodel_config— the loading host's default wins; passdevice=explicitly. (#1534, #1561)[coreai]extra pinscoreai-torch==0.4.3, widens Python range to<3.15, and declares a uv conflict with[tflite]. (#1527)- The
e2e_onnxruntimepytest marker is renamede2e_onnxinpyproject.toml. Contributor-facing only:pytest -m e2e_onnxruntimenow silently selects nothing (pytest doesn't error on an unmatched-m) — update local invocations and CI configs toe2e_onnx. (#1530) amp_dtype="auto"now picks fp16 on CUDA GPUs without native bf16 (T4, V100); bf16 only when every selected GPU is Ampere or newer. Loss curves/checkpoints across this change are not comparable on pre-Ampere GPUs. (#1538)- Val/test mAP now ignores detections on COCO crowd regions, matching pycocotools. (#1532)
TrainConfig.clip_max_normnow rejects a negative value at construction instead of silently training unclipped. (#1537, #1549)
❌ Removed
rfdetr.export._onnx.inference._onnx_runtime(private ONNX-only benchmark helper) — superseded byrfdetr.export._benchmark. (#1530)
🔧 Fixed
- YOLO datasets stored under a directory named
imagesnow resolve their labels directory correctly. (#1557) last.ckpt/interval checkpoints record model class, config, class names, and rfdetr version;from_checkpointno longer loses the dataset's class names. (#1561)- COCO training on non-JPEG images (or JPEG without
simplejpeg) is back to 1.10 decode speed — up to 27% faster per batch on large BMP files. (#1551) format="coreai"works withcoreai-torch0.4.3. (#1527)from_checkpoint("checkpoint_best_total.pth")rebuilds the trained architecture instead of falling back to class defaults. (#1534)- Keypoint fp16 training clips the real gradient (was clipping before GradScaler unscale, silently stalling training). (#1537, #1549)
amp_dtype="auto"no longer selects emulated bf16 on T4/V100 — Colab T4 Nano step 443ms → 226ms in fp16. (#1538)- Crowd-region detections no longer counted as false positives in COCO eval. (#1532)
TRTInference.build_engineno longer truncates an existing.trtfile on a failed build. (#1546)- An undeserializable TensorRT engine reports a named
RuntimeErrorinstead ofAttributeError. (#1546) TRTInference(device=...)now loads and runs the engine on that device (was mixing GPUs). (#1546)- TensorRT export refusal messages name working export settings instead of a bare flag. (#1546)
backbone_lora=Truecheckpoints reload their trained backbone (was silently random-init; measured val mAP50:95 0.0001 → 0.6221). (#1543)RFDETR.export(format="tensorrt")withouttensorrtinstalled fails before the ONNX export with a namedImportError. (#1542)compile=Truewith defaultmulti_scalestops recompiling the transformer per resolution. (#1539)- COCO segmentation datasets mixing annotated and box-only instances keep every mask. (#1558)
- Fixed JSON serialization in the COCO val-subset builder. (
fd3bb991, no PR — direct commit)
📚 Documentation
- Documented
TrainConfig.eval_backendin the advanced training guide. (#1526) - Scoped the static INT8 export limit to TFLite specifically. (#1559)
🏆 Contributors
- Jirka Borovec (@Borda, LinkedIn) — restructured export cookbooks by hardware target, replacing four per-format notebooks; fixed JSON serialization in the COCO val-subset builder; unified export CI jobs into one matrix.
- Lương Thanh Hoàng Phú (@lthphuw, LinkedIn) — hardened
TRTInferenceto reject mismatched device/dtype/contiguity instead of returning silently wrong detections; fixed checkpoints to record training args and model config sofrom_checkpointno longer raisesKeyError; fixed LoRA backbone reload, which had silently stayed at random init. - Raghav Rathi (@raghav-rathi, LinkedIn) — fixed keypoint gradient clipping under fp16, which ran before GradScaler unscale and silently stalled training; fixed
amp_dtype="auto"selecting emulated bf16 on GPUs without native support; excluded COCO crowd-region detections from val/test mAP. - Mohammad Hijjawi (@MohammadHijjawi97, LinkedIn) — fixed COCO segmentation to keep all masks when an image's first annotation has no
segmentationfield; fixed YOLO labels-directory resolution for datasets nested under animagesdirectory. - Christoph Deil (@cdeil, LinkedIn) — fixed CoreAI export compatibility with
coreai-torch0.4.3 after it removedAIProgram.optimize().
Automated contributions from claude[bot], pre-commit-ci[bot], Copilot Autofix powered by AI, copilot-swe-agent[bot], OpenAI Codex, Claude Sonnet 5, Claude Opus 5 (1M context), and Claude Opus 5.5 supported this release.
Full changelog: 1.11.0...1.11.1