1.11.0
Textualize/textual1.11.0Sep 24, 2026by Borda
Upgrading?
AI Summary
Introduces new export formats (OpenVINO, LiteRT, Core AI), dynamic-batch TensorRT, and CUDA graph training, while removing deprecated legacy features and restructuring export internals.
Key Highlights
- New export formats: OpenVINO IR, LiteRT, and Apple Core AI.
- Dynamic-batch TensorRT engines via a single optimization profile.
- Opt-in CUDA graph training replay for faster single-GPU training.
- WebDataset streaming reader for tar-shard training input.
- Export internals restructured into format-specific `Exporter` classes.
Breaking Changes
- Export internal modules and classes renamed (e.g., `export_onnx` -> `OnnxExporter`).
- `TrainConfig.multi_scale` is now a three-state enum; `do_random_resize_via_padding` removed.
- `RFDETR.optimize_for_inference()` removed (renamed to `inference()`).
- `TrainConfig.lr_drop`/`lr_min_factor` removed (moved to kwargs).
- `rfdetr.datasets.synthetic` module removed.
New Features
- OpenVINO IR export support with configurable precision.
- Apple Core AI export support for iOS/iPadOS/macOS 27+.
- CUDA graph training support combinable with `torch.compile`.
- WebDataset streaming support for tar-shard training.
- Dynamic batch TensorRT export support.
Full Release Notes
RF-DETR 1.11.0 widens the export surface and speeds up training. Three new export formats land — OpenVINO IR (with configurable precision), LiteRT via litert-torch, and Apple Core AI (`.aimodel`, for iOS/iPadOS/macOS 27+) — alongside dynamic-batch TensorRT engines built through a single optimization profile, so one engine now serves a batch range instead of one fixed size. Training gains opt-in CUDA-graph replay (combinable with `torch.compile`, plus a Transformer Engine FP8 capture route on supported hardware) and a WebDataset streaming reader for tar-shard training input at scale.
This release also completes a deprecation cycle from 1.9.0: `RFDETR.optimize_for_inference()`, `TrainConfig.lr_drop`/`lr_min_factor`, and the standalone synthetic-dataset generator are removed, and export internals were restructured into one `Exporter` class per format behind a registry. `RFDETR.export()`'s own signature is unchanged, but code importing the old converter functions directly needs updated imports. See the Migration guide below for exact before/after code.
## ✨ Spotlights / highlights
### Breaking: export internals restructured into one `Exporter` class per format
Each export format (`onnx`, `openvino`, `coreml`, `executorch`, `tflite`, `tensorrt`, `litert`) is now written by a `<Format>Exporter` class built from that format's own frozen config dataclass, reached through a registry. `RFDETR.export()`'s own signature and return value are unchanged — this only breaks code that imported the old converter functions directly. (#1448)
```python
# Before (removed)
from rfdetr.export._onnx.exporter import export_onnx
# After
from rfdetr.export._onnx.exporter import OnnxExporter
```
### New: OpenVINO IR export
```python
from rfdetr import RFDETRNano
model = RFDETRNano()
model.export(format="openvino", openvino_precision="float32", output_dir="output")
# -> output/rfdetr-nano.xml + output/rfdetr-nano.bin
```
`openvino_precision` accepts `"float32"`, `"float16"`, or `None`. Install with `pip install "rfdetr[openvino]"`. (#1238)
### New: Apple Core AI export
```python
from rfdetr import RFDETRNano
model = RFDETRNano(pretrain_weights="<path/to/checkpoint.pth>")
model.export(format="coreai")
# -> output/rfdetr-nano_fp32.aimodel
```
Experimental. Targets iOS, iPadOS and macOS 27+ via `coreai-torch`, no ONNX step. `coreai_precision="float16"` halves asset size; the exporter runs the two-stage `topk` in float32 even in a float16 graph because the Neural Engine returns corrupt float16 `topk` indices. Install with `pip install "rfdetr[coreai]"` (Python 3.11-3.13, macOS Apple silicon or Linux x86-64 to build the asset). (#1521)
### New: opt-in CUDA graph training replay, combinable with `compile` and FP8
```python
model = RFDETRNano(cuda_graphs=True)
# or, delegated to Inductor's own CUDA graph trees:
model = RFDETRNano(cuda_graphs=True, compile=True)
```
Single-GPU detection training only. BF16/eager route supports gradient accumulation; a separate FP8 route through Transformer Engine (`amp_dtype="fp8"`, `compile=False`) is limited to `grad_accum_steps=1`. Measured on an RTX PRO 6000 (Nano, batch 64, BF16): `compile=True` alone is ~17% faster than eager; `cuda_graphs=True` combined with `compile=True` is fastest at large batch. (#1468, #1479, #1481)
### New: stream training input directly from WebDataset tar shards
```python
from rfdetr.config import TrainConfig
config = TrainConfig(dataset_file="webdataset", dataset_dir="path/to/shards")
```
Pack shards with `python -m rfdetr.cli.webdataset`. Requires the `data` extra: `pip install "rfdetr[data]"` (not `[webdataset]` — that extra never existed). (#1396)
### New: dynamic-batch TensorRT engines through a single optimization profile
```python
model.export(format="tensorrt", dynamic_batch=True, batch_size=4, max_batch_size=16)
```
One engine now accepts any batch from 1 to `max_batch_size`, tuned for `batch_size`; without `dynamic_batch` the engine still accepts only the batch size baked into the intermediate ONNX graph. (#1511)
## 🔄 Migration guide
### Breaking changes
**The export internals are now exporter classes.** `RFDETR.export()`'s own signature and return value are unchanged — only the internal modules moved (see table below). This matters only if you imported the converter functions directly. Every path with a leading underscore was already internal and carried no stability guarantee. (#1448)
| Removed | Replacement |
| ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| `rfdetr.export.main` (whole module, including `main()` and `make_infer_image`) | `rfdetr.export.prepare.make_infer_image`; the `main()` CLI had no entry point and is gone |
| `rfdetr.export.protocols.ExporterProtocol` | `rfdetr.export.base.Exporter` |
| `rfdetr.export._onnx.exporter.export_onnx` | `rfdetr.export._onnx.exporter.OnnxExporter` |
| `rfdetr.export._openvino.exporter.export_openvino` | `rfdetr.export._openvino.exporter.OpenVINOExporter` |
| `rfdetr.export._coreml.converter.export_coreml` | `rfdetr.export._coreml.exporter.CoreMLExporter` |
| `rfdetr.export._executorch.converter.export_executorch` | `rfdetr.export._executorch.exporter.ExecuTorchExporter` |
| `rfdetr.export._tflite.converter.export_tflite` | `rfdetr.export._tflite.exporter.TFLiteExporter` |
| `rfdetr.export._tensorrt.build_engine` | `rfdetr.export._tensorrt.exporter.TensorRTExporter` |
| `rfdetr.export.benchmark.TRTInference` | `rfdetr.export._tensorrt.inference.TRTInference` |
**`format="tensorrt"` with `backbone_only=True` and `output_name` now marks the engine.** The engine is named `{output_name}-backbone.trt` instead of `{output_name}.trt` — without the marker a backbone engine silently overwrote a full-detector engine exported under the same name. (#1448)
**`TrainConfig.multi_scale` is now a three-state enum; `do_random_resize_via_padding` is gone.** Booleans still work as input — `True` normalizes to `PER_BATCH` (the old default's behavior), `False` to `OFF`. (#1485)
```python
# Before
TrainConfig(multi_scale=True, do_random_resize_via_padding=True)
# After
TrainConfig(multi_scale="per-sample")
```
### Deprecated (removal in v1.14)
- `ModelConfig.amp` → `TrainConfig.amp_dtype`, which now accepts `None` to disable autocast entirely. An explicit `amp_dtype` always wins over the deprecated `amp` flag. (#1471)
- `TrainConfig.fp16_eval` — no runtime consumer since the PyTorch Lightning migration; evaluation precision follows `amp_dtype`.
### Removed (deprecated since v1.9.0)
- `RFDETR.optimize_for_inference()` → `RFDETR.inference()`, same signature. (#1500)
- `TrainConfig.lr_drop` / `lr_min_factor` → pass through `lr_scheduler_kwargs={"lr_drop": ..., "min_factor": ...}`. (#1500)
- `rfdetr.datasets.synthetic` (whole module) → use the [`fuse-augmentations`](https://github.com/Borda/fuse-augmentations) package's `fuse_augmentations.data` module. (#1435)
Full before/after code for every item above: [Migration Guide](../../docs/getting-started/migration.md#upgrade-110--111).
## 📝 Notable changes
### 🚀 Added
- **OpenVINO IR export** — `RFDETR.export(format="openvino", openvino_precision=...)`. `openvino_precision` picks `"float32"`, `"float16"`, or `None` to control IR weight precision; dynamic batch is not supported for this format yet. Install with `[openvino]`. (#1238)
- **LiteRT export** via litert-torch — `format="litert"` captures the model directly with `torch.export` and lowers it to `.tflite`, skipping the ONNX/TensorFlow hop the existing `format="tflite"` route takes. Experimental: float32 only, CPU (XNNPACK) delegate only, no dynamic batch, keypoint models unsupported on litert-torch 0.9.4, and its own dependency pins `torch<2.14`. Install with `[litert]`. On the pretrained Nano/Seg-Nano checkpoints it tracks eager PyTorch to about `1e-7` (boxes) and `3e-5` (class logits, mask probabilities) over the confident queries, up to `2e-3` at worst over all 300 raw mask logits; the single-level deformable-attention core no longer emits a one-output `split` in export graphs, the one op litert-torch couldn't lower, so every other export route's numbers are unaffected. (#1459)
- **Apple Core AI export** — `format="coreai"` writes an Apple Core AI `.aimodel` for iOS/iPadOS/macOS 27+ through `torch.export` and `coreai-torch`; `coreai_precision="float16"` halves asset size, and `notes` are stored in the asset metadata. Experimental. Install with `[coreai]`. The export decomposes `aten.grid_sampler_2d`, which `coreai-torch` cannot lower, and runs float16 two-stage `topk` in float32 because the Neural Engine returns corrupt float16 `topk` indices; detection, segmentation and keypoint models match eager PyTorch within 1e-4 on CPU in the new `e2e_coreai` suite, which runs on GitHub's macOS 27 (`xcode-27`) runner. (#1521)
- **Dynamic-batch TensorRT engines** — `dynamic_batch=True` with `max_batch_size` builds one engine spanning a single TensorRT optimization profile (`min=1`, `opt=batch_size`, `max=max_batch_size`) instead of a separate engine per batch size. The engine was previously refused for a dynamic ONNX graph only because no profile was passed to the build; `TRTInference` now allocates a dynamic engine's bindings at the profile's max batch and trims outputs per call, and its async path moves off `execute_async_v2` (removed in TensorRT 11) to `execute_async_v3`. Verified within `1e-2` of eager PyTorch on an RTX 4090 (batch 1/3/4 against a `batch_size=2, max_batch_size=4` engine, plus a rejected batch of 5). Latency, FP16, median of 5x100 runs: RTX 4090 Nano-384px static 0.88/1.49/2.25 ms at batch 1/4/8 vs. dynamic 1.30/1.52/2.60 ms, Medium-576px static 1.31/2.66/4.67 ms vs. dynamic 1.70/2.69/5.06 ms; a Colab T4 shows the same pattern at higher absolute latency. At the profile's `opt` batch the dynamic engine is within ~2% of a static one on the 4090 (~5% on the T4); other batch sizes pay 1-48% more — the trade for one file instead of one engine per batch size. (#1511)
- Public export inference wrapper module, `rfdetr/export/inference.py`, replacing the previous private `rfdetr.export._openvino.inference` import; the ONNX and TFLite inference helpers now route through shared `preprocess_to_nchw`/`decode_detections` helpers instead of duplicating that logic each. (#1446)
- `backbone_only=True` now exports the encoder plus feature projectors instead of calling the full detector and raising `AttributeError`. (#1438)
- Opt-in CUDA-graph training replay for single-GPU detection training, landed across three PRs:
- `ModelConfig.cuda_graphs=True` captures each static input shape's forward and backward once and replays it on later batches (BF16 only; segmentation, keypoints, non-BF16/FP8 precision, distributed runs and gradient checkpointing stay eager with a warning, and a failed capture stops training with a process-restart instruction rather than retrying in a damaged CUDA context). On an NVIDIA L4 (RF-DETR Nano, batch 4, 8-resolution multi-scale set), the median training batch fell from 149.8 ms eager to 101.7 ms graphed — a 32.4% median reduction across 5 paired runs, all 8 signatures capturing every run. Peak allocated CUDA memory rose from 2,395 MiB to 3,466 MiB and peak reserved memory from 2,754 MiB to 14,550 MiB for the 8 graph pools (one per resolution). On real data at the other end of the range (Nano, RTX PRO 6000, batch 64, `multi_scale=False`, COCO train2017), graph replay matched eager at 3.54 it/s while `compile=True` cut the epoch from ~8m40s to ~7m10s — graphs remove launch gaps and pay off at small batch, compilation fuses kernels and pays off at large batch. (#1468)
- Combinable with `compile=True`: Inductor's `triton.cudagraphs` option records and replays the compiled kernels, single-GPU detection only, no gradient accumulation (segmentation, keypoints, `grad_accum_steps > 1` and multi-device fall back to compile-only). On an RTX PRO 6000 (Nano, BF16), 1.20x faster than compile-alone at batch 4, matching it at batch 64; an A100 gave 1.70x at batch 4. `amp_dtype="fp8"` with both flags stays compile-only — the two capture runtimes are never nested. (#1479)
- A Transformer Engine FP8 capture route (`amp_dtype="fp8"`, `compile=False`) captures the fixed-shape training step through `make_graphed_callables` under the active Lightning FP8 recipe, with gradients cloned before Transformer Engine reclaims its buffers; needs Transformer Engine 2.19+ (`clone_param_grads_on_return`), an older release raises rather than silently falling back. Scope: single-GPU, `grad_accum_steps=1`, `multi_scale=False`, `square_resize_div_64=True`. Synthetic RF-DETR Nano step on an RTX PRO 6000 Blackwell: 78.075 ms eager vs. 40.066 ms graphed (~1.95x), one capture serving 70 calls — a single fixed-shape synthetic measurement, not COCO parity or large-batch evidence. (#1481)
- FP8 training precision — `amp_dtype="fp8"` reaches Transformer Engine's hardware FP8 path directly, independent of the CUDA-graph capture route above. (#1431)
- `TrainConfig.eval_backend` selects the COCO evaluator used for validation/test mAP, gaining two more values on top of the existing `"hotcoco"` (default) and `"faster_coco_eval"`:
- `"ufcoco"`, selecting [ultrafast-pycocotools](https://github.com/developer0hye/ultrafast-pycocotools) (Rust, BSD-2-Clause, `numpy`-only runtime dependency) that reproduces pycocotools' precision, recall and score arrays byte for byte; parity tests hold it to exact equality against `faster_coco_eval` for box-only and box-plus-mask evaluation at `eval_max_dets` 100 and 500. (#1449)
- `"vernier"`, selecting [vernier](https://github.com/NoeFontana/vernier) (Rust, MIT OR Apache-2.0), run in its `corrected` parity mode, which corrects the same pycocotools quirks as `hotcoco` and `faster_coco_eval`. (#1491)
- `dataset_file="webdataset"` streams training input from pre-packed `.tar` shards instead of opening one file per image, with `python -m rfdetr.cli.webdataset` to pack a COCO split into ~100 MB shards and a small JSON index; packing needs no extra dependency, reading installs with `[data]`. Verified tensor-for-tensor identical to the source directory on 500 real COCO images (pixels, boxes, labels, label space). Training refuses to start when `world_size × num_workers` exceeds the shard count, and now raises (not just warns) past 30% shard-split skew. On a 32-vCPU NVMe instance, streaming throughput matched the loose-file loaders across six configurations (0.92x-1.03x) and construction was far faster (median 19.9s to 0.012s per DDP rank) since the annotation file is never parsed — packing pays off where per-file access is the real constraint, not raw loader throughput. Accuracy isn't established either way: over five seeds of a 3-epoch fine-tune, streaming trailed map-style by ~0.011 mAP@50:95 at 39 shards and matched it at 290, with more seed-to-seed spread in both. Detection/segmentation only — keypoint rejects the format, since its label space needs a fully parsed COCO file a shard index doesn't carry. (#1396)
- `TrainConfig.pad_targets_to` pads every image's targets to a fixed row count so the detection loss keeps one tensor shape across batches, avoiding per-batch XLA recompilation on TPU driven by the ground-truth box count (issue #1433: a new count cost ~47s and four fresh compilations vs. 0.18s repeated, never reaching steady state on realistic data). On a Cloud TPU v6e-1, this cut compiles/time from 78/1453.9s unpadded to 9/159.0s median padded. Requires `augmentation_backend` off Kornia/GPU; excess boxes past the cap are dropped with a warning; applies to the training dataloader only. Semantically transparent — padded rows carry no cost in matching or loss (masked); the unmasked classification branches, segmentation mask loss and keypoint loss raise rather than silently reporting a wrong loss. (#1450)
- `RFDETR.inference(compile_backend="inductor")` — opt-in Inductor backend for long-running inference at a fixed batch size and resolution, alongside the existing default TorchScript path. (#1425)
- `simplejpeg` JPEG decode (libjpeg-turbo straight into a NumPy buffer) for `CocoDetection` / `YoloDetection` / WebDataset readers when installed (now part of `[train]`) — same pixels as Pillow on tested wheels, though `simplejpeg` bundles its own libjpeg-turbo copy so small rounding differences are possible. Measured up to 1.8x faster on smooth content and 1.3x on detailed content at the decode stage for a reader that consumes the array directly (i7-8750H: 640x480 2.38ms to 1.30ms, 1920x1440 21.19ms to 11.78ms); consumers that wrap the array back into a PIL image see a smaller or hardware-dependent net gain. Falls back to Pillow for non-JPEG or rejected files, with the same decompression-bomb limit enforced. (#1473)
- CoreML Apple-Neural-Engine fallback boundary documented — how to choose `compute_units`, why fp32 never reaches the ANE, and the measured latency per compute unit. (#1495)
### ⚠️ Breaking Changes
- Export internals restructured into one `Exporter` class per format, each with its own config dataclass, reached through a registry mapping format name to module. `RFDETR.export()`'s own signature, accepted formats and return value are unchanged, but the modules behind it moved: `rfdetr.export.main` and `rfdetr.export.protocols.ExporterProtocol` are removed, the per-format `export_*` functions are replaced by `*Exporter` classes, and `TensorRTExporter`/`TRTInference` move under `rfdetr.export._tensorrt` — see the Migration guide below for the full symbol table. Format-independent graph prep now runs once in `rfdetr.export.prepare`, and requesting a capability a format lacks (e.g. `dynamic_batch` on CoreML/ExecuTorch/OpenVINO) is now refused from registry data before the optional dependency imports. (#1448)
- `format="tensorrt"` with `backbone_only=True` and `output_name` now writes `{output_name}-backbone.trt` instead of `{output_name}.trt`, matching every other format and what `export()`'s own documentation already described — previously a backbone engine silently overwrote a full-detector engine exported under the same name. Scripts that rebuild the engine path from `output_name` need the suffix added. (#1448)
- `TrainConfig.multi_scale` is now a `MultiScale` enum (`rfdetr.config.MultiScale`), absorbing the removed `do_random_resize_via_padding` flag: `"per-batch"` (new default) draws one random scale per batch, `"per-sample"` draws a scale per sample inside the dataset transforms and pads at collate, `"off"` trains at a fixed resolution. Booleans are still accepted as input (`True`→`"per-batch"`, matching the old default; `False`→`"off"`) but the stored value is always the enum member; the old `do_random_resize_via_padding=True` becomes `multi_scale="per-sample"`, and `do_random_resize_via_padding` itself is now rejected as an unknown field. (#1485)
### 🌱 Changed
- `training_config.json` is now written when training starts, not only when it finishes, so a run killed mid-training still leaves a configuration record — the reproducibility guarantee the file existed for. (#1496)
- `HungarianMatcher` builds its L1 box cost with a broadcast reduction over the four box coordinates instead of `torch.cdist(..., p=1)`, which cannot exploit a 4-wide feature dimension on CUDA and was the matcher's single most expensive operator (15.3% of device time in a profiler trace on RF-DETR Medium). Bit-identical results (`torch.equal` holds against `torch.cdist` on every tested shape); on CUDA, `compile=True` also compiles this cost with dynamic shapes independently of model CUDA graphs. Microbenchmarked 15-29x faster on an RTX 5060 Ti; end-to-end training throughput on a real 3,000-image subset rose +15.0% (math-SDPA) and +5.4% (fused-attention). (#1486, #1488)
- `group_detr` two-stage selection now batches each group's projection/ranking/top-k/box-MLP work — on an L4 (Nano, BF16, batch 4), median steady-state step time fell 15.76% (141.13ms to 119.09ms) — and reuses `enc_out_class_embed`'s already-computed class logits instead of the caller re-running that same linear layer a second time per group. A separate 3-seed COCO-subset screen showed mixed mAP50-95 deltas — no accuracy claim. (#1482, #1489)
- Two DDP training-step-time reductions, composing to a median -10.56% full step on 2x L4:
- `DDPStrategy` now also sets `static_graph=True` and `gradient_as_bucket_view=True`, skipping the per-iteration autograd-graph search `find_unused_parameters=True` otherwise repeats every step — safe because an empirical probe found only one constant unused parameter across real forward/backward runs (a DINOv2 mask token untouched in supervised training), not the data-dependent set the old flag comment assumed. Scope: automatic-optimization models with `grad_accum_steps<=1`. Alone: median -8.70% full step on 2x L4; gradients verified bit-identical under real NCCL. (#1489)
- The batched two-stage top-k pass reuses its already-computed classification logits instead of the caller re-running `enc_out_class_embed` a second time per group. Alone: median -2.75% full step on 2x L4 (below this project's 3% bar); composes with the DDP-flags change above to the reliable -10.56% combined median. (#1489)
- CUDA segmentation matching now takes a compact per-image route once it can avoid at least 728,000 cross-image entries, instead of the full-cartesian one (BCE+Dice mask cost keeps the full-batch calculation); an imbalanced batch dominated by one image's targets stays on the full-cartesian path. On an L4 (RFDETRSegNano, BF16), batch 8 improved 7.98% and batch 20 improved 12.28%; batch 4 was neutral, below the gate. Peak CUDA memory unchanged. (#1480)
- XLA val/test/train-metric callbacks now materialize each model forward once before COCO metric code reads individual tensors on the host, instead of recompiling overlapping lazy-graph fragments. On a Cloud TPU v5e-1 (RFDETRNano), median fit time fell 25.2% (310.2s to 232.0s) and validation time fell 73.4% (105.8s to 28.1s); metrics and checkpoint tensors unchanged. (#1467)
- Multi-GPU keypoint training with `grad_accum_steps > 1` now syncs gradients once per optimizer step, not once per microbatch. (#1443)
- The ONNX Runtime CPU inference session built by the ONNX export's inference helper no longer lets its intra-op thread pool busy-spin between calls, freeing CPU for other work sharing the process. (#1456)
- `predict()` now batches each image's per-tensor GPU→CPU transfers into one non-blocking sync per image instead of up to six sequential blocking round-trips. (#1420)
- Progress-bar `max_mem`/`free_mem` values switch from whole MB to GB with one decimal above 1000 MB. (#1470)
- On Python 3.14+, the four `*_jit` loss helpers are plain aliases of their eager versions, since TorchScript is unsupported there; `import rfdetr` no longer warns about it on that version. (#1406)
- The `[coreml]` extra no longer pins `torch<2.12`, so `pip install rfdetr[coreml]` resolves current torch — a two-minor jump (2.11 to 2.14). The pin was attributed to a coremltools MIL lowering regression; investigation found the real cause: untrained-weight encoder query scores sit ~1e-6 apart (within legitimate fp32 eager-vs-CoreML rounding drift), so a near-tied pair swaps rank and self-attention spreads that into every logit — a property of the ranking, not the conversion. `e2e_coreml` parity exports now assert a query-gap-vs-drift margin before comparing raw tensors, instead of trusting it blindly. (#1487)
- Detection and segmentation COCO evaluation now runs on [hotcoco](https://github.com/derekallman/hotcoco) by default — a Rust evaluator under MIT, added to `[train]`. Reported metrics unchanged: parity tests require exact equality against the previous backend for box-only and box-plus-mask evaluation. `TrainConfig.eval_backend="faster_coco_eval"` restores the previous evaluator (still installed/required). `compute()` itself measured 6.2s to 1.1s on synthetic COCO-val-shaped state (macOS CPU), mostly because predictions are handed over as one detection array instead of per-annotation dictionaries. One hotcoco quirk is handled defensively: its `dataset` getter returns a copy, so in-place mutation is silently discarded — which would otherwise leak one IoU type's annotation areas into the other's size buckets. (#1402)
- Fixed-resolution (`multi_scale=False`) `compile=True` detection training now statically specialises the model and batches the default IA-BCE classification, box, GIoU and cardinality losses across final/auxiliary/encoder outputs into one lazily compiled tensor function; multi-scale, aspect-ratio resize, CUDA graphs, segmentation/keypoint and custom criteria keep their established paths. On an L4 (Nano, BF16, batch 4, 384px), median settled epoch fell 14.43% (3.225s to 2.759s); multi-GPU, EMA-on training, convergence and mAP not measured. (#1522)
### 🗑️ Deprecated
- `ModelConfig.amp` deprecated, removal in v1.14, superseded by `TrainConfig.amp_dtype`. (#1471)
- `TrainConfig.fp16_eval` deprecated, removal in v1.14, no runtime consumer. (#1471)
### ❌ Removed
- `RFDETR.optimize_for_inference()` (deprecated since 1.9.0) — use `RFDETR.inference()`. (#1500)
- `TrainConfig.lr_drop` and `lr_min_factor` (deprecated since 1.9.0) — use `lr_scheduler_kwargs`. (#1500)
- `rfdetr.datasets.synthetic` (whole module) — use [`fuse-augmentations`](https://github.com/Borda/fuse-augmentations). (#1435)
### 🔧 Fixed
- CoreML exports of keypoint models no longer invert the keypoint self-attention mask, which caused garbled or missing keypoint detections. `nn.MultiheadAttention` turns a boolean `attn_mask` into a float one via `zeros_like(mask, dtype=...)`, and coremltools (9.0, 9.1) drops that `dtype` keyword during `torch.export` conversion, so the mask applied with the opposite meaning — with multiple keypoint classes, cross-class logits diverged from eager by up to 1.19; with one class every logit got a -3e4 bias, and float16 found 0/11 detections on `CPU_ONLY` (now finds all 11). The decoder now passes the additive float mask directly; eager outputs unchanged. Fixed upstream in apple/coremltools#2865. (#1520)
- `format="tflite"` now converts keypoint models instead of failing with `onnx2tf`'s `Dimensions must be equal, but are 17 and 100` on the keypoint decode (a rank-4 tensor broadcast over the keypoint axis). The decode now runs on a flattened `(..., K * 2)` layout with the same result; FP32 `.tflite` matches ONNX within `3.2e-3` on every output over all 100 queries. (#1518)
- `TrainConfig.eval_backend="vernier"` no longer disagrees with the other backends on `map_small`/`map_medium`/`map_large` bucketing for a combined `bbox`+`segm` run — the old code bucketed box AP by box area while `hotcoco`/`faster_coco_eval` bucket by mask area, so results disagreed for objects straddling the `32²`/`96²` size thresholds. Ground-truth `area` now always derives from the mask when `segm` is requested. Now converts through vernier's own columnar `coco_inputs_from_columns` (vernier 0.5.2+) instead of ~210 lines of hand-rolled conversion, ~2-10% faster `compute()`. (#1512)
- Albumentations `OneOf`/`Sequential` transform containers now respect an explicit container-level `p` instead of always forcing `p=1.0` regardless of what was configured. (#1515)
- `last.ckpt` and `checkpoint_<epoch>.ckpt` are now written every due epoch regardless of `eval_interval` — both `ModelCheckpoint` callbacks previously saved only from `on_validation_end`, so with `eval_interval=2` the resume checkpoint refreshed only every other epoch and a crash between validations lost up to `eval_interval - 1` epochs of progress (e.g. `checkpoint_interval=3` with `eval_interval=2` never archived epoch 2 or 8). Both callbacks now save at train epoch end, still carrying that epoch's validation results. (#1510)
- Multi-GPU validation/test metrics no longer double-count the images Lightning's `DistributedSampler` repeats to pad a split to a multiple of `world_size` — up to `world_size - 1` images were scored twice (a 21-image split over 2 GPUs held 22 in the merged mAP state). `COCOEvalCallback` now reads the sampler's own rank/replica/total-size info and skips accumulation past the dataset length. Measured: 2-GPU `val/mAR`/`val/mAP_50_95` now match the 1-GPU values exactly (were 0.1286/0.0286 vs 0.1299/0.0287). (#1508)
- `run_test=True` no longer hangs multi-GPU training at the end of `fit` — `trainer.test()` was called from the main process alone under `strategy="ddp"`'s collective test loop while other ranks had already left `fit`, so the run never finished (reproduced: rank 0 still waiting at 600s). Every rank now enters `trainer.test()`, with file work and the test-or-not decision still made and broadcast from the main process. Spawn-based launchers (`ddp_spawn`/`ddp_notebook`), which can't host a nested `trainer.test()`, now warn and skip the fit-end test instead of crashing with `DistNetworkError: EADDRINUSE`. (#1506)
- Detection/segmentation training with `grad_accum_steps > 1` no longer divides the loss by the accumulation count twice — `training_step` divided by `accumulate_grad_batches` on top of Lightning's own division, so the optimizer saw `1/N` of the mean gradient instead of the mean (verified: accumulated gradient was 0.5 at N=2, 0.25 at N=4, where it should be 1.0). AdamW is largely scale-invariant, but `clip_max_norm` engaged N times less often and SGD-family optimizers trained at an effective `lr / N`; `batch_size="auto"` selects `grad_accum_steps > 1` on its own, so unrequested runs were affected too. Introduced in 1.8.0. (#1504)
- `python -m rfdetr.export.benchmark` no longer crashes with `AttributeError: 'str' object has no attribute 'dataset'` when COCO evaluation is enabled — `main()` passed the raw annotation path to `CocoEvaluator` instead of a loaded `COCO` object; `--disable_eval` was unaffected throughout. (#1502)
- `MultiScaleProjector` no longer crashes with `IndexError` on `feat_fuse_list[0]` for `scale_factors=0.25` — an `elif continue` only broke the inner per-channel loop, leaving an empty pyramid stage built on top of nothing. The `scale==0.25` check now skips the whole scale up front, matching its intent as an extra max-pool marker. (#1498)
- Empty `gt_boxes`/`pred_boxes` lists no longer crash the GT/pred visualization saver with `IndexError` — an empty list produced a 1-D array that `xywh_to_xyxy` indexed as 2-D; `reshape(-1, 4)` now keeps it a well-formed `(0, 4)` array so the existing no-op guards actually run. (#1497)
- fp16 native CoreML exports of an untrained model now compile for the Apple Neural Engine instead of silently falling back to CPU-only — the ANE compiler rejected the whole program when the pre-`topk` norm still carried its identity affine (untrained weights) and the encoder token count was a multiple of 32, so the model ran CPU-only under `CPU_AND_NE` and failed to load under the default `ALL`. Trained checkpoints were never affected. The selection now gathers pre-norm rows and normalizes only the selected tokens (same computation, LayerNorm acts per token); eager outputs unchanged, mAP unchanged within noise. (#1495)
- Multi-device TPU/XLA training now disables EMA with a runtime warning, since per-step EMA weight reads can corrupt subsequent optimizer updates on multiple TPU cores under BF16 — single-device XLA keeps EMA (verified over 100-350 updates on a Cloud TPU v6e-1 with finite tensors). CPU and CUDA EMA unaffected. (#1465, #1476, #1494)
- Explicit and `auto` `amp_dtype="bf16"` now select XLA's `bf16-true` precision instead of silently falling back to FP32 on TPU. (#1469)
- Segmentation `_compute_mask_iou` now converts its bool→float32 masks 32 rows at a time instead of materializing every matched prediction and ground truth of a class at once (`N x H x W` / `M x H x W` at full resolution) — a densely annotated class could make both large enough to OOM. Output is bit-identical for nonzero counts, and a zero count on either side now returns an empty result instead of raising `RuntimeError`. (#1463)
- `RFDETR.export(format="tensorrt", fp16=True)` now actually builds an FP16 engine on strongly-typed TensorRT 11+ by casting the ONNX graph to FP16 first (`[tensorrt]` now also pulls `onnx` and `onnxconverter-common`, and raises `ImportError` if missing rather than silently building FP32) — previously it silently fell back to an FP32 engine reported as FP16. (#1454)
- Compiled training with `torch.compile(..., dynamic=True)` no longer silently falls back whole forward frames to eager — `triton.coalesce_tiling_analysis` is unsupported on the dynamic-shape path but still ran, hitting a size-hint assert with no symbolic-shape escape. Now disabled before the compile call. (#1455)
- The export-mode switch is now idempotent — calling it a second time in the same state no longer raises. (#1445)
- Distributed (DDP) training now preserves the minimum five optimizer steps per epoch for short datasets instead of losing the replacement sample count when Lightning injects its distributed sampler. (#1447)
- Kornia `Affine` now applies a scalar `translate_percent` to both axes and reads a scalar `scale` as a fixed range, avoiding silent horizontal-translation loss and construction failures. Scalar translation now emits a warning, since Kornia samples signed offsets where Albumentations applies a fixed positive offset. (#1444)
- TFLite INT8 documentation and warnings now reflect that dynamic-range quantization needs no calibration data. (#1364)
## 🏆 Contributors
- **Jesús Royeth** (@JESUSROYETH) — opt-in CUDA-graph training replay, combinable with `compile=True` and paired with a Transformer Engine FP8 capture route for single-GPU detection training; streaming training input directly from WebDataset tar shards; fixed EMA under XLA so multi-device XLA disables it with a warning while single-device XLA keeps it; batched fixed-resolution `compile=True` training losses into one compiled function.
- **Jirka Borovec** (@Borda, [LinkedIn](https://linkedin.com/in/jirka-borovec)) — removed the deprecated `optimize_for_inference()` method, the deprecated `lr_drop`/`lr_min_factor` training fields, and the standalone synthetic-dataset generator module; restructured export internals into one `Exporter` class plus a frozen config dataclass per format behind a registry; added FP8 training precision; made `hotcoco` the default COCO evaluation backend; skipped flaky spawn-based DDP tests on macOS CI.
- **Raghav Rathi** (@raghav-rathi) — fixed checkpoint-writing cadence so `last.ckpt` and interval checkpoints are written every due epoch regardless of `eval_interval`; fixed multi-GPU validation/test metrics to skip `DistributedSampler` padding duplicates; fixed `run_test=True` hanging at the end of `fit` under DDP; fixed loss being divided by `accumulate_grad_batches` twice.
- **Lương Thanh Hoàng Phú** (@lthphuw, [LinkedIn](https://linkedin.com/in/phu-lth)) — fixed fp16 native CoreML exports to compile onto the Apple Neural Engine and documented the ANE fallback boundary; `training_config.json` now written when training starts and rewritten at finish; dropped the `torch<2.12` pin on the `[coreml]` extra and made end-to-end parity checks tie-robust; fixed `format="tflite"` to convert keypoint models.
- **Anna Gubenkova** (@avbelova) — added OpenVINO IR as a new export format, including a configurable export precision.
- **Daisuke Majima (MLBoy)** (@john-rocky, [LinkedIn](https://www.linkedin.com/in/daisuke-majima-43a637154/)) — added LiteRT export via litert-torch as a new export format.
- **Yonghye Kwon** (@developer0hye, [LinkedIn](https://www.linkedin.com/in/yonghye-kwon-91641a174/)) — added `ufcoco` (ultrafast-pycocotools) as a selectable COCO evaluation backend.
- **Noé Fontana** (@NoeFontana, [LinkedIn](https://www.linkedin.com/in/noe-fontana/)) — added `vernier` as a selectable COCO evaluation backend; refactored it onto vernier's own columnar state adapter, fixing a bbox/segm area-bucketing mismatch and speeding up `compute()`.
- **Tuan Nguyen** (@tuan3w, [LinkedIn](https://www.linkedin.com/in/tuan3w/)) — rebuilt the matcher's L1 box cost as a broadcast reduction instead of `torch.cdist`, later compiled together with the model on CUDA.
- **Tuncay BAHADIR** (@tuncaybahadir, [LinkedIn](https://www.linkedin.com/in/tuncaybahadir)) — batched `predict()`'s per-image GPU-to-CPU tensor transfers into a single sync.
- **Vitor Toledo** (@ToledoVitor, [LinkedIn](https://www.linkedin.com/in/vitorstoledo/)) — added weekly-metrics SVG automation for the project's CI/docs.
- **Christoph Deil** (@cdeil, [LinkedIn](https://www.linkedin.com/in/christophdeil)) — made the eager loss helpers the sole implementation on Python 3.14+, where TorchScript JIT is unsupported; added Apple Core AI export as a new format; fixed CoreML keypoint exports to stop inverting the keypoint self-attention mask.
- **David Igech** (@davidiglesias) — added `simplejpeg`-based JPEG decoding for the CocoDetection, YoloDetection, and WebDataset readers.
- **Aman Harsh** (@amanharshx) — corrected the TFLite INT8 documentation and warnings to state that dynamic-range quantization needs no calibration data.
*This release also includes automated maintenance from `Copilot` (bot-authored PRs) and `pre-commit-ci[bot]` (dependency autoupdate), plus AI pair-programming co-authorship on several human-authored commits (Claude, Codex).*
---
**Full changelog**: https://github.com/roboflow/rf-detr/compare/1.10.1...v1.11.0
Related Repositories
public-apis/public-apis★ 491,126
A collective list of free APIs
NousResearch/hermes-agent★ 255,899
The agent that grows with you
yt-dlp/yt-dlp★ 199,996
A feature-rich command-line audio/video downloader
microsoft/markitdown★ 191,480
Python tool for converting files and office documents to Markdown.
Significant-Gravitas/AutoGPT★ 187,700
AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.