diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index ea9d0bc..e3e2e06 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -18,8 +18,9 @@ jobs: pr-message: | 👋 Hello @${{ github.actor }}, thank you for submitting a YOLOv8 🚀 PR! To allow your work to be integrated as seamlessly as possible, we advise you to: - - ✅ Verify your PR is **up-to-date** with `ultralytics/ultralytics` `main` branch. If your PR is behind you can update your code by clicking the 'Update branch' button or by running `git pull` and `git merge master` locally. + - ✅ Verify your PR is **up-to-date** with `ultralytics/ultralytics` `main` branch. If your PR is behind you can update your code by clicking the 'Update branch' button or by running `git pull` and `git merge main` locally. - ✅ Verify all YOLOv8 Continuous Integration (CI) **checks are passing**. + - ✅ Update YOLOv8 [Docs](https://docs.ultralytics.com) for any new or updated features. - ✅ Reduce changes to the absolute **minimum** required for your bug fix or feature addition. _"It is not daily increase but daily decrease, hack away the unessential. The closer to the source, the less wastage there is."_ — Bruce Lee See our [Contributing Guide](https://github.com/ultralytics/ultralytics/blob/main/CONTRIBUTING.md) for details and let us know if you have any questions! @@ -33,7 +34,7 @@ jobs: ## Install - Pip install the `ultralytics` package including all [requirements.txt](https://github.com/ultralytics/ultralytics/blob/main/requirements.txt) in a [**Python>=3.7**](https://www.python.org/) environment with [**PyTorch>=1.7**](https://pytorch.org/get-started/locally/). + Pip install the `ultralytics` package including all [requirements](https://github.com/ultralytics/ultralytics/blob/main/requirements.txt) in a [**Python>=3.7**](https://www.python.org/) environment with [**PyTorch>=1.7**](https://pytorch.org/get-started/locally/). ```bash pip install ultralytics diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index 816643a..2baea63 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -23,7 +23,7 @@ jobs: with: fail: true # accept 429(Instagram, 'too many requests'), 999(LinkedIn, 'unknown status code'), Timeout(Twitter) - args: --accept 429,999 --exclude twitter.com --verbose --no-progress './**/*.md' './**/*.html' + args: --accept 429,999 --exclude-loopback --exclude twitter.com --verbose --no-progress './**/*.md' './**/*.html' env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} @@ -33,6 +33,6 @@ jobs: with: fail: true # accept 429(Instagram, 'too many requests'), 999(LinkedIn, 'unknown status code'), Timeout(Twitter) - args: --accept 429,999 --exclude twitter.com,url.com --verbose --no-progress './**/*.md' './**/*.html' './**/*.yml' './**/*.yaml' './**/*.py' './**/*.ipynb' + args: --accept 429,999 --exclude-loopback --exclude twitter.com,url.com --verbose --no-progress './**/*.md' './**/*.html' './**/*.yml' './**/*.yaml' './**/*.py' './**/*.ipynb' env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/README.md b/README.md index 7713612..c1683e4 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ full documentation on training, validation, prediction and deployment. Install Pip install the ultralytics package including -all [requirements.txt](https://github.com/ultralytics/ultralytics/blob/main/requirements.txt) in a +all [requirements](https://github.com/ultralytics/ultralytics/blob/main/requirements.txt) in a [**Python>=3.7**](https://www.python.org/) environment with [**PyTorch>=1.7**](https://pytorch.org/get-started/locally/). @@ -105,28 +105,11 @@ success = model.export(format="onnx") # export the model to ONNX format Ultralytics [release](https://github.com/ultralytics/assets/releases). See YOLOv8 [Python Docs](https://docs.ultralytics.com/usage/python) for more examples. -#### Model Architectures - -⭐ **NEW** YOLOv5u anchor free models are now available. - -All supported model architectures can be found in the [Models](./ultralytics/models/) section. - -#### Known Issues / TODOs - -We are still working on several parts of YOLOv8! We aim to have these completed soon to bring the YOLOv8 feature set up -to par with YOLOv5, including export and inference to all the same formats. We are also writing a YOLOv8 paper which we -will submit to [arxiv.org](https://arxiv.org) once complete. - -- [x] TensorFlow exports -- [x] DDP resume -- [ ] [arxiv.org](https://arxiv.org) paper - ##
Models
-All YOLOv8 pretrained models are available here. Detection and Segmentation models are pretrained on the COCO dataset, -while Classification models are pretrained on the ImageNet dataset. +All YOLOv8 pretrained models are available here. Detect, Segment and Pose models are pretrained on the [COCO](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/coco.yaml) dataset, while Classify models are pretrained on the [ImageNet](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/ImageNet.yaml) dataset. [Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models) download automatically from the latest Ultralytics [release](https://github.com/ultralytics/assets/releases) on first use. @@ -147,7 +130,7 @@ See [Detection Docs](https://docs.ultralytics.com/tasks/detect/) for usage examp
Reproduce by `yolo val detect data=coco.yaml device=0` - **Speed** averaged over COCO val images using an [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) instance. -
Reproduce by `yolo val detect data=coco128.yaml batch=1 device=0/cpu` +
Reproduce by `yolo val detect data=coco128.yaml batch=1 device=0|cpu` @@ -167,7 +150,7 @@ See [Segmentation Docs](https://docs.ultralytics.com/tasks/segment/) for usage e
Reproduce by `yolo val segment data=coco.yaml device=0` - **Speed** averaged over COCO val images using an [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) instance. -
Reproduce by `yolo val segment data=coco128-seg.yaml batch=1 device=0/cpu` +
Reproduce by `yolo val segment data=coco128-seg.yaml batch=1 device=0|cpu` @@ -187,7 +170,7 @@ See [Classification Docs](https://docs.ultralytics.com/tasks/classify/) for usag
Reproduce by `yolo val classify data=path/to/ImageNet device=0` - **Speed** averaged over ImageNet val images using an [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) instance. -
Reproduce by `yolo val classify data=path/to/ImageNet batch=1 device=0/cpu` +
Reproduce by `yolo val classify data=path/to/ImageNet batch=1 device=0|cpu` diff --git a/README.zh-CN.md b/README.zh-CN.md index 2765db5..81e568c 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -52,7 +52,7 @@ SOTA 模型。它在以前成功的 YOLO 版本基础上,引入了新的功能
安装 -Pip 安装包含所有 [requirements.txt](https://github.com/ultralytics/ultralytics/blob/main/requirements.txt) 的 +Pip 安装包含所有 [requirements](https://github.com/ultralytics/ultralytics/blob/main/requirements.txt) 的 ultralytics 包,环境要求 [**Python>=3.7**](https://www.python.org/),且 [\*\*PyTorch>=1.7 \*\*](https://pytorch.org/get-started/locally/)。 @@ -100,15 +100,6 @@ success = model.export(format="onnx") # 将模型导出为 ONNX 格式 [模型](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models) 会从 Ultralytics [发布页](https://github.com/ultralytics/ultralytics/releases) 自动下载。 -### 已知问题 / 待办事项 - -我们仍在努力完善 YOLOv8 的几个部分!我们的目标是尽快完成这些工作,使 YOLOv8 的功能设置达到YOLOv5 -的水平,包括对所有相同格式的导出和推理。我们还在写一篇 YOLOv8 的论文,一旦完成,我们将提交给 [arxiv.org](https://arxiv.org)。 - -- [x] TensorFlow 导出 -- [x] DDP 恢复训练 -- [ ] [arxiv.org](https://arxiv.org) 论文 -
##
模型
@@ -132,7 +123,7 @@ Ultralytics [发布页](https://github.com/ultralytics/ultralytics/releases) 自
复现命令 `yolo val detect data=coco.yaml device=0` - **推理速度**使用 COCO 验证集图片推理时间进行平均得到,测试环境使用 [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) 实例。 -
复现命令 `yolo val detect data=coco128.yaml batch=1 device=0/cpu` +
复现命令 `yolo val detect data=coco128.yaml batch=1 device=0|cpu` @@ -150,7 +141,7 @@ Ultralytics [发布页](https://github.com/ultralytics/ultralytics/releases) 自
复现命令 `yolo val segment data=coco.yaml device=0` - **推理速度**使用 COCO 验证集图片推理时间进行平均得到,测试环境使用 [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) 实例。 -
复现命令 `yolo val segment data=coco128-seg.yaml batch=1 device=0/cpu` +
复现命令 `yolo val segment data=coco128-seg.yaml batch=1 device=0|cpu` @@ -168,7 +159,7 @@ Ultralytics [发布页](https://github.com/ultralytics/ultralytics/releases) 自
复现命令 `yolo val classify data=path/to/ImageNet device=0` - **推理速度**使用 ImageNet 验证集图片推理时间进行平均得到,测试环境使用 [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) 实例。 -
复现命令 `yolo val classify data=path/to/ImageNet batch=1 device=0/cpu` +
复现命令 `yolo val classify data=path/to/ImageNet batch=1 device=0|cpu` diff --git a/docs/tasks/classify.md b/docs/tasks/classify.md index 9e2a419..3c8cb6f 100644 --- a/docs/tasks/classify.md +++ b/docs/tasks/classify.md @@ -9,9 +9,31 @@ of that class are located or what their exact shape is. !!! tip "Tip" - YOLOv8 _classification_ models use the `-cls` suffix, i.e. `yolov8n-cls.pt` and are pretrained on ImageNet. - -[Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models/v8){ .md-button .md-button--primary} + YOLOv8 Classify models use the `-cls` suffix, i.e. `yolov8n-cls.pt` and are pretrained on [ImageNet](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/ImageNet.yaml). + +## [Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models/v8) + +YOLOv8 pretrained Classify models are shown here. Detect, Segment and Pose models are pretrained on +the [COCO](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/coco.yaml) dataset, while Classify +models are pretrained on +the [ImageNet](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/ImageNet.yaml) dataset. + +[Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models) download automatically from the latest +Ultralytics [release](https://github.com/ultralytics/assets/releases) on first use. + +| Model | size
(pixels) | acc
top1 | acc
top5 | Speed
CPU ONNX
(ms) | Speed
A100 TensorRT
(ms) | params
(M) | FLOPs
(B) at 640 | +|----------------------------------------------------------------------------------------------|-----------------------|------------------|------------------|--------------------------------|-------------------------------------|--------------------|--------------------------| +| [YOLOv8n-cls](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n-cls.pt) | 224 | 66.6 | 87.0 | 12.9 | 0.31 | 2.7 | 4.3 | +| [YOLOv8s-cls](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8s-cls.pt) | 224 | 72.3 | 91.1 | 23.4 | 0.35 | 6.4 | 13.5 | +| [YOLOv8m-cls](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8m-cls.pt) | 224 | 76.4 | 93.2 | 85.4 | 0.62 | 17.0 | 42.7 | +| [YOLOv8l-cls](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8l-cls.pt) | 224 | 78.0 | 94.1 | 163.0 | 0.87 | 37.5 | 99.7 | +| [YOLOv8x-cls](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8x-cls.pt) | 224 | 78.4 | 94.3 | 232.0 | 1.01 | 57.4 | 154.8 | + +- **acc** values are model accuracies on the [ImageNet](https://www.image-net.org/) dataset validation set. +
Reproduce by `yolo val classify data=path/to/ImageNet device=0` +- **Speed** averaged over ImageNet val images using an [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) + instance. +
Reproduce by `yolo val classify data=path/to/ImageNet batch=1 device=0|cpu` ## Train diff --git a/docs/tasks/detect.md b/docs/tasks/detect.md index 7202010..34b580d 100644 --- a/docs/tasks/detect.md +++ b/docs/tasks/detect.md @@ -9,9 +9,31 @@ scene, but don't need to know exactly where the object is or its exact shape. !!! tip "Tip" - YOLOv8 _detection_ models have no suffix and are the default YOLOv8 models, i.e. `yolov8n.pt` and are pretrained on COCO. - -[Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models/v8){ .md-button .md-button--primary} + YOLOv8 Detect models are the default YOLOv8 models, i.e. `yolov8n.pt` and are pretrained on [COCO](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/coco.yaml). + +## [Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models/v8) + +YOLOv8 pretrained Detect models are shown here. Detect, Segment and Pose models are pretrained on +the [COCO](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/coco.yaml) dataset, while Classify +models are pretrained on +the [ImageNet](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/ImageNet.yaml) dataset. + +[Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models) download automatically from the latest +Ultralytics [release](https://github.com/ultralytics/assets/releases) on first use. + +| Model | size
(pixels) | mAPval
50-95 | Speed
CPU ONNX
(ms) | Speed
A100 TensorRT
(ms) | params
(M) | FLOPs
(B) | +|--------------------------------------------------------------------------------------|-----------------------|----------------------|--------------------------------|-------------------------------------|--------------------|-------------------| +| [YOLOv8n](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n.pt) | 640 | 37.3 | 80.4 | 0.99 | 3.2 | 8.7 | +| [YOLOv8s](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8s.pt) | 640 | 44.9 | 128.4 | 1.20 | 11.2 | 28.6 | +| [YOLOv8m](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8m.pt) | 640 | 50.2 | 234.7 | 1.83 | 25.9 | 78.9 | +| [YOLOv8l](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8l.pt) | 640 | 52.9 | 375.2 | 2.39 | 43.7 | 165.2 | +| [YOLOv8x](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8x.pt) | 640 | 53.9 | 479.1 | 3.53 | 68.2 | 257.8 | + +- **mAPval** values are for single-model single-scale on [COCO val2017](http://cocodataset.org) dataset. +
Reproduce by `yolo val detect data=coco.yaml device=0` +- **Speed** averaged over COCO val images using an [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) + instance. +
Reproduce by `yolo val detect data=coco128.yaml batch=1 device=0|cpu` ## Train diff --git a/docs/tasks/segment.md b/docs/tasks/segment.md index 6829a9f..2ff7f58 100644 --- a/docs/tasks/segment.md +++ b/docs/tasks/segment.md @@ -9,9 +9,31 @@ segmentation is useful when you need to know not only where objects are in an im !!! tip "Tip" - YOLOv8 _segmentation_ models use the `-seg` suffix, i.e. `yolov8n-seg.pt` and are pretrained on COCO. - -[Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models/v8){ .md-button .md-button--primary} + YOLOv8 Segment models use the `-seg` suffix, i.e. `yolov8n-seg.pt` and are pretrained on [COCO](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/coco.yaml). + +## [Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models/v8) + +YOLOv8 pretrained Segment models are shown here. Detect, Segment and Pose models are pretrained on +the [COCO](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/coco.yaml) dataset, while Classify +models are pretrained on +the [ImageNet](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/datasets/ImageNet.yaml) dataset. + +[Models](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/models) download automatically from the latest +Ultralytics [release](https://github.com/ultralytics/assets/releases) on first use. + +| Model | size
(pixels) | mAPbox
50-95 | mAPmask
50-95 | Speed
CPU ONNX
(ms) | Speed
A100 TensorRT
(ms) | params
(M) | FLOPs
(B) | +|----------------------------------------------------------------------------------------------|-----------------------|----------------------|-----------------------|--------------------------------|-------------------------------------|--------------------|-------------------| +| [YOLOv8n-seg](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n-seg.pt) | 640 | 36.7 | 30.5 | 96.1 | 1.21 | 3.4 | 12.6 | +| [YOLOv8s-seg](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8s-seg.pt) | 640 | 44.6 | 36.8 | 155.7 | 1.47 | 11.8 | 42.6 | +| [YOLOv8m-seg](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8m-seg.pt) | 640 | 49.9 | 40.8 | 317.0 | 2.18 | 27.3 | 110.2 | +| [YOLOv8l-seg](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8l-seg.pt) | 640 | 52.3 | 42.6 | 572.4 | 2.79 | 46.0 | 220.5 | +| [YOLOv8x-seg](https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8x-seg.pt) | 640 | 53.4 | 43.4 | 712.1 | 4.02 | 71.8 | 344.1 | + +- **mAPval** values are for single-model single-scale on [COCO val2017](http://cocodataset.org) dataset. +
Reproduce by `yolo val segment data=coco.yaml device=0` +- **Speed** averaged over COCO val images using an [Amazon EC2 P4d](https://aws.amazon.com/ec2/instance-types/p4/) + instance. +
Reproduce by `yolo val segment data=coco128-seg.yaml batch=1 device=0|cpu` ## Train diff --git a/examples/tutorial.ipynb b/examples/tutorial.ipynb index 2a3a8b3..22f271b 100644 --- a/examples/tutorial.ipynb +++ b/examples/tutorial.ipynb @@ -59,21 +59,21 @@ "colab": { "base_uri": "https://localhost:8080/" }, - "outputId": "9bda69d4-e57f-404b-b6fe-117234e24677" + "outputId": "ea235da2-8fb5-4094-9dc2-8523d0800a22" }, "source": [ "%pip install ultralytics\n", "import ultralytics\n", "ultralytics.checks()" ], - "execution_count": null, + "execution_count": 1, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ - "Ultralytics YOLOv8.0.24 🚀 Python-3.8.10 torch-1.13.1+cu116 CUDA:0 (Tesla T4, 15110MiB)\n", - "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 30.8/166.8 GB disk)\n" + "Ultralytics YOLOv8.0.57 🚀 Python-3.9.16 torch-1.13.1+cu116 CUDA:0 (Tesla T4, 15102MiB)\n", + "Setup complete ✅ (2 CPUs, 12.7 GB RAM, 25.9/166.8 GB disk)\n" ] } ] @@ -96,28 +96,24 @@ "colab": { "base_uri": "https://localhost:8080/" }, - "outputId": "abe002b5-3df9-4324-9e50-1587394398a2" + "outputId": "fe0a5a26-3bcc-4c1f-e688-cae00ee5b958" }, "source": [ "# Run inference on an image with YOLOv8n\n", "!yolo predict model=yolov8n.pt source='https://ultralytics.com/images/zidane.jpg'" ], - "execution_count": null, + "execution_count": 3, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "Downloading https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n.pt to yolov8n.pt...\n", - "\r 0% 0.00/6.23M [00:00= n: raise KeyError(f'{n}-class dataset requires class indices 0-{n - 1}, but you have invalid class indices ' diff --git a/ultralytics/nn/tasks.py b/ultralytics/nn/tasks.py index 2c03083..dffd8e6 100644 --- a/ultralytics/nn/tasks.py +++ b/ultralytics/nn/tasks.py @@ -245,7 +245,7 @@ class SegmentationModel(DetectionModel): super().__init__(cfg, ch, nc, verbose) def _forward_augment(self, x): - raise NotImplementedError('WARNING ⚠️ SegmentationModel has not supported augment inference yet!') + raise NotImplementedError(emojis('WARNING ⚠️ SegmentationModel has not supported augment inference yet!')) class ClassificationModel(BaseModel): diff --git a/ultralytics/tracker/track.py b/ultralytics/tracker/track.py index 13a7ec6..78e32c6 100644 --- a/ultralytics/tracker/track.py +++ b/ultralytics/tracker/track.py @@ -3,9 +3,7 @@ import torch from ultralytics.yolo.utils import IterableSimpleNamespace, yaml_load -from ultralytics.yolo.utils.checks import check_requirements, check_yaml - -check_requirements('lap') # for linear_assignment +from ultralytics.yolo.utils.checks import check_yaml from .trackers import BOTSORT, BYTETracker diff --git a/ultralytics/tracker/utils/matching.py b/ultralytics/tracker/utils/matching.py index 561f385..a2e2488 100644 --- a/ultralytics/tracker/utils/matching.py +++ b/ultralytics/tracker/utils/matching.py @@ -1,12 +1,20 @@ # Ultralytics YOLO 🚀, GPL-3.0 license -import lap import numpy as np import scipy from scipy.spatial.distance import cdist from .kalman_filter import chi2inv95 +try: + import lap # for linear_assignment + assert lap.__version__ # verify package is not directory +except (ImportError, AssertionError, AttributeError): + from ultralytics.yolo.utils.checks import check_requirements + + check_requirements('lap>=0.4') # install + import lap + def merge_matches(m1, m2, shape): O, P, Q = shape diff --git a/ultralytics/yolo/data/base.py b/ultralytics/yolo/data/base.py index cd4f920..f9fd90f 100644 --- a/ultralytics/yolo/data/base.py +++ b/ultralytics/yolo/data/base.py @@ -12,8 +12,8 @@ import numpy as np from torch.utils.data import Dataset from tqdm import tqdm -from ..utils import NUM_THREADS, TQDM_BAR_FORMAT -from .utils import HELP_URL, IMG_FORMATS, LOCAL_RANK +from ..utils import LOCAL_RANK, NUM_THREADS, TQDM_BAR_FORMAT +from .utils import HELP_URL, IMG_FORMATS class BaseDataset(Dataset): diff --git a/ultralytics/yolo/data/build.py b/ultralytics/yolo/data/build.py index 90fb86d..d4e0b07 100644 --- a/ultralytics/yolo/data/build.py +++ b/ultralytics/yolo/data/build.py @@ -14,10 +14,10 @@ from ultralytics.yolo.data.dataloaders.stream_loaders import (LOADERS, LoadImage from ultralytics.yolo.data.utils import IMG_FORMATS, VID_FORMATS from ultralytics.yolo.utils.checks import check_file -from ..utils import LOGGER, colorstr +from ..utils import LOGGER, RANK, colorstr from ..utils.torch_utils import torch_distributed_zero_first from .dataset import ClassificationDataset, YOLODataset -from .utils import PIN_MEMORY, RANK +from .utils import PIN_MEMORY class InfiniteDataLoader(dataloader.DataLoader): diff --git a/ultralytics/yolo/data/dataloaders/stream_loaders.py b/ultralytics/yolo/data/dataloaders/stream_loaders.py index 0dec447..876d44e 100644 --- a/ultralytics/yolo/data/dataloaders/stream_loaders.py +++ b/ultralytics/yolo/data/dataloaders/stream_loaders.py @@ -335,6 +335,7 @@ class LoadTensor: def __init__(self, imgs) -> None: self.im0 = imgs self.bs = imgs.shape[0] + self.mode = 'image' def __iter__(self): self.count = 0 @@ -346,6 +347,9 @@ class LoadTensor: self.count += 1 return None, self.im0, self.im0, None, '' # self.paths, im, self.im0, None, '' + def __len__(self): + return self.bs + def autocast_list(source): """ diff --git a/ultralytics/yolo/data/dataset.py b/ultralytics/yolo/data/dataset.py index 048e16e..2bc7536 100644 --- a/ultralytics/yolo/data/dataset.py +++ b/ultralytics/yolo/data/dataset.py @@ -10,10 +10,10 @@ import torch import torchvision from tqdm import tqdm -from ..utils import NUM_THREADS, TQDM_BAR_FORMAT, is_dir_writeable +from ..utils import LOCAL_RANK, NUM_THREADS, TQDM_BAR_FORMAT, is_dir_writeable from .augment import Compose, Format, Instances, LetterBox, classify_albumentations, classify_transforms, v8_transforms from .base import BaseDataset -from .utils import HELP_URL, LOCAL_RANK, LOGGER, get_hash, img2label_paths, verify_image_label +from .utils import HELP_URL, LOGGER, get_hash, img2label_paths, verify_image_label class YOLODataset(BaseDataset): diff --git a/ultralytics/yolo/data/utils.py b/ultralytics/yolo/data/utils.py index d2708b7..dc67b0e 100644 --- a/ultralytics/yolo/data/utils.py +++ b/ultralytics/yolo/data/utils.py @@ -25,8 +25,6 @@ from ultralytics.yolo.utils.ops import segments2boxes HELP_URL = 'See https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data' IMG_FORMATS = 'bmp', 'dng', 'jpeg', 'jpg', 'mpo', 'png', 'tif', 'tiff', 'webp', 'pfm' # image suffixes VID_FORMATS = 'asf', 'avi', 'gif', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'ts', 'wmv', 'webm' # video suffixes -LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html -RANK = int(os.getenv('RANK', -1)) PIN_MEMORY = str(os.getenv('PIN_MEMORY', True)).lower() == 'true' # global pin_memory for dataloaders IMAGENET_MEAN = 0.485, 0.456, 0.406 # RGB mean IMAGENET_STD = 0.229, 0.224, 0.225 # RGB standard deviation diff --git a/ultralytics/yolo/engine/model.py b/ultralytics/yolo/engine/model.py index e401432..57d8e0a 100644 --- a/ultralytics/yolo/engine/model.py +++ b/ultralytics/yolo/engine/model.py @@ -2,6 +2,7 @@ import sys from pathlib import Path +from typing import Union from ultralytics import yolo # noqa from ultralytics.nn.tasks import (ClassificationModel, DetectionModel, SegmentationModel, attempt_load_one_weight, @@ -67,7 +68,7 @@ class YOLO: list(ultralytics.yolo.engine.results.Results): The prediction results. """ - def __init__(self, model='yolov8n.pt', task=None, session=None) -> None: + def __init__(self, model: Union[str, Path] = 'yolov8n.pt', task=None, session=None) -> None: """ Initializes the YOLO model. @@ -87,6 +88,7 @@ class YOLO: self.session = session # HUB session # Load or create new YOLO model + model = str(model).strip() # strip spaces suffix = Path(model).suffix if not suffix and Path(model).stem in GITHUB_ASSET_STEMS: model, suffix = Path(model).with_suffix('.pt'), '.pt' # add suffix, i.e. yolov8n -> yolov8n.pt diff --git a/ultralytics/yolo/engine/predictor.py b/ultralytics/yolo/engine/predictor.py index 66342bd..ee0bab0 100644 --- a/ultralytics/yolo/engine/predictor.py +++ b/ultralytics/yolo/engine/predictor.py @@ -42,6 +42,18 @@ from ultralytics.yolo.utils.checks import check_imgsz, check_imshow from ultralytics.yolo.utils.files import increment_path from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode +STREAM_WARNING = """ + WARNING ⚠️ stream/video/webcam/dir predict source will accumulate results in RAM unless `stream=True` is passed, + causing potential out-of-memory errors for large sources or long-running streams/videos. + + Usage: + results = model(source=..., stream=True) # generator of Results objects + for r in results: + boxes = r.boxes # Boxes object for bbox outputs + masks = r.masks # Masks object for segment masks outputs + probs = r.probs # Class probabilities for classification outputs +""" + class BasePredictor: """ @@ -108,6 +120,7 @@ class BasePredictor: return preds def __call__(self, source=None, model=None, stream=False): + self.stream = stream if stream: return self.stream_inference(source, model) else: @@ -132,6 +145,10 @@ class BasePredictor: stride=self.model.stride, auto=self.model.pt) self.source_type = self.dataset.source_type + if not getattr(self, 'stream', True) and (self.dataset.mode == 'stream' or # streams + len(self.dataset) > 1000 or # images + any(getattr(self.dataset, 'video_flag', [False]))): # videos + LOGGER.warning(STREAM_WARNING) self.vid_path, self.vid_writer = [None] * self.dataset.bs, [None] * self.dataset.bs @smart_inference_mode() diff --git a/ultralytics/yolo/engine/results.py b/ultralytics/yolo/engine/results.py index 3b70877..8f4357f 100644 --- a/ultralytics/yolo/engine/results.py +++ b/ultralytics/yolo/engine/results.py @@ -5,7 +5,6 @@ Ultralytics Results, Boxes and Masks classes for handling inference results Usage: See https://docs.ultralytics.com/modes/predict/ """ -import pprint from copy import deepcopy from functools import lru_cache @@ -13,11 +12,11 @@ import numpy as np import torch import torchvision.transforms.functional as F -from ultralytics.yolo.utils import LOGGER, ops +from ultralytics.yolo.utils import LOGGER, SimpleClass, ops from ultralytics.yolo.utils.plotting import Annotator, colors -class Results: +class Results(SimpleClass): """ A class for storing and manipulating inference results. @@ -96,17 +95,6 @@ class Results: for k in self.keys: return len(getattr(self, k)) - def __str__(self): - attr = {k: v for k, v in vars(self).items() if not isinstance(v, type(self))} - return pprint.pformat(attr, indent=2, width=120, depth=10, compact=True) - - def __repr__(self): - return self.__str__() - - def __getattr__(self, attr): - name = self.__class__.__name__ - raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") - @property def keys(self): return [k for k in self._keys if getattr(self, k) is not None] @@ -153,7 +141,7 @@ class Results: return np.asarray(annotator.im) if annotator.pil else annotator.im -class Boxes: +class Boxes(SimpleClass): """ A class for storing and manipulating detection boxes. @@ -242,15 +230,6 @@ class Boxes: def pandas(self): LOGGER.info('results.pandas() method not yet implemented') - ''' - new = copy(self) # return copy - ca = 'xmin', 'ymin', 'xmax', 'ymax', 'confidence', 'class', 'name' # xyxy columns - cb = 'xcenter', 'ycenter', 'width', 'height', 'confidence', 'class', 'name' # xywh columns - for k, c in zip(['xyxy', 'xyxyn', 'xywh', 'xywhn'], [ca, ca, cb, cb]): - a = [[x[:5] + [int(x[5]), self.names[int(x[5])]] for x in x.tolist()] for x in getattr(self, k)] # update - setattr(new, k, [pd.DataFrame(x, columns=c) for x in a]) - return new - ''' @property def shape(self): @@ -263,25 +242,11 @@ class Boxes: def __len__(self): # override len(results) return len(self.boxes) - def __str__(self): - return self.boxes.__str__() - - def __repr__(self): - return (f'{self.__class__.__module__}.{self.__class__.__name__}\n' - f'type: {self.boxes.__class__.__module__}.{self.boxes.__class__.__name__}\n' - f'shape: {self.boxes.shape}\n' - f'dtype: {self.boxes.dtype}\n' - f'{self.boxes.__repr__()}') - def __getitem__(self, idx): return Boxes(self.boxes[idx], self.orig_shape) - def __getattr__(self, attr): - name = self.__class__.__name__ - raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") - -class Masks: +class Masks(SimpleClass): """ A class for storing and manipulating detection masks. @@ -301,11 +266,6 @@ class Masks: numpy(): Returns a copy of the masks tensor as a numpy array. cuda(): Returns a copy of the masks tensor on GPU memory. to(): Returns a copy of the masks tensor with the specified device and dtype. - __len__(): Returns the number of masks in the tensor. - __str__(): Returns a string representation of the masks tensor. - __repr__(): Returns a detailed string representation of the masks tensor. - __getitem__(): Returns a new Masks object with the masks at the specified index. - __getattr__(): Raises an AttributeError with a list of valid attributes and properties. """ def __init__(self, masks, orig_shape) -> None: @@ -342,19 +302,5 @@ class Masks: def __len__(self): # override len(results) return len(self.masks) - def __str__(self): - return self.masks.__str__() - - def __repr__(self): - return (f'{self.__class__.__module__}.{self.__class__.__name__}\n' - f'type: {self.masks.__class__.__module__}.{self.masks.__class__.__name__}\n' - f'shape: {self.masks.shape}\n' - f'dtype: {self.masks.dtype}\n' - f'{self.masks.__repr__()}') - def __getitem__(self, idx): return Masks(self.masks[idx], self.orig_shape) - - def __getattr__(self, attr): - name = self.__class__.__name__ - raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") diff --git a/ultralytics/yolo/engine/trainer.py b/ultralytics/yolo/engine/trainer.py index 8bd250a..988faa8 100644 --- a/ultralytics/yolo/engine/trainer.py +++ b/ultralytics/yolo/engine/trainer.py @@ -174,7 +174,12 @@ class BaseTrainer: # Run subprocess if DDP training, else train normally if world_size > 1 and 'LOCAL_RANK' not in os.environ: - cmd, file = generate_ddp_command(world_size, self) # security vulnerability in Snyk scans + # Argument checks + if self.args.rect: + LOGGER.warning("WARNING ⚠️ 'rect=True' is incompatible with Multi-GPU training, setting rect=False") + self.args.rect = False + # Command + cmd, file = generate_ddp_command(world_size, self) try: LOGGER.info(f'Running DDP command {cmd}') subprocess.run(cmd, check=True) @@ -183,17 +188,15 @@ class BaseTrainer: finally: ddp_cleanup(self, str(file)) else: - self._do_train(RANK, world_size) + self._do_train(world_size) - def _setup_ddp(self, rank, world_size): - # os.environ['MASTER_ADDR'] = 'localhost' - # os.environ['MASTER_PORT'] = '9020' - torch.cuda.set_device(rank) - self.device = torch.device('cuda', rank) - LOGGER.info(f'DDP settings: RANK {rank}, WORLD_SIZE {world_size}, DEVICE {self.device}') - dist.init_process_group('nccl' if dist.is_nccl_available() else 'gloo', rank=rank, world_size=world_size) + def _setup_ddp(self, world_size): + torch.cuda.set_device(RANK) + self.device = torch.device('cuda', RANK) + LOGGER.info(f'DDP settings: RANK {RANK}, WORLD_SIZE {world_size}, DEVICE {self.device}') + dist.init_process_group('nccl' if dist.is_nccl_available() else 'gloo', rank=RANK, world_size=world_size) - def _setup_train(self, rank, world_size): + def _setup_train(self, world_size): """ Builds dataloaders and optimizer on correct rank process. """ @@ -213,7 +216,7 @@ class BaseTrainer: self.amp = bool(self.amp) # as boolean self.scaler = amp.GradScaler(enabled=self.amp) if world_size > 1: - self.model = DDP(self.model, device_ids=[rank]) + self.model = DDP(self.model, device_ids=[RANK]) # Check imgsz gs = max(int(self.model.stride.max() if hasattr(self.model, 'stride') else 32), 32) # grid size (max stride) self.args.imgsz = check_imgsz(self.args.imgsz, stride=gs, floor=gs, max_dim=1) @@ -243,8 +246,8 @@ class BaseTrainer: # dataloaders batch_size = self.batch_size // world_size if world_size > 1 else self.batch_size - self.train_loader = self.get_dataloader(self.trainset, batch_size=batch_size, rank=rank, mode='train') - if rank in (-1, 0): + self.train_loader = self.get_dataloader(self.trainset, batch_size=batch_size, rank=RANK, mode='train') + if RANK in (-1, 0): self.test_loader = self.get_dataloader(self.testset, batch_size=batch_size * 2, rank=-1, mode='val') self.validator = self.get_validator() metric_keys = self.validator.metrics.keys + self.label_loss_items(prefix='val') @@ -256,11 +259,11 @@ class BaseTrainer: self.scheduler.last_epoch = self.start_epoch - 1 # do not move self.run_callbacks('on_pretrain_routine_end') - def _do_train(self, rank=-1, world_size=1): + def _do_train(self, world_size=1): if world_size > 1: - self._setup_ddp(rank, world_size) + self._setup_ddp(world_size) - self._setup_train(rank, world_size) + self._setup_train(world_size) self.epoch_time = None self.epoch_time_start = time.time() @@ -280,7 +283,7 @@ class BaseTrainer: self.epoch = epoch self.run_callbacks('on_train_epoch_start') self.model.train() - if rank != -1: + if RANK != -1: self.train_loader.sampler.set_epoch(epoch) pbar = enumerate(self.train_loader) # Update dataloader attributes (optional) @@ -291,7 +294,7 @@ class BaseTrainer: if hasattr(self.train_loader.dataset, 'close_mosaic'): self.train_loader.dataset.close_mosaic(hyp=self.args) - if rank in (-1, 0): + if RANK in (-1, 0): LOGGER.info(self.progress_string()) pbar = tqdm(enumerate(self.train_loader), total=nb, bar_format=TQDM_BAR_FORMAT) self.tloss = None @@ -315,7 +318,7 @@ class BaseTrainer: batch = self.preprocess_batch(batch) preds = self.model(batch['img']) self.loss, self.loss_items = self.criterion(preds, batch) - if rank != -1: + if RANK != -1: self.loss *= world_size self.tloss = (self.tloss * i + self.loss_items) / (i + 1) if self.tloss is not None \ else self.loss_items @@ -332,7 +335,7 @@ class BaseTrainer: mem = f'{torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0:.3g}G' # (GB) loss_len = self.tloss.shape[0] if len(self.tloss.size()) else 1 losses = self.tloss if loss_len > 1 else torch.unsqueeze(self.tloss, 0) - if rank in (-1, 0): + if RANK in (-1, 0): pbar.set_description( ('%11s' * 2 + '%11.4g' * (2 + loss_len)) % (f'{epoch + 1}/{self.epochs}', mem, *losses, batch['cls'].shape[0], batch['img'].shape[-1])) @@ -347,7 +350,7 @@ class BaseTrainer: self.scheduler.step() self.run_callbacks('on_train_epoch_end') - if rank in (-1, 0): + if RANK in (-1, 0): # Validation self.ema.update_attr(self.model, include=['yaml', 'nc', 'args', 'names', 'stride', 'class_weights']) @@ -377,7 +380,7 @@ class BaseTrainer: if self.stop: break # must break all DDP ranks - if rank in (-1, 0): + if RANK in (-1, 0): # Do final val with best.pt LOGGER.info(f'\n{epoch - self.start_epoch + 1} epochs completed in ' f'{(time.time() - self.train_time_start) / 3600:.3f} hours.') @@ -408,7 +411,8 @@ class BaseTrainer: torch.save(ckpt, self.wdir / f'epoch{self.epoch}.pt') del ckpt - def get_dataset(self, data): + @staticmethod + def get_dataset(data): """ Get train, val path from data dict if it exists. Returns None if data format is not recognized. """ diff --git a/ultralytics/yolo/utils/__init__.py b/ultralytics/yolo/utils/__init__.py index 5b7cb6d..e4f9b88 100644 --- a/ultralytics/yolo/utils/__init__.py +++ b/ultralytics/yolo/utils/__init__.py @@ -22,11 +22,15 @@ import yaml from ultralytics import __version__ -# Constants +# PyTorch Multi-GPU DDP Constants +RANK = int(os.getenv('RANK', -1)) +LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html +WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1)) + +# Other Constants FILE = Path(__file__).resolve() ROOT = FILE.parents[2] # YOLO DEFAULT_CFG_PATH = ROOT / 'yolo/cfg/default.yaml' -RANK = int(os.getenv('RANK', -1)) NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLOv5 multiprocessing threads AUTOINSTALL = str(os.getenv('YOLO_AUTOINSTALL', True)).lower() == 'true' # global auto-install mode VERBOSE = str(os.getenv('YOLO_VERBOSE', True)).lower() == 'true' # global verbose mode @@ -92,25 +96,59 @@ HELP_MSG = \ """ # Settings -torch.set_printoptions(linewidth=320, precision=5, profile='long') +torch.set_printoptions(linewidth=320, precision=4, profile='default') np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5 cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with PyTorch DataLoader) os.environ['NUMEXPR_MAX_THREADS'] = str(NUM_THREADS) # NumExpr max threads os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8' # for deterministic training +class SimpleClass: + """ + Ultralytics SimpleClass is a base class providing helpful string representation, error reporting, and attribute + access methods for easier debugging and usage. + """ + + def __str__(self): + """Return a human-readable string representation of the object.""" + attr = [] + for a in dir(self): + v = getattr(self, a) + if not callable(v) and not a.startswith('__'): + if isinstance(v, SimpleClass): + # Display only the module and class name for subclasses + s = f'{a}: {v.__module__}.{v.__class__.__name__} object' + else: + s = f'{a}: {repr(v)}' + attr.append(s) + return f'{self.__module__}.{self.__class__.__name__} object with attributes:\n\n' + '\n'.join(attr) + + def __repr__(self): + """Return a machine-readable string representation of the object.""" + return self.__str__() + + def __getattr__(self, attr): + """Custom attribute access error message with helpful information.""" + name = self.__class__.__name__ + raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") + + class IterableSimpleNamespace(SimpleNamespace): """ - Iterable SimpleNamespace class to allow SimpleNamespace to be used with dict() and in for loops + Ultralytics IterableSimpleNamespace is an extension class of SimpleNamespace that adds iterable functionality and + enables usage with dict() and for loops. """ def __iter__(self): + """Return an iterator of key-value pairs from the namespace's attributes.""" return iter(vars(self).items()) def __str__(self): + """Return a human-readable string representation of the object.""" return '\n'.join(f'{k}={v}' for k, v in vars(self).items()) def __getattr__(self, attr): + """Custom attribute access error message with helpful information.""" name = self.__class__.__name__ raise AttributeError(f""" '{name}' object has no attribute '{attr}'. This may be caused by a modified or out of date ultralytics @@ -120,6 +158,7 @@ class IterableSimpleNamespace(SimpleNamespace): """) def get(self, key, default=None): + """Return the value of the specified key if it exists; otherwise, return the default value.""" return getattr(self, key, default) diff --git a/ultralytics/yolo/utils/callbacks/clearml.py b/ultralytics/yolo/utils/callbacks/clearml.py index 533da4a..b91e3fc 100644 --- a/ultralytics/yolo/utils/callbacks/clearml.py +++ b/ultralytics/yolo/utils/callbacks/clearml.py @@ -8,7 +8,7 @@ try: assert clearml.__version__ # verify package is not directory assert not TESTS_RUNNING # do not log pytest -except (ImportError, AssertionError): +except (ImportError, AssertionError, AttributeError): clearml = None diff --git a/ultralytics/yolo/utils/callbacks/comet.py b/ultralytics/yolo/utils/callbacks/comet.py index a6299ee..9f193d6 100644 --- a/ultralytics/yolo/utils/callbacks/comet.py +++ b/ultralytics/yolo/utils/callbacks/comet.py @@ -7,7 +7,7 @@ try: assert not TESTS_RUNNING # do not log pytest assert comet_ml.__version__ # verify package is not directory -except (ImportError, AssertionError): +except (ImportError, AssertionError, AttributeError): comet_ml = None diff --git a/ultralytics/yolo/utils/checks.py b/ultralytics/yolo/utils/checks.py index 7597087..cf54ebd 100644 --- a/ultralytics/yolo/utils/checks.py +++ b/ultralytics/yolo/utils/checks.py @@ -239,7 +239,7 @@ def check_suffix(file='yolov8n.pt', suffix='.pt', msg=''): if isinstance(suffix, str): suffix = (suffix, ) for f in file if isinstance(file, (list, tuple)) else [file]: - s = Path(f).suffix.lower() # file suffix + s = Path(f).suffix.lower().strip() # file suffix if len(s): assert s in suffix, f'{msg}{f} acceptable suffix is {suffix}, not {s}' @@ -261,7 +261,7 @@ def check_yolov5u_filename(file: str, verbose: bool = True): def check_file(file, suffix='', download=True, hard=True): # Search/download file (if necessary) and return path check_suffix(file, suffix) # optional - file = str(file) # convert to string + file = str(file).strip() # convert to string and strip spaces file = check_yolov5u_filename(file) # yolov5n -> yolov5nu if not file or ('://' not in file and Path(file).exists()): # exists ('://' check required in Windows Python<3.10) return file diff --git a/ultralytics/yolo/utils/metrics.py b/ultralytics/yolo/utils/metrics.py index d367fb9..26e2b0e 100644 --- a/ultralytics/yolo/utils/metrics.py +++ b/ultralytics/yolo/utils/metrics.py @@ -11,7 +11,7 @@ import numpy as np import torch import torch.nn as nn -from ultralytics.yolo.utils import LOGGER, TryExcept +from ultralytics.yolo.utils import LOGGER, SimpleClass, TryExcept # boxes @@ -425,7 +425,7 @@ def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir=Path(), na return tp, fp, p, r, f1, ap, unique_classes.astype(int) -class Metric: +class Metric(SimpleClass): """ Class for computing evaluation metrics for YOLOv8 model. @@ -461,10 +461,6 @@ class Metric: self.ap_class_index = [] # (nc, ) self.nc = 0 - def __getattr__(self, attr): - name = self.__class__.__name__ - raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") - @property def ap50(self): """AP@0.5 of all classes. @@ -550,7 +546,7 @@ class Metric: self.p, self.r, self.f1, self.all_ap, self.ap_class_index = results -class DetMetrics: +class DetMetrics(SimpleClass): """ This class is a utility class for computing detection metrics such as precision, recall, and mean average precision (mAP) of an object detection model. @@ -585,10 +581,6 @@ class DetMetrics: self.box = Metric() self.speed = {'preprocess': 0.0, 'inference': 0.0, 'loss': 0.0, 'postprocess': 0.0} - def __getattr__(self, attr): - name = self.__class__.__name__ - raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") - def process(self, tp, conf, pred_cls, target_cls): results = ap_per_class(tp, conf, pred_cls, target_cls, plot=self.plot, save_dir=self.save_dir, names=self.names)[2:] @@ -622,7 +614,7 @@ class DetMetrics: return dict(zip(self.keys + ['fitness'], self.mean_results() + [self.fitness])) -class SegmentMetrics: +class SegmentMetrics(SimpleClass): """ Calculates and aggregates detection and segmentation metrics over a given set of classes. @@ -657,10 +649,6 @@ class SegmentMetrics: self.seg = Metric() self.speed = {'preprocess': 0.0, 'inference': 0.0, 'loss': 0.0, 'postprocess': 0.0} - def __getattr__(self, attr): - name = self.__class__.__name__ - raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") - def process(self, tp_m, tp_b, conf, pred_cls, target_cls): """ Processes the detection and segmentation metrics over the given set of predictions. @@ -724,7 +712,7 @@ class SegmentMetrics: return dict(zip(self.keys + ['fitness'], self.mean_results() + [self.fitness])) -class ClassifyMetrics: +class ClassifyMetrics(SimpleClass): """ Class for computing classification metrics including top-1 and top-5 accuracy. @@ -747,10 +735,6 @@ class ClassifyMetrics: self.top5 = 0 self.speed = {'preprocess': 0.0, 'inference': 0.0, 'loss': 0.0, 'postprocess': 0.0} - def __getattr__(self, attr): - name = self.__class__.__name__ - raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}") - def process(self, targets, pred): # target classes and predicted classes pred, targets = torch.cat(pred), torch.cat(targets) diff --git a/ultralytics/yolo/utils/plotting.py b/ultralytics/yolo/utils/plotting.py index b764ffc..8418495 100644 --- a/ultralytics/yolo/utils/plotting.py +++ b/ultralytics/yolo/utils/plotting.py @@ -295,7 +295,7 @@ def plot_images(images, for j, box in enumerate(boxes.T.tolist()): c = classes[j] color = colors(c) - c = names[c] if names else c + c = names.get(c, c) if names else c if labels or conf[j] > 0.25: # 0.25 conf thresh label = f'{c}' if labels else f'{c} {conf[j]:.1f}' annotator.box_label(box, label, color=color) diff --git a/ultralytics/yolo/utils/torch_utils.py b/ultralytics/yolo/utils/torch_utils.py index 5b510c4..e07b893 100644 --- a/ultralytics/yolo/utils/torch_utils.py +++ b/ultralytics/yolo/utils/torch_utils.py @@ -8,6 +8,7 @@ import time from contextlib import contextmanager from copy import deepcopy from pathlib import Path +from typing import Union import numpy as np import thop @@ -15,15 +16,10 @@ import torch import torch.distributed as dist import torch.nn as nn import torch.nn.functional as F -from torch.nn.parallel import DistributedDataParallel as DDP -from ultralytics.yolo.utils import DEFAULT_CFG_DICT, DEFAULT_CFG_KEYS, LOGGER, __version__ +from ultralytics.yolo.utils import DEFAULT_CFG_DICT, DEFAULT_CFG_KEYS, LOGGER, RANK, __version__ from ultralytics.yolo.utils.checks import check_version -LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html -RANK = int(os.getenv('RANK', -1)) -WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1)) - TORCH_1_9 = check_version(torch.__version__, '1.9.0') TORCH_1_11 = check_version(torch.__version__, '1.11.0') TORCH_1_12 = check_version(torch.__version__, '1.12.0') @@ -49,17 +45,6 @@ def smart_inference_mode(): return decorate -def DDP_model(model): - # Model DDP creation with checks - assert not check_version(torch.__version__, '1.12.0', pinned=True), \ - 'torch==1.12.0 torchvision==0.13.0 DDP training is not supported due to a known issue. ' \ - 'Please upgrade or downgrade torch to use DDP. See https://github.com/ultralytics/yolov5/issues/8395' - if TORCH_1_11: - return DDP(model, device_ids=[LOCAL_RANK], output_device=LOCAL_RANK, static_graph=True) - else: - return DDP(model, device_ids=[LOCAL_RANK], output_device=LOCAL_RANK) - - def select_device(device='', batch=0, newline=False, verbose=True): # device = None or 'cpu' or 0 or '0' or '0,1,2,3' s = f'Ultralytics YOLOv{__version__} 🚀 Python-{platform.python_version()} torch-{torch.__version__} ' @@ -141,6 +126,7 @@ def fuse_conv_and_bn(conv, bn): def fuse_deconv_and_bn(deconv, bn): + # Fuse ConvTranspose2d() and BatchNorm2d() layers fuseddconv = nn.ConvTranspose2d(deconv.in_channels, deconv.out_channels, kernel_size=deconv.kernel_size, @@ -186,14 +172,17 @@ def model_info(model, detailed=False, verbose=True, imgsz=640): def get_num_params(model): + # Return the total number of parameters in a YOLO model return sum(x.numel() for x in model.parameters()) def get_num_gradients(model): + # Return the total number of parameters with gradients in a YOLO model return sum(x.numel() for x in model.parameters() if x.requires_grad) def get_flops(model, imgsz=640): + # Return a YOLO model's FLOPs try: model = de_parallel(model) p = next(model.parameters()) @@ -208,6 +197,7 @@ def get_flops(model, imgsz=640): def initialize_weights(model): + # Initialize model weights to random values for m in model.modules(): t = type(m) if t is nn.Conv2d: @@ -239,7 +229,7 @@ def make_divisible(x, divisor): def copy_attr(a, b, include=(), exclude=()): - # Copy attributes from b to a, options to only include [...] and to exclude [...] + # Copy attributes from 'b' to 'a', options to only include [...] and to exclude [...] for k, v in b.__dict__.items(): if (len(include) and k not in include) or k.startswith('_') or k in exclude: continue @@ -322,7 +312,7 @@ class ModelEMA: copy_attr(self.ema, model, include, exclude) -def strip_optimizer(f='best.pt', s=''): +def strip_optimizer(f: Union[str, Path] = 'best.pt', s: str = '') -> None: """ Strip optimizer from 'f' to finalize training, optionally save as 's'. diff --git a/ultralytics/yolo/v8/segment/train.py b/ultralytics/yolo/v8/segment/train.py index 5fc62b3..86d7433 100644 --- a/ultralytics/yolo/v8/segment/train.py +++ b/ultralytics/yolo/v8/segment/train.py @@ -126,11 +126,11 @@ class SegLoss(Loss): # WARNING: lines below prevents Multi-GPU DDP 'unused gradient' PyTorch errors, do not remove else: - loss[1] += proto.sum() * 0 + pred_masks.sum() * 0 + loss[1] += (proto * 0).sum() + (pred_masks * 0).sum() # inf sums may lead to nan loss # WARNING: lines below prevent Multi-GPU DDP 'unused gradient' PyTorch errors, do not remove else: - loss[1] += proto.sum() * 0 + pred_masks.sum() * 0 + loss[1] += (proto * 0).sum() + (pred_masks * 0).sum() # inf sums may lead to nan loss loss[0] *= self.hyp.box # box gain loss[1] *= self.hyp.box / batch_size # seg gain