1.11.2

0xNyk/council-of-high-intelligence1.11.2Oct 4, 2026by Borda

Upgrading?

AI Summary

Hardens threaded inference to prevent weight corruption, fixes `predict()` to handle flipped frames, and improves ONNX export performance on Apple hardware.

Key Highlights

  • Threads sharing a model no longer corrupt its weights
  • A root category in `class_names` is now an error
  • `predict()` accepts flipped frames
  • ONNX exports run faster through Apple's CoreML
  • `REQUIRED_DATA_SUBDIRS` is removed

Breaking Changes

  • `rfdetr.datasets.yolo.REQUIRED_DATA_SUBDIRS` removed; inline `['images', 'labels']` if imported
  • YOLO split directory resolving outside dataset root is refused
  • `export(format='tflite', dynamic_batch=True)` raises `NotImplementedError`
  • The `[executorch]` extra is capped at `1.3.x`

New Features

  • Added `find_yolo_data_file` and `find_yolo_train_images` in `rfdetr.datasets.yolo`

Full Release Notes

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_names`** list that includes the Roboflow root category now errors instead of shifting every label.
- `predict()` takes `frame[:, :, ::-1]`, so the docs' video, webcam and RTSP examples run as written.
- YOLO `data.yaml` files load consistently: class IDs sort numerically and `images/train` layouts 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)

```python
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)

```python
# 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)

```python
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`:

```python
# 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](./MIGRATION.md). `docs/getting-started/migration.md` doesn't cover 1.11.2 yet.

## 📝 Notable changes

### 🚀 Added

- `find_yolo_data_file` and `find_yolo_train_images` in `rfdetr.datasets.yolo`, re-exported from `rfdetr.datasets`: locate a YOLO dataset's `data.yaml` and resolve its training images from the YAML split paths. (#1570)

### ⚠️ Breaking Changes

- `rfdetr.datasets.yolo.REQUIRED_DATA_SUBDIRS` removed; 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_dataset` reject it, and `build_roboflow_from_yolo` raises `ValueError` naming it. A dataset whose whole root is a symlink is unaffected. (#1570)
- ONNX exports drop the single-input `Concat`, the `Concat` of an empty tensor and the segmentation head's `Einsum`, 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)` raises `NotImplementedError` before conversion starts instead of failing inside `onnx2tf`. TFLite is now a fixed-batch format, like ExecuTorch, CoreML, Core AI, OpenVINO and LiteRT. (#1591)
- The `[executorch]` extra is capped at `executorch>=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-models` managed engine and the raw `.trt` run by `TRTInference`. 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()` or `export()` 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_names` list containing a Roboflow root category raises `ValueError` before training starts, instead of shifting every name by one. (#1572)
- YOLO datasets whose `data.yaml` declares split paths such as `train: images/train`, with an optional `path` base, are detected and loaded. (#1570)
- YOLO `data.yaml` class 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.pth` and `training_config.json` follow 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](https://www.linkedin.com/in/marvel-harisson-4102b7345/)) — made YOLO layout detection follow the `data.yaml` split paths; resolved YOLO class IDs consistently across entry points; fixed keypoint joint swaps under repeated flips.
- **Brad Dwyer** (@yeldarby) — kept the CoreML-rejected `Concat` and `Einsum` patterns out of ONNX exports.
- **Tuncay BAHADIR** (@tuncaybahadir, [LinkedIn](https://www.linkedin.com/in/tuncaybahadir)) — serialised the deferred weight move across threads; kept cuDNN enabled after concurrent segmentation inference.
- **Raghav Rathi** (@raghav-rathi, [LinkedIn](https://www.linkedin.com/in/raghavrathi00/)) — made `class_names` with a Roboflow root category fail loudly; made `predict()` accept negative-stride NumPy views.
- **Mohammad Hijjawi** (@MohammadHijjawi97, [LinkedIn](https://www.linkedin.com/in/mohammadhijjawi)) — made downloaded weights and checkpoints follow the umask.
- **Jirka Borovec** (@Borda, [LinkedIn](https://linkedin.com/in/jirka-borovec)) — 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**: https://github.com/roboflow/rf-detr/compare/1.11.1...1.11.2

Related Repositories