From d86cb966893e2b96345e596a66d1ca8656de6590 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 18 Apr 2026 21:10:21 +0200 Subject: [PATCH] `ultralytics 8.4.39` Use dashed incremented run paths (#24193) Signed-off-by: Glenn Jocher Co-authored-by: UltralyticsAssistant --- docs/en/usage/cfg.md | 2 +- docs/en/yolov5/tutorials/model_ensembling.md | 6 +++--- docs/en/yolov5/tutorials/model_pruning_and_sparsity.md | 6 +++--- docs/en/yolov5/tutorials/test_time_augmentation.md | 2 +- docs/en/yolov5/tutorials/train_custom_data.md | 2 +- tests/test_python.py | 10 +++++++++- ultralytics/__init__.py | 2 +- ultralytics/utils/files.py | 6 +++--- 8 files changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/en/usage/cfg.md b/docs/en/usage/cfg.md index 33974e9fc1..9a7e679ba6 100644 --- a/docs/en/usage/cfg.md +++ b/docs/en/usage/cfg.md @@ -168,7 +168,7 @@ Effective management of these aspects helps track progress and makes debugging a | Argument | Default | Description | | ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `project` | `'runs'` | Specifies the root directory for saving training runs. Each run is saved in a separate subdirectory. | -| `name` | `'exp'` | Defines the experiment name. If unspecified, YOLO increments this name for each run (e.g., `exp`, `exp2`) to avoid overwriting. | +| `name` | `'exp'` | Defines the experiment name. If unspecified, YOLO increments this name for each run (e.g., `exp`, `exp-2`) to avoid overwriting. | | `exist_ok` | `False` | Determines whether to overwrite an existing experiment directory. `True` allows overwriting; `False` prevents it. | | `plots` | `True` | Controls the generation and saving of training and validation plots. Set to `True` to create plots like loss curves, [precision](https://www.ultralytics.com/glossary/precision)-[recall](https://www.ultralytics.com/glossary/recall) curves, and sample predictions for visual tracking of performance. | | `save` | `True` | Enables saving training checkpoints and final model weights. Set to `True` to save model states periodically, allowing training resumption or model deployment. | diff --git a/docs/en/yolov5/tutorials/model_ensembling.md b/docs/en/yolov5/tutorials/model_ensembling.md index d1cc654f18..b7d2de1cb1 100644 --- a/docs/en/yolov5/tutorials/model_ensembling.md +++ b/docs/en/yolov5/tutorials/model_ensembling.md @@ -72,7 +72,7 @@ Multiple pretrained models can be ensembled together at test and inference time python val.py --weights yolov5x.pt yolov5l6.pt --data coco.yaml --img 640 --half ``` -You can list as many checkpoints as you would like, including custom weights such as `runs/train/exp5/weights/best.pt`. YOLOv5 will automatically run each model, align the predictions on a per-image basis, and average the outputs before performing NMS. +You can list as many checkpoints as you would like, including custom weights such as `runs/train/exp-5/weights/best.pt`. YOLOv5 will automatically run each model, align the predictions on a per-image basis, and average the outputs before performing NMS. Output: @@ -91,7 +91,7 @@ val: Scanning '../datasets/coco/val2017.cache' images and labels... 4952 found, all 5000 36335 0.747 0.637 0.692 0.502 Speed: 0.1ms pre-process, 39.5ms inference, 2.0ms NMS per image at shape (32, 3, 640, 640) # <--- ensemble speed -Evaluating pycocotools mAP... saving runs/val/exp3/yolov5x_predictions.json... +Evaluating pycocotools mAP... saving runs/val/exp-3/yolov5x_predictions.json... ... Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.515 # <--- ensemble mAP Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.699 @@ -128,7 +128,7 @@ Ensemble created with ['yolov5x.pt', 'yolov5l6.pt'] image 1/2 /content/yolov5/data/images/bus.jpg: 640x512 4 persons, 1 bus, 1 tie, Done. (0.063s) image 2/2 /content/yolov5/data/images/zidane.jpg: 384x640 3 persons, 2 ties, Done. (0.056s) -Results saved to runs/detect/exp2 +Results saved to runs/detect/exp-2 Done. (0.223s) ``` diff --git a/docs/en/yolov5/tutorials/model_pruning_and_sparsity.md b/docs/en/yolov5/tutorials/model_pruning_and_sparsity.md index 0c12305d9b..606ceec1b4 100644 --- a/docs/en/yolov5/tutorials/model_pruning_and_sparsity.md +++ b/docs/en/yolov5/tutorials/model_pruning_and_sparsity.md @@ -50,7 +50,7 @@ val: Scanning '/content/datasets/coco/val2017.cache' images and labels... 4952 f all 5000 36335 0.732 0.628 0.683 0.496 Speed: 0.1ms pre-process, 5.2ms inference, 1.7ms NMS per image at shape (32, 3, 640, 640) # <--- base speed -Evaluating pycocotools mAP... saving runs/val/exp2/yolov5x_predictions.json... +Evaluating pycocotools mAP... saving runs/val/exp-2/yolov5x_predictions.json... ... Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.507 # <--- base mAP Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.689 @@ -87,7 +87,7 @@ val: Scanning '/content/datasets/coco/val2017.cache' images and labels... 4952 f all 5000 36335 0.724 0.614 0.671 0.478 Speed: 0.1ms pre-process, 5.2ms inference, 1.7ms NMS per image at shape (32, 3, 640, 640) # <--- prune speed -Evaluating pycocotools mAP... saving runs/val/exp3/yolov5x_predictions.json... +Evaluating pycocotools mAP... saving runs/val/exp-3/yolov5x_predictions.json... ... Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.489 # <--- prune mAP Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.677 @@ -101,7 +101,7 @@ Evaluating pycocotools mAP... saving runs/val/exp3/yolov5x_predictions.json... Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.496 Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.722 Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.803 -Results saved to runs/val/exp3 +Results saved to runs/val/exp-3 ``` ## Results Analysis diff --git a/docs/en/yolov5/tutorials/test_time_augmentation.md b/docs/en/yolov5/tutorials/test_time_augmentation.md index d9f7357868..5eddbf8ea7 100644 --- a/docs/en/yolov5/tutorials/test_time_augmentation.md +++ b/docs/en/yolov5/tutorials/test_time_augmentation.md @@ -81,7 +81,7 @@ val: New cache created: ../datasets/coco/val2017.cache all 5000 36335 0.718 0.656 0.695 0.503 Speed: 0.2ms pre-process, 80.6ms inference, 2.7ms NMS per image at shape (32, 3, 832, 832) # <--- TTA speed -Evaluating pycocotools mAP... saving runs/val/exp2/yolov5x_predictions.json... +Evaluating pycocotools mAP... saving runs/val/exp-2/yolov5x_predictions.json... ... Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.516 # <--- TTA mAP Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.701 diff --git a/docs/en/yolov5/tutorials/train_custom_data.md b/docs/en/yolov5/tutorials/train_custom_data.md index 6d4048ff83..fbf0b112a7 100644 --- a/docs/en/yolov5/tutorials/train_custom_data.md +++ b/docs/en/yolov5/tutorials/train_custom_data.md @@ -170,7 +170,7 @@ python train.py --img 640 --batch 16 --epochs 3 --data coco128.yaml --weights yo 💡 Always train using datasets stored locally. Accessing data from network drives (like Google Drive) or remote storage can be significantly slower and impede training performance. Copying your dataset to a local SSD is often the best practice. -All training outputs, including weights and logs, are saved in the `runs/train/` directory. Each training session creates a new subdirectory (e.g., `runs/train/exp`, `runs/train/exp2`, etc.). For an interactive, hands-on experience, explore the training section in our official tutorial notebooks: Open In Colab Open In Kaggle +All training outputs, including weights and logs, are saved in the `runs/train/` directory. Each training session creates a new subdirectory (e.g., `runs/train/exp`, `runs/train/exp-2`, etc.). For an interactive, hands-on experience, explore the training section in our official tutorial notebooks: Open In Colab Open In Kaggle ## 4. Visualize diff --git a/tests/test_python.py b/tests/test_python.py index 871c7bb8af..03c22af9cf 100644 --- a/tests/test_python.py +++ b/tests/test_python.py @@ -549,7 +549,7 @@ def test_utils_ops(): def test_utils_files(tmp_path): """Test file handling utilities including file age, date, and paths with spaces.""" - from ultralytics.utils.files import file_age, file_date, get_latest_run, spaces_in_path + from ultralytics.utils.files import file_age, file_date, get_latest_run, increment_path, spaces_in_path file_age(SOURCE) file_date(SOURCE) @@ -560,6 +560,14 @@ def test_utils_files(tmp_path): with spaces_in_path(path) as new_path: print(new_path) + exp_dir = tmp_path / "runs" / "exp" + exp_dir.mkdir(parents=True) + assert increment_path(exp_dir) == tmp_path / "runs" / "exp-2" + + results_file = exp_dir / "results.txt" + results_file.touch() + assert increment_path(results_file) == exp_dir / "results-2.txt" + @pytest.mark.slow def test_utils_patches_torch_save(tmp_path): diff --git a/ultralytics/__init__.py b/ultralytics/__init__.py index 1184e63c07..dd953fc09d 100644 --- a/ultralytics/__init__.py +++ b/ultralytics/__init__.py @@ -1,6 +1,6 @@ # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license -__version__ = "8.4.38" +__version__ = "8.4.39" import importlib import os diff --git a/ultralytics/utils/files.py b/ultralytics/utils/files.py index 1d40f34c45..a58083a29d 100644 --- a/ultralytics/utils/files.py +++ b/ultralytics/utils/files.py @@ -103,7 +103,7 @@ def spaces_in_path(path: str | Path): yield path -def increment_path(path: str | Path, exist_ok: bool = False, sep: str = "", mkdir: bool = False) -> Path: +def increment_path(path: str | Path, exist_ok: bool = False, sep: str = "-", mkdir: bool = False) -> Path: """Increment a file or directory path, i.e., runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... etc. If the path exists and `exist_ok` is not True, the path will be incremented by appending a number and `sep` to the @@ -125,13 +125,13 @@ def increment_path(path: str | Path, exist_ok: bool = False, sep: str = "", mkdi >>> path = Path("runs/exp") >>> new_path = increment_path(path) >>> print(new_path) - runs/exp2 + runs/exp-2 Increment a file path: >>> path = Path("runs/exp/results.txt") >>> new_path = increment_path(path) >>> print(new_path) - runs/exp/results2.txt + runs/exp/results-2.txt """ path = Path(path) # os-agnostic if path.exists() and not exist_ok: