Repository navigation
1.11.2 hardens threaded inference, turns a silent class-name shift into an error, and fixes predict() on flipped frames.
- Threads sharing one model no longer corrupt its weights or switch off cuDNN for the whole process.
- A
class_nameslist that includes the Roboflow root category now errors instead of shifting every label. predict()takesframe[:, :, ::-1], so the docs' video, webcam and RTSP examples run as written.- YOLO
data.yamlfiles load consistently: class IDs sort numerically andimages/trainlayouts work. - ONNX exports run faster through Apple's CoreML: Nano drops from 9.3 to 8.3 ms on an M4 Max with ONNX Runtime 1.30.
Upgrading from 1.11.1 means one removed constant: inline ["images", "labels"] if you imported REQUIRED_DATA_SUBDIRS. Four behaviour changes are worth a skim in the Migration guide.
✨ Spotlights
Threads sharing a model could corrupt its weights
A cold model's weights move from CPU to the accelerator on first use (predict(), inference(), and export()'s restore step), and several threads could run that move at once. On a cold RF-DETR segmentation model, four threads calling predict() together left 13 of 573 state_dict tensors corrupted. Nothing raised, and every frame came back with no detections. The check and the move now happen under a per-model lock, so a thread that arrives mid-move waits for it. (#1564)
from concurrent.futures import ThreadPoolExecutor
model = RFDETRSegNano() # cold: weights still on CPU
with ThreadPoolExecutor(max_workers=4) as pool:
detections = list(pool.map(model.predict, frames))Segmentation threads no longer disable cuDNN for everyone
The segmentation head wraps its depthwise convolution in torch.backends.cudnn.flags(enabled=False). That flag is process-global and not thread-safe: a second thread entering mid-scope saved False and restored it last, so every later convolution in the process quietly skipped cuDNN. Detections near the score threshold changed (25 instead of 24 on one real frame). Forward and backward scopes now share a lock, and a CPU input skips them entirely. No code change on your side. (#1565)
A root category in class_names is now an error
Roboflow COCO exports list a top-level category first, such as animals above cat, dog and horse. Since 1.9.2 it isn't one of the model's classes, so a list built from every category gave each class its neighbour's name, in checkpoints and in predict() output, with no warning. RFDETR.train() now compares class_names with the dataset before it writes training_config.json or starts a wandb/MLflow run. (#1572)
# 1.11.1 trained, with every name shifted by one
model.train(dataset_dir=ds, class_names=["animals", "cat", "dog", "horse"])
# 1.11.2 raises ValueError naming the extra entry; pass the real classes
model.train(dataset_dir=ds, class_names=["cat", "dog", "horse"])predict() accepts flipped frames
frame[:, :, ::-1], the BGR-to-RGB flip in the video file, webcam and RTSP examples of docs/learn/pretrained.md, is a negative-stride NumPy view, and torch.from_numpy rejects those. The examples raised ValueError: At least one stride in the given numpy array is negative on the first frame. Only arrays with a negative stride get copied now. (#1573)
detections = model.predict(frame[:, :, ::-1])REQUIRED_DATA_SUBDIRS is gone
is_valid_yolo_dataset finds split directories through the YAML-aware resolver, and that resolver derives the labels directory from the resolved images path. Its only reader was is_valid_yolo_dataset, rewritten in this release, so nothing reads the ["images", "labels"] constant any more. A split directory that resolves outside the dataset root is refused too, even through a conventional train/images symlink. (#1570)
🔄 Migration guide
One removal, in rfdetr.datasets.yolo:
# 1.11.1
from rfdetr.datasets.yolo import REQUIRED_DATA_SUBDIRS
# 1.11.2: ImportError above; inline the value
REQUIRED_DATA_SUBDIRS = ["images", "labels"]Four behaviour changes could also affect existing workflows: a YOLO split directory resolving outside the dataset root is refused, class_names with a root category raises, export(format="tflite", dynamic_batch=True) raises NotImplementedError, and the [executorch] extra now resolves to 1.3.x. Before/after code for each is in MIGRATION.md. docs/getting-started/migration.md doesn't cover 1.11.2 yet.
📝 Notable changes
🚀 Added
find_yolo_data_fileandfind_yolo_train_imagesinrfdetr.datasets.yolo, re-exported fromrfdetr.datasets: locate a YOLO dataset'sdata.yamland resolve its training images from the YAML split paths. (#1570)
⚠️ Breaking Changes
rfdetr.datasets.yolo.REQUIRED_DATA_SUBDIRSremoved; inline["images", "labels"]if you imported it. (#1570)
🌱 Changed
- A YOLO split directory that resolves outside the dataset root is refused however it's reached. Format detection and
is_valid_yolo_datasetreject it, andbuild_roboflow_from_yoloraisesValueErrornaming it. A dataset whose whole root is a symlink is unaffected. (#1570) - ONNX exports drop the single-input
Concat, theConcatof an empty tensor and the segmentation head'sEinsum, the three patterns ONNX Runtime's CoreML provider rejects. On an Apple M4 Max with a static batch, Nano runs in 8.3 ms instead of 9.3, Large in 19.4 instead of 19.9, and Seg Nano in 16.5 instead of 18.9. Outputs are bit-identical on the ONNX Runtime CPU provider. (#1566) export(format="tflite", dynamic_batch=True)raisesNotImplementedErrorbefore conversion starts instead of failing insideonnx2tf. TFLite is now a fixed-batch format, like ExecuTorch, CoreML, Core AI, OpenVINO and LiteRT. (#1591)- The
[executorch]extra is capped atexecutorch>=1.3,<1.4, matching the version CI installs. (#1591) - Export docs split into Basics and Advanced pages, with the overview's L4 TensorRT row separated into the
inference-modelsmanaged engine and the raw.trtrun byTRTInference. The export cookbooks share private benchmark helpers and report batch sizes 1, 4 and 8 (CPU and Mobile: 1 and 4). (#1562, #1567, #1591, #1594)
🔧 Fixed
- Threads calling
predict(),inference()orexport()on one cold model no longer corrupt its weights. (#1564) - Concurrent segmentation inference no longer leaves cuDNN disabled for the process. (#1565)
predict()accepts NumPy views with negative strides. (#1573)- A
class_nameslist containing a Roboflow root category raisesValueErrorbefore training starts, instead of shifting every name by one. (#1572) - YOLO datasets whose
data.yamldeclares split paths such astrain: images/train, with an optionalpathbase, are detected and loaded. (#1570) - YOLO
data.yamlclass IDs resolve the same way in every entry point; keys sort numerically (not lexicographically) and non-ASCII digits are rejected, and a YOLO pose file that fails validation names the real cause. (#1571) - Downloaded weights,
checkpoint_best_total.pthandtraining_config.jsonfollow the umask again instead of being owner-only. (#1582) - Keypoint augmentation swaps paired joint identities only after an odd number of horizontal flips, including repeated draws from
SomeOf(replace=True). (#1569)
🏆 Contributors
Welcome to Marvel Harisson and Brad Dwyer, whose first merged changes to RF-DETR land in this release.
- Marvel Harisson (@INo-xious, LinkedIn) — made YOLO layout detection follow the
data.yamlsplit paths; resolved YOLO class IDs consistently across entry points; fixed keypoint joint swaps under repeated flips. - Brad Dwyer (@yeldarby) — kept the CoreML-rejected
ConcatandEinsumpatterns out of ONNX exports. - Tuncay BAHADIR (@tuncaybahadir, LinkedIn) — serialised the deferred weight move across threads; kept cuDNN enabled after concurrent segmentation inference.
- Raghav Rathi (@raghav-rathi, LinkedIn) — made
class_nameswith a Roboflow root category fail loudly; madepredict()accept negative-stride NumPy views. - Mohammad Hijjawi (@MohammadHijjawi97, LinkedIn) — made downloaded weights and checkpoints follow the umask.
- Jirka Borovec (@Borda, LinkedIn) — reorganised the export docs and cookbook benchmarks; prepared this release.
Automated contributions: claude[bot], pre-commit-ci[bot], Copilot Autofix powered by AI, OpenAI Codex, Claude Sonnet 5, Claude Sonnet 5.5, Claude Opus 5.5.
Full changelog: 1.11.1...1.11.2