Upgrading to 1.7.0

Version 1.7.0 keeps older YOLO9 checkpoints loading with the same float boxes. It changes YOLO9-family training, the names code sees inside the network, and a few validation and predict contracts.

1.6.0 to 1.7.0

  • YOLO9, YOLO9-E2E and YOLO9-P2 checkpoints saved by 1.7.0 use new tensor names and do not load in 1.6.x: 1.6.x loads YOLO9 and YOLO9-P2 ones with a missing-keys warning and predicts no boxes, and raises a model_family mismatch error for YOLO9-E2E ones. Upgrade every environment that reads them. Float, quantized and training checkpoints written by earlier releases load in 1.7.0, and ONNX exports are unaffected.

  • Code that reaches into model.model of a YOLO9-family model sees the MultimediaTechLab/YOLO module names, with no aliases. DDetect is YOLO9Head (anchor_convs / class_convs), RepConvN is RepConv, RepNBottleneck is Bottleneck, DFL is Anchor2Vec (anchor2vec), nc is num_classes, and block sublayers are conv1/conv2/bottleneck. In YOLO9-E2E, YOLO9E2EDetect is YOLO9E2EHead and one2one_cv2 / one2one_cv3 are one_to_one_anchor_convs / one_to_one_class_convs. LibreYOLO9Model.fuse(), RepConvN.fuse_convs(), Conv.forward_without_bn(), Concat and yolo9.utils.decode_boxes are removed. The changelog lists every rename.

  • ONNX node names follow the new module names in the head, backbone and neck, for example /head/anchor_convs.0/.... Update tools keyed on node names, such as Hailo end-node configs. Model inputs, outputs and output names are unchanged.

  • A YOLO9 fine-tune from weights without PGI tensors, which includes the published LibreYOLO9{t,s,m,c}.pt, trains the main head only, as in 1.5.0; 1.6.0 attached a randomly initialized PGI branch at aux_weight=0.25. Weights that carry PGI tensors, and runs from scratch, still train with PGI. Pass aux_weight=0.25 to attach a new branch anyway, or aux_weight=0 to force it off.

  • When PGI is attached, YOLO9-m and YOLO9-c build the MultimediaTechLab auxiliary branch: 12.8M (m) and 25.7M (c) training-only parameters at 80 classes, up from 7.5M and 10.2M, so PGI training of these sizes needs more memory and time per step. A 1.6.0 m/c training checkpoint resumes and fine-tunes with the branch it was trained with. Inference and YOLO9-t/s are unchanged.

  • YOLO9, YOLO9-E2E and YOLO9-P2 clip the gradient L2 norm at 10, and their training assigner scores anchors in fp32, so positives chosen under fp16 AMP can differ. Pass clip_max_norm=0 for the 1.6.0 clipping; a run started in 1.6.0 and resumed in 1.7.0 also clips unless you pass it. The fp32 assignment has no switch.

  • A non-zero perspective warps by corner displacement in every family that uses the shared affine warp (YOLO9 and its E2E and P2 variants, YOLOX, YOLOv7, YOLO-NAS, PP-YOLOE, RTMDet, PicoDet, RT-DETR and RT-DETRv2), including runs resumed from a 1.6.0 checkpoint that set it. Values of 0.002 and above give the same maximum warp, and negative values raise ValueError. No setting reproduces the 1.6.0 warp; the default perspective=0 is unchanged.

  • The YOLO9 mosaic pipeline draws the mixup ratio from Beta(1, 1) instead of Beta(32, 32), with no setting for the old value. Default YOLOv7 training changes, because YOLOv7 enables mixup by default. YOLO9, YOLO9-E2E, YOLO9-P2, RT-DETR and RT-DETRv2 change only when mixup is enabled.

  • libreyolo train without name= writes EC, D-FINE, SegFormer and LingBot-Vision runs to runs/train/ec_exp, dfine_exp, segformer_exp and lingbotvision_exp instead of runs/train/exp, V-JEPA 2 probes to runs/train/train, and YOLO9-P2 runs to runs/train/yolo9_exp. Pass name=exp for the old path, or name=yolo9_p2_exp for YOLO9-P2.

  • libreyolo train starts D-FINE, SegFormer and LingBot-Vision from their own recipes. For D-FINE that is 132 epochs, lr0=2e-4, weight_decay=1e-4, 2 warmup epochs and no mosaic, mixup, HSV or affine augmentation. To send the values 1.6.0 sent, pass epochs=300 optimizer=sgd lr0=0.01 weight_decay=5e-4 warmup_epochs=5 mosaic=1.0 mixup=1.0 hsv_prob=1.0 degrees=10 translate=0.1 shear=2.0.

  • libreyolo.validation.ConfusionMatrix indexes matrix[predicted, true] (was [actual, predicted]), counts detections at conf_thres=0.25 (was 0.15), takes names= and task= as keyword arguments only, and its matrix can no longer be reassigned. Transpose code that indexed the old matrix, and pass conf_thres=0.15 for the old counts. plots=True writes counts to confusion_matrix.png and the normalized plot to confusion_matrix_normalized.png.

  • predict(agnostic_nms=True) suppresses overlapping boxes across classes instead of warning. Tasks without boxes raise ValueError, LibreEnsemble and HRNet raise NotImplementedError, and the SAM tier rejects the keyword with TypeError; drop the argument from those calls.

  • An unknown classifier interpolation raises ValueError, including the torchvision modes LANCZOS, BOX, HAMMING and NEAREST_EXACT. Use bilinear, bicubic, nearest or bilinear_no_antialias.

  • Re-run validation before comparing with 1.6.0 numbers for val(single_cls=True) on a model trained on several classes, segmentation datasets that mix box-only and polygon rows, and RTMDet-Ins, which keeps up to max_det=300 masks per image by default; pass max_det=100 for MMDetection's setting.

  • Without libreyolo[rfdetr], CLI calls with an RF-DETR or DINOv2 model name exit with the JSON error code model_load_failed instead of model_not_found, and the message names pip install libreyolo[rfdetr].

See the changelog for the full release.

1.5.0 to 1.6.0

  • Upgrade environments pinned below ONNX Runtime 1.18 to onnxruntime>=1.18.0 before installing the ONNX extra.

  • SAM 3D Body accepts the reviewed snapshot asset set. Install libreyolo[hf], obtain gated access and use automatic acquisition, or supply the unchanged snapshot directory and pinned MHR asset on a trusted local filesystem.

  • Re-run validation after RF-DETR non-pose resize and classifier-normalization corrections. Recheck deployed confidence thresholds before comparing with 1.5.0 results; there is no legacy-preprocessing flag.

  • D-FINE, DEIM, RT-DETRv4 and YOLO-NAS detection enable FP16 AMP by default. Pass amp=False to retain FP32.

  • For former YOLO9 fine-tune choices, set aux_weight=0, max_labels=100 and warmup_momentum=0.937. Set letterbox_pad="topleft" when a new center-stamped conversion must reproduce the old geometry. Old single-head checkpoints resume with their original graph. New YOLO9 runs use more GPU memory, so lower batch if a 1.5.0 batch size runs out of memory.

  • RF-DETR and DINOv2 create incremented family-named run directories. Update artifact-path consumers, or set output_dir="runs/train", exist_ok=True to retain the old location and reuse behavior.

  • Keep classification mixup + cutmix <= 1; invalid combinations now raise during setup.

  • QAT disables EMA, SyncBatchNorm and checkpoint averaging. Use QAT best/last checkpoints without relying on those states.

  • Custom loaders with dataset-mutation hooks must use persistent_workers=False or rebuild workers after mutation. Incompatible persistent multi-worker copies now raise.

  • NumPy image arrays are read as BGR, the OpenCV order. Pass color_format="rgb" where you pass an RGB array such as np.asarray(pil_image); cv2.imread() output and video frames need no change.

  • A 4D NumPy array or tensor is a batch: predict() returns a list with one Results per image instead of using only the first image.

  • train(resume=True) and train(resume="path/to/last.pt") restore the run's saved training arguments and keep writing into its run directory; arguments you pass explicitly win. Resuming released weights, or a run that already reached its epochs, raises ValueError.

  • With validation on, the final epoch always validates, so runs shorter than eval_interval now report metrics and write best.pt. val=False turns validation off, final epoch included. Validation during training writes into <run>/val instead of runs/val/.

  • CLI train failures report their error type, so configuration errors exit with code 2 instead of 1 with io_error.

See the changelog for the full release and importing weights for checkpoint conversion.

1.4.0 to 1.5.0

This page is about upgrading LibreYOLO itself. If you are looking for how to load a checkpoint from an upstream project, that is import existing weights, a different subject.

The full entry for the release is the changelog. What follows is only the part that asks something of you.

Code changes you must make

allow_experimental=True no longer exists

The acknowledgement gate is gone, along with the ddp_aware(experimental_key=...) mechanism behind it. EC, RTMDet, PicoDet and FOMO training and export previously required the argument, so any script that trains one of those families is affected.

python
# 1.4.0model.train(data="data.yaml", epochs=100, allow_experimental=True) # 1.5.0: delete the argumentmodel.train(data="data.yaml", epochs=100)

There is no deprecation shim. A call that still passes it gets an Unknown training config keys (ignored) warning, and the argument has no effect. BaseModel.EXPERIMENTAL_WEIGHT_FILENAMES was removed with it. The get_download_notice() hook survives, and is still overridden by MiDaS, SegFormer and YOLO9-P2.

Support levels are still published, they are just no longer an argument: see stability tiers.

The export tier "experimental" no longer exists

python
from libreyolo.export.support import Tier # 1.4.0: Literal["validated", "experimental", "blocked"]# 1.5.0: Literal["validated", "available", "blocked"]

Code branching on the tier string should read "available" where it read "experimental". BaseExporter no longer emits a RuntimeWarning for those formats. The per-format state is listed in the export matrix.

pretrained=False with resume is now rejected

The combination previously proceeded incoherently. It now raises:

ValueError: pretrained=False cannot be combined with resume.

Pick one. pretrained=False starts from a fresh seeded initialization, which in 1.5.0 works for every trainable family rather than three of them, and resume continues an interrupted run from its checkpoint. Both are documented under training.

CLI --imgsz is a string, not an int

Narrower than it sounds. Both of these are unaffected:

bash
libreyolo predict --model yolo9-t --source img.jpg --imgsz 640   # still fine
python
model.predict("img.jpg", imgsz=640)   # still fine

Only code that calls the CLI command functions directly from Python needs to change, because predict, train and val widened --imgsz from int to str so it can accept rectangular sizes:

python
from libreyolo.cli.commands.predict import predict_cmd predict_cmd(..., imgsz=640)      # 1.4.0predict_cmd(..., imgsz="640")    # 1.5.0, and "480x640" now works too

train's default is now the string "640". export --imgsz was already a string, and profile is unchanged.

Numbers that change

Three changes move metrics at default settings. If you track results across versions, read these before you compare a 1.5.0 run against a 1.4.0 one.

faster-coco-eval is the default COCO metrics backend

val() and per-epoch training validation now compute COCO metrics with the faster-coco-eval C++ backend instead of pycocotools.

The switch was decided on measured parity across all 100 RF100-VL test splits: 1381 of 1400 metric values bit-identical, maximum deviation 2.22e-16, headline deltas exactly 0, at 15.6x faster overall and 56x on detection-dense datasets. Your numbers should not move. They are produced by a different implementation all the same, which is the reason this is on the list.

pycocotools stays the automatic fallback when faster-coco-eval is not installed. To force it:

bash
libreyolo val --model yolo9-t --data coco.yaml --no-faster-coco-eval
python
model.val(data="coco.yaml", faster_coco_eval=False)

LIBREYOLO_FASTER_COCO_EVAL=0 does the same thing globally. The backend actually used is logged at INFO, exposed as model.last_eval_backend after val(), and included as eval_backend in the CLI JSON payload. Install the fast path with pip install libreyolo[fast-eval].

YOLOX checkpoints trained before 1.5.0 need an eps override

This is the trap in the release. Read it if you have fine-tuned YOLOX.

YOLOX specifies BatchNorm eps=1e-3 and momentum=0.03. Until 1.5.0 those values were applied as a post-hoc fixup that did not survive the class-count rebuild train() performs when your dataset's nc differs from the checkpoint's. Such a fine-tune trained and reported in-training validation at torch's default eps=1e-5, then reloaded for inference at 1e-3: the same tensors under different normalization.

Regular-conv sizes barely move. Depthwise n moves a lot, because its per-channel running_var is small enough for eps to dominate. On RF100-VL ball, the same nano checkpoint scores 0.566 mAP50-95 evaluated at its trained eps and 0.151 after a stock reload.

A checkpoint trained before 1.5.0 carries eps=1e-5 semantics. To report faithful numbers for it, either evaluate with BN eps overridden to 1e-5:

python
import torchfrom libreyolo import LibreYOLO model = LibreYOLO("my-yolox-finetune.pt")for module in model.model.modules():    if isinstance(module, torch.nn.BatchNorm2d):        module.eps = 1e-5 model.val(data="data.yaml")

or fold sqrt((var + 1e-3) / (var + 1e-5)) into the BN weights once and save the result. Checkpoints trained on 1.5.0 and later need neither.

D-FINE multi-scale training uses the upstream per-size recipe

base_size_repeat was hardcoded to 3 for every size. It now resolves per size as upstream specifies: n trains at fixed size with multi-scale off, s 20, m 6, l 4, x 3. Only x matched before, so n, s, m and l see a different scale distribution and converge to different metrics.

To restore the old behavior, set it explicitly:

python
from libreyolo.training.config import DFINEConfig config = DFINEConfig(base_size_repeat=3)

DEIM still uses the hardcoded 3. Family details are on D-FINE.

Worth knowing, no action needed

  • Rectangular imgsz results changed because they were wrong before. Box coordinates, RTMDet mask resizing, YOLO-NAS rescaling and validator ground-truth scaling now use per-axis height and width instead of one scalar. Square imgsz is bit-unchanged. Rectangular inference or validation run on 1.4.0 was mis-scaled. YOLO-NAS now rejects rectangular imgsz outright rather than silently producing wrong output.
  • Metrics dictionaries gained keys. max_det, ar_max_det and AR_max_det from the COCO evaluator, and metrics/loss plus metrics/loss/ce from FOMO. Values at defaults are unchanged, but anything iterating metric keys, including custom loggers and CSV headers, sees new columns.
  • Seeded YOLO9 runs that trigger a head rebuild start from a different initialization, because the seed is now applied before the rebuild rather than after. A seeded 1.4.0 fine-tune onto a different class count is not reproducible bit-for-bit on 1.5.0.
  • libreyolo[hub-kernels] on CUDA now actually engages the native MS-deform-attn kernel. 1.4.0 gated it behind a condition RF-DETR never took, so the kernel never ran. Predictions can shift at float tolerance for RF-DETR and the other deformable-attention families. Stock installs are unaffected, and LIBREYOLO_HUB_KERNELS=0 disables it.
  • libreyolo predict drops unsupported options instead of raising. The CLI filters kwargs against the model's __call__ signature, so an option a family does not accept is ignored rather than raising TypeError. An unknown flag name is still rejected with No such option.
  • Live sources change the JSON output shape. Webcams, RTSP streams and screen capture implicitly enable streaming, which emits one record per frame rather than one for the call. These sources are new in 1.5.0, so no 1.4.0 script is affected.
  • Re-exporting rfdetr-pose or yolonas-pose to ONNX yields different output names. 1.4.0 misread their multi-tensor pose heads as segmentation through an output-count heuristic. Existing .onnx files on disk are untouched.
  • On a torch-free install, results hold numpy arrays rather than torch.Tensor, so .boxes.data returns a different type and NMS tie-breaking may differ from torchvision. With torch installed, behavior is byte-for-byte unchanged. See lightweight install.
  • Config objects validate more at construction. TrainConfig gained a __post_init__ where it had none, so a config that was already invalid now raises immediately instead of failing deep into a run. ValidationConfig serialization gained an edge_thresholds key, which breaks a strict ValidationConfig(**dump) round-trip from a 1.4.0 dump.
  • Weight filenames for task-suffixed families resolve differently. segformer-b0 now resolves to LibreSegformerb0-sem.pt. This fixes auto-download 404s, and breaks any script that hardcoded the old unsuffixed filename.
  • The pytest marker experimental_backend is now extended_backend. Only relevant if you run the test suite with -m.

Checkpoints and datasets

Checkpoints written by 1.4.0 load unchanged. The schema gained imgsz_h and imgsz_w for rectangular models, and still writes the scalar imgsz = max(h, w) for older readers. ExecuTorch and MNN exports now require a sidecar, <program>.pte.json and <model>.mnn.json respectively, and HRNet exports carry pose_input: "person_crop". Dataset formats are unchanged.

Verified against LibreYOLO v1.7.0.