mirror of
https://github.com/THU-MIG/yolov10.git
synced 2025-06-09 01:24:25 +08:00
Threadpool fixes and CLI improvements (#550)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ayush Chaurasia <ayush.chaurarsia@gmail.com>
This commit is contained in:
parent
d9a0fba251
commit
21b701c4ea
43
docs/cfg.md
43
docs/cfg.md
@ -2,40 +2,51 @@ YOLO settings and hyperparameters play a critical role in the model's performanc
|
|||||||
and hyperparameters can affect the model's behavior at various stages of the model development process, including
|
and hyperparameters can affect the model's behavior at various stages of the model development process, including
|
||||||
training, validation, and prediction.
|
training, validation, and prediction.
|
||||||
|
|
||||||
Properly setting and tuning these parameters can have a significant impact on the model's ability to learn effectively
|
YOLOv8 'yolo' CLI commands use the following syntax:
|
||||||
from the training data and generalize to new data. For example, choosing an appropriate learning rate, batch size, and
|
|
||||||
optimization algorithm can greatly affect the model's convergence speed and accuracy. Similarly, setting the correct
|
|
||||||
confidence threshold and non-maximum suppression (NMS) threshold can affect the model's performance on detection tasks.
|
|
||||||
|
|
||||||
It is important to carefully consider and experiment with these settings and hyperparameters to achieve the best
|
!!! example ""
|
||||||
possible performance for a given task. This can involve trial and error, as well as using techniques such as
|
|
||||||
hyperparameter optimization to search for the optimal set of parameters.
|
|
||||||
|
|
||||||
In summary, YOLO settings and hyperparameters are a key factor in the success of a YOLO model, and it is important to
|
=== "CLI"
|
||||||
pay careful attention to them to achieve the desired results.
|
|
||||||
|
|
||||||
### Setting the operation type
|
```bash
|
||||||
|
yolo TASK MODE ARGS
|
||||||
|
```
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
- `TASK` (optional) is one of `[detect, segment, classify]`. If it is not passed explicitly YOLOv8 will try to guess
|
||||||
|
the `TASK` from the model type.
|
||||||
|
- `MODE` (required) is one of `[train, val, predict, export]`
|
||||||
|
- `ARGS` (optional) are any number of custom `arg=value` pairs like `imgsz=320` that override defaults.
|
||||||
|
For a full list of available `ARGS` see the [Configuration](cfg.md) page and `defaults.yaml`
|
||||||
|
GitHub [source](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/yolo/cfg/default.yaml).
|
||||||
|
|
||||||
|
#### Tasks
|
||||||
|
|
||||||
YOLO models can be used for a variety of tasks, including detection, segmentation, and classification. These tasks
|
YOLO models can be used for a variety of tasks, including detection, segmentation, and classification. These tasks
|
||||||
differ in the type of output they produce and the specific problem they are designed to solve.
|
differ in the type of output they produce and the specific problem they are designed to solve.
|
||||||
|
|
||||||
- Detection: Detection tasks involve identifying and localizing objects or regions of interest in an image or video.
|
- **Detect**: Detection tasks involve identifying and localizing objects or regions of interest in an image or video.
|
||||||
YOLO models can be used for object detection tasks by predicting the bounding boxes and class labels of objects in an
|
YOLO models can be used for object detection tasks by predicting the bounding boxes and class labels of objects in an
|
||||||
image.
|
image.
|
||||||
- Segmentation: Segmentation tasks involve dividing an image or video into regions or pixels that correspond to
|
- **Segment**: Segmentation tasks involve dividing an image or video into regions or pixels that correspond to
|
||||||
different objects or classes. YOLO models can be used for image segmentation tasks by predicting a mask or label for
|
different objects or classes. YOLO models can be used for image segmentation tasks by predicting a mask or label for
|
||||||
each pixel in an image.
|
each pixel in an image.
|
||||||
- Classification: Classification tasks involve assigning a class label to an input, such as an image or text. YOLO
|
- **Classify**: Classification tasks involve assigning a class label to an input, such as an image or text. YOLO
|
||||||
models can be used for image classification tasks by predicting the class label of an input image.
|
models can be used for image classification tasks by predicting the class label of an input image.
|
||||||
|
|
||||||
|
#### Modes
|
||||||
|
|
||||||
YOLO models can be used in different modes depending on the specific problem you are trying to solve. These modes
|
YOLO models can be used in different modes depending on the specific problem you are trying to solve. These modes
|
||||||
include train, val, and predict.
|
include train, val, and predict.
|
||||||
|
|
||||||
- Train: The train mode is used to train the model on a dataset. This mode is typically used during the development and
|
- **Train**: The train mode is used to train the model on a dataset. This mode is typically used during the development
|
||||||
|
and
|
||||||
testing phase of a model.
|
testing phase of a model.
|
||||||
- Val: The val mode is used to evaluate the model's performance on a validation dataset. This mode is typically used to
|
- **Val**: The val mode is used to evaluate the model's performance on a validation dataset. This mode is typically used
|
||||||
|
to
|
||||||
tune the model's hyperparameters and detect overfitting.
|
tune the model's hyperparameters and detect overfitting.
|
||||||
- Predict: The predict mode is used to make predictions with the model on new data. This mode is typically used in
|
- **Predict**: The predict mode is used to make predictions with the model on new data. This mode is typically used in
|
||||||
production or when deploying the model to users.
|
production or when deploying the model to users.
|
||||||
|
|
||||||
| Key | Value | Description |
|
| Key | Value | Description |
|
||||||
|
@ -17,7 +17,8 @@ Where:
|
|||||||
the `TASK` from the model type.
|
the `TASK` from the model type.
|
||||||
- `MODE` (required) is one of `[train, val, predict, export]`
|
- `MODE` (required) is one of `[train, val, predict, export]`
|
||||||
- `ARGS` (optional) are any number of custom `arg=value` pairs like `imgsz=320` that override defaults.
|
- `ARGS` (optional) are any number of custom `arg=value` pairs like `imgsz=320` that override defaults.
|
||||||
For a full list of available `ARGS` see the [Configuration](cfg.md) page.
|
For a full list of available `ARGS` see the [Configuration](cfg.md) page and `defaults.yaml`
|
||||||
|
GitHub [source](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/yolo/cfg/default.yaml).
|
||||||
|
|
||||||
!!! note ""
|
!!! note ""
|
||||||
|
|
||||||
|
@ -1,74 +1,94 @@
|
|||||||
Inference or prediction of a task returns a list of `Results` objects. Alternatively, in the streaming mode, it returns a generator of `Results` objects which is memory efficient. Streaming mode can be enabled by passing `stream=True` in predictor's call method.
|
Inference or prediction of a task returns a list of `Results` objects. Alternatively, in the streaming mode, it returns
|
||||||
|
a generator of `Results` objects which is memory efficient. Streaming mode can be enabled by passing `stream=True` in
|
||||||
|
predictor's call method.
|
||||||
|
|
||||||
!!! example "Predict"
|
!!! example "Predict"
|
||||||
|
|
||||||
=== "Getting a List"
|
=== "Getting a List"
|
||||||
```python
|
|
||||||
inputs = [img, img] # list of np arrays
|
```python
|
||||||
results = model(inputs) # List of Results objects
|
inputs = [img, img] # list of np arrays
|
||||||
for result in results:
|
results = model(inputs) # List of Results objects
|
||||||
boxes = result.boxes # Boxes object for bbox outputs
|
|
||||||
masks = result.masks # Masks object for segmenation masks outputs
|
for result in results:
|
||||||
probs = result.probs # Class probabilities for classification outputs
|
boxes = result.boxes # Boxes object for bbox outputs
|
||||||
...
|
masks = result.masks # Masks object for segmenation masks outputs
|
||||||
```
|
probs = result.probs # Class probabilities for classification outputs
|
||||||
|
```
|
||||||
|
|
||||||
=== "Getting a Generator"
|
=== "Getting a Generator"
|
||||||
```python
|
|
||||||
inputs = [img, img] # list of np arrays
|
```python
|
||||||
results = model(inputs, stream=True) # Generator of Results objects
|
inputs = [img, img] # list of numpy arrays
|
||||||
for result in results:
|
results = model(inputs, stream=True) # generator of Results objects
|
||||||
boxes = result.boxes # Boxes object for bbox outputs
|
|
||||||
masks = result.masks # Masks object for segmenation masks outputs
|
for r in results:
|
||||||
probs = result.probs # Class probabilities for classification outputs
|
boxes = r.boxes # Boxes object for bbox outputs
|
||||||
...
|
masks = r.masks # Masks object for segmenation masks outputs
|
||||||
```
|
probs = r.probs # Class probabilities for classification outputs
|
||||||
|
```
|
||||||
|
|
||||||
## Working with Results
|
## Working with Results
|
||||||
|
|
||||||
Results object consists of these component objects:
|
Results object consists of these component objects:
|
||||||
|
|
||||||
- `results.boxes` : It is an object of class `Boxes`. It has properties and methods for manipulating bboxes
|
- `Results.boxes` : `Boxes` object with properties and methods for manipulating bboxes
|
||||||
- `results.masks` : It is an object of class `Masks`. It can be used to index masks or to get segment coordinates.
|
- `Results.masks` : `Masks` object used to index masks or to get segment coordinates.
|
||||||
- `results.prob` : It is a `Tensor` object. It contains the class probabilities/logits.
|
- `Results.prob` : `torch.Tensor` containing the class probabilities/logits.
|
||||||
|
|
||||||
Each result is composed of torch.Tensor by default, in which you can easily use following functionality:
|
Each result is composed of torch.Tensor by default, in which you can easily use following functionality:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
results = results.cuda()
|
results = results.cuda()
|
||||||
results = results.cpu()
|
results = results.cpu()
|
||||||
results = results.to("cpu")
|
results = results.to("cpu")
|
||||||
results = results.numpy()
|
results = results.numpy()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Boxes
|
### Boxes
|
||||||
`Boxes` object can be used index, manipulate and convert bboxes to different formats. The box format conversion operations are cached, which means they're only calculated once per object and those values are reused for future calls.
|
|
||||||
|
`Boxes` object can be used index, manipulate and convert bboxes to different formats. The box format conversion
|
||||||
|
operations are cached, which means they're only calculated once per object and those values are reused for future calls.
|
||||||
|
|
||||||
- Indexing a `Boxes` objects returns a `Boxes` object
|
- Indexing a `Boxes` objects returns a `Boxes` object
|
||||||
|
|
||||||
```python
|
```python
|
||||||
boxes = results.boxes
|
results = model(inputs)
|
||||||
box = boxes[0] # returns one box
|
boxes = results[0].boxes
|
||||||
|
box = boxes[0] # returns one box
|
||||||
box.xyxy
|
box.xyxy
|
||||||
```
|
```
|
||||||
|
|
||||||
- Properties and conversions
|
- Properties and conversions
|
||||||
```
|
|
||||||
boxes.xyxy # box with xyxy format, (N, 4)
|
```python
|
||||||
boxes.xywh # box with xywh format, (N, 4)
|
boxes.xyxy # box with xyxy format, (N, 4)
|
||||||
|
boxes.xywh # box with xywh format, (N, 4)
|
||||||
boxes.xyxyn # box with xyxy format but normalized, (N, 4)
|
boxes.xyxyn # box with xyxy format but normalized, (N, 4)
|
||||||
boxes.xywhn # box with xywh format but normalized, (N, 4)
|
boxes.xywhn # box with xywh format but normalized, (N, 4)
|
||||||
boxes.conf # confidence score, (N, 1)
|
boxes.conf # confidence score, (N, 1)
|
||||||
boxes.cls # cls, (N, 1)
|
boxes.cls # cls, (N, 1)
|
||||||
boxes.data # raw bboxes tensor, (N, 6) or boxes.boxes .
|
boxes.data # raw bboxes tensor, (N, 6) or boxes.boxes .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Masks
|
### Masks
|
||||||
|
|
||||||
`Masks` object can be used index, manipulate and convert masks to segments. The segment conversion operation is cached.
|
`Masks` object can be used index, manipulate and convert masks to segments. The segment conversion operation is cached.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
masks = results.masks # Masks object
|
results = model(inputs)
|
||||||
|
masks = results[0].masks # Masks object
|
||||||
masks.segments # bounding coordinates of masks, List[segment] * N
|
masks.segments # bounding coordinates of masks, List[segment] * N
|
||||||
masks.data # raw masks tensor, (N, H, W) or masks.masks
|
masks.data # raw masks tensor, (N, H, W) or masks.masks
|
||||||
```
|
```
|
||||||
|
|
||||||
### probs
|
### probs
|
||||||
|
|
||||||
`probs` attribute of `Results` class is a `Tensor` containing class probabilities of a classification operation.
|
`probs` attribute of `Results` class is a `Tensor` containing class probabilities of a classification operation.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
results.probs # cls prob, (num_class, )
|
results = model(inputs)
|
||||||
|
results[0].probs # cls prob, (num_class, )
|
||||||
```
|
```
|
||||||
|
|
||||||
Class reference documentation for `Results` module and its components can be found [here](reference/results.md)
|
Class reference documentation for `Results` module and its components can be found [here](reference/results.md)
|
||||||
|
@ -90,8 +90,8 @@ nav:
|
|||||||
- Ultralytics HUB: hub.md
|
- Ultralytics HUB: hub.md
|
||||||
- iOS and Android App: app.md
|
- iOS and Android App: app.md
|
||||||
- Reference:
|
- Reference:
|
||||||
- Python Model interface: reference/model.md
|
|
||||||
- Engine:
|
- Engine:
|
||||||
|
- Model: reference/model.md
|
||||||
- Trainer: reference/base_trainer.md
|
- Trainer: reference/base_trainer.md
|
||||||
- Validator: reference/base_val.md
|
- Validator: reference/base_val.md
|
||||||
- Predictor: reference/base_pred.md
|
- Predictor: reference/base_pred.md
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Ultralytics YOLO 🚀, GPL-3.0 license
|
# Ultralytics YOLO 🚀, GPL-3.0 license
|
||||||
|
import signal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
@ -13,22 +13,6 @@ AGENT_NAME = f'python-{__version__}-colab' if is_colab() else f'python-{__versio
|
|||||||
|
|
||||||
session = None
|
session = None
|
||||||
|
|
||||||
# Causing problems in tests (non-authenticated)
|
|
||||||
# import signal
|
|
||||||
# import sys
|
|
||||||
# def signal_handler(signum, frame):
|
|
||||||
# """ Confirm exit """
|
|
||||||
# global hub_logger
|
|
||||||
# LOGGER.info(f'Signal received. {signum} {frame}')
|
|
||||||
# if isinstance(session, HubTrainingSession):
|
|
||||||
# hub_logger.alive = False
|
|
||||||
# del hub_logger
|
|
||||||
# sys.exit(signum)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# signal.signal(signal.SIGTERM, signal_handler)
|
|
||||||
# signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
|
|
||||||
|
|
||||||
class HubTrainingSession:
|
class HubTrainingSession:
|
||||||
|
|
||||||
@ -43,10 +27,11 @@ class HubTrainingSession:
|
|||||||
self.alive = True # for heartbeats
|
self.alive = True # for heartbeats
|
||||||
self.model = self._get_model()
|
self.model = self._get_model()
|
||||||
self._heartbeats() # start heartbeats
|
self._heartbeats() # start heartbeats
|
||||||
|
signal.signal(signal.SIGTERM, self.shutdown) # register the shutdown function to be called on exit
|
||||||
|
signal.signal(signal.SIGINT, self.shutdown)
|
||||||
|
|
||||||
def __del__(self):
|
def shutdown(self, *args): # noqa
|
||||||
# Class destructor
|
self.alive = False # stop heartbeats
|
||||||
self.alive = False
|
|
||||||
|
|
||||||
def upload_metrics(self):
|
def upload_metrics(self):
|
||||||
payload = {"metrics": self.metrics_queue.copy(), "type": "metrics"}
|
payload = {"metrics": self.metrics_queue.copy(), "type": "metrics"}
|
||||||
@ -100,13 +85,6 @@ class HubTrainingSession:
|
|||||||
if not check_dataset_disk_space(self.model['data']):
|
if not check_dataset_disk_space(self.model['data']):
|
||||||
raise MemoryError("Not enough disk space")
|
raise MemoryError("Not enough disk space")
|
||||||
|
|
||||||
# COMMENT: Should not be needed as HUB is now considered an integration and is in integrations_callbacks
|
|
||||||
# import ultralytics.yolo.utils.callbacks.hub as hub_callbacks
|
|
||||||
# @staticmethod
|
|
||||||
# def register_callbacks(trainer):
|
|
||||||
# for k, v in hub_callbacks.callbacks.items():
|
|
||||||
# trainer.add_callback(k, v)
|
|
||||||
|
|
||||||
@threaded
|
@threaded
|
||||||
def _heartbeats(self):
|
def _heartbeats(self):
|
||||||
while self.alive:
|
while self.alive:
|
||||||
|
@ -4,6 +4,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from random import random
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ HELP_MSG = 'If this issue persists please visit https://github.com/ultralytics/h
|
|||||||
HUB_API_ROOT = os.environ.get("ULTRALYTICS_HUB_API", "https://api.ultralytics.com")
|
HUB_API_ROOT = os.environ.get("ULTRALYTICS_HUB_API", "https://api.ultralytics.com")
|
||||||
|
|
||||||
|
|
||||||
def check_dataset_disk_space(url='https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip', sf=2.0):
|
def check_dataset_disk_space(url='https://ultralytics.com/assets/coco128.zip', sf=2.0):
|
||||||
# Check that url fits on disk with safety factor sf, i.e. require 2GB free if url size is 1GB with sf=2.0
|
# Check that url fits on disk with safety factor sf, i.e. require 2GB free if url size is 1GB with sf=2.0
|
||||||
gib = 1 << 30 # bytes per GiB
|
gib = 1 << 30 # bytes per GiB
|
||||||
data = int(requests.head(url).headers['Content-Length']) / gib # dataset size (GB)
|
data = int(requests.head(url).headers['Content-Length']) / gib # dataset size (GB)
|
||||||
@ -130,18 +131,18 @@ def smart_request(*args, retry=3, timeout=30, thread=True, code=-1, method="post
|
|||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@TryExcept()
|
@TryExcept(verbose=False)
|
||||||
def sync_analytics(cfg, all_keys=False, enabled=False):
|
def traces(cfg, all_keys=False, traces_sample_rate=0.0):
|
||||||
"""
|
"""
|
||||||
Sync analytics data if enabled in the global settings
|
Sync traces data if enabled in the global settings
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cfg (UltralyticsCFG): Configuration for the task and mode.
|
cfg (IterableSimpleNamespace): Configuration for the task and mode.
|
||||||
all_keys (bool): Sync all items, not just non-default values.
|
all_keys (bool): Sync all items, not just non-default values.
|
||||||
enabled (bool): For debugging.
|
traces_sample_rate (float): Fraction of traces captured from 0.0 to 1.0
|
||||||
"""
|
"""
|
||||||
if SETTINGS['sync'] and RANK in {-1, 0} and enabled:
|
if SETTINGS['sync'] and RANK in {-1, 0} and (random() < traces_sample_rate):
|
||||||
cfg = dict(cfg) # convert type from UltralyticsCFG to dict
|
cfg = vars(cfg) # convert type from IterableSimpleNamespace to dict
|
||||||
if not all_keys:
|
if not all_keys:
|
||||||
cfg = {k: v for k, v in cfg.items() if v != DEFAULT_CFG_DICT.get(k, None)} # retain non-default values
|
cfg = {k: v for k, v in cfg.items() if v != DEFAULT_CFG_DICT.get(k, None)} # retain non-default values
|
||||||
cfg['uuid'] = SETTINGS['uuid'] # add the device UUID to the configuration data
|
cfg['uuid'] = SETTINGS['uuid'] # add the device UUID to the configuration data
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Ultralytics YOLO 🚀, GPL-3.0 license
|
# Ultralytics YOLO 🚀, GPL-3.0 license
|
||||||
import argparse
|
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
@ -9,46 +8,39 @@ from types import SimpleNamespace
|
|||||||
from typing import Dict, Union
|
from typing import Dict, Union
|
||||||
|
|
||||||
from ultralytics import __version__, yolo
|
from ultralytics import __version__, yolo
|
||||||
from ultralytics.yolo.utils import DEFAULT_CFG_PATH, LOGGER, PREFIX, checks, colorstr, print_settings, yaml_load
|
from ultralytics.yolo.utils import (DEFAULT_CFG_DICT, DEFAULT_CFG_PATH, LOGGER, PREFIX, USER_CONFIG_DIR,
|
||||||
|
IterableSimpleNamespace, checks, colorstr, yaml_load, yaml_print)
|
||||||
DIR = Path(__file__).parent
|
|
||||||
|
|
||||||
CLI_HELP_MSG = \
|
CLI_HELP_MSG = \
|
||||||
"""
|
"""
|
||||||
YOLOv8 CLI Usage examples:
|
YOLOv8 'yolo' CLI commands use the following syntax:
|
||||||
|
|
||||||
1. Install the ultralytics package:
|
yolo TASK MODE ARGS
|
||||||
|
|
||||||
pip install ultralytics
|
Where TASK (optional) is one of [detect, segment, classify]
|
||||||
|
MODE (required) is one of [train, val, predict, export]
|
||||||
|
ARGS (optional) are any number of custom 'arg=value' pairs like 'imgsz=320' that override defaults.
|
||||||
|
See all ARGS at https://docs.ultralytics.com/cfg or with 'yolo cfg'
|
||||||
|
|
||||||
2. Train, Val, Predict and Export using 'yolo' commands:
|
1. Train a detection model for 10 epochs with an initial learning_rate of 0.01
|
||||||
|
yolo detect train data=coco128.yaml model=yolov8n.pt epochs=10 lr0=0.01
|
||||||
|
|
||||||
yolo TASK MODE ARGS
|
2. Predict a YouTube video using a pretrained segmentation model at image size 320:
|
||||||
|
yolo segment predict model=yolov8n-seg.pt source=https://youtu.be/Zgi9g1ksQHc imgsz=320
|
||||||
|
|
||||||
Where TASK (optional) is one of [detect, segment, classify]
|
3. Val a pretrained detection model at batch-size 1 and image size 640:
|
||||||
MODE (required) is one of [train, val, predict, export]
|
yolo detect val model=yolov8n.pt data=coco128.yaml batch=1 imgsz=640
|
||||||
ARGS (optional) are any number of custom 'arg=value' pairs like 'imgsz=320' that override defaults.
|
|
||||||
For a full list of available ARGS see https://docs.ultralytics.com/cfg.
|
|
||||||
|
|
||||||
Train a detection model for 10 epochs with an initial learning_rate of 0.01
|
4. Export a YOLOv8n classification model to ONNX format at image size 224 by 128 (no TASK required)
|
||||||
yolo detect train data=coco128.yaml model=yolov8n.pt epochs=10 lr0=0.01
|
yolo export model=yolov8n-cls.pt format=onnx imgsz=224,128
|
||||||
|
|
||||||
Predict a YouTube video using a pretrained segmentation model at image size 320:
|
|
||||||
yolo segment predict model=yolov8n-seg.pt source=https://youtu.be/Zgi9g1ksQHc imgsz=320
|
|
||||||
|
|
||||||
Validate a pretrained detection model at batch-size 1 and image size 640:
|
|
||||||
yolo detect val model=yolov8n.pt data=coco128.yaml batch=1 imgsz=640
|
|
||||||
|
|
||||||
Export a YOLOv8n classification model to ONNX format at image size 224 by 128 (no TASK required)
|
|
||||||
yolo export model=yolov8n-cls.pt format=onnx imgsz=224,128
|
|
||||||
|
|
||||||
3. Run special commands:
|
|
||||||
|
|
||||||
|
5. Run special commands:
|
||||||
yolo help
|
yolo help
|
||||||
yolo checks
|
yolo checks
|
||||||
yolo version
|
yolo version
|
||||||
yolo settings
|
yolo settings
|
||||||
yolo copy-cfg
|
yolo copy-cfg
|
||||||
|
yolo cfg
|
||||||
|
|
||||||
Docs: https://docs.ultralytics.com/cli
|
Docs: https://docs.ultralytics.com/cli
|
||||||
Community: https://community.ultralytics.com
|
Community: https://community.ultralytics.com
|
||||||
@ -56,15 +48,6 @@ CLI_HELP_MSG = \
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class UltralyticsCFG(SimpleNamespace):
|
|
||||||
"""
|
|
||||||
UltralyticsCFG iterable SimpleNamespace class to allow SimpleNamespace to be used with dict() and in for loops
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(vars(self).items())
|
|
||||||
|
|
||||||
|
|
||||||
def cfg2dict(cfg):
|
def cfg2dict(cfg):
|
||||||
"""
|
"""
|
||||||
Convert a configuration object to a dictionary.
|
Convert a configuration object to a dictionary.
|
||||||
@ -104,7 +87,7 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace], overrides: Dict = None
|
|||||||
cfg = {**cfg, **overrides} # merge cfg and overrides dicts (prefer overrides)
|
cfg = {**cfg, **overrides} # merge cfg and overrides dicts (prefer overrides)
|
||||||
|
|
||||||
# Return instance
|
# Return instance
|
||||||
return UltralyticsCFG(**cfg)
|
return IterableSimpleNamespace(**cfg)
|
||||||
|
|
||||||
|
|
||||||
def check_cfg_mismatch(base: Dict, custom: Dict):
|
def check_cfg_mismatch(base: Dict, custom: Dict):
|
||||||
@ -118,12 +101,19 @@ def check_cfg_mismatch(base: Dict, custom: Dict):
|
|||||||
"""
|
"""
|
||||||
base, custom = (set(x.keys()) for x in (base, custom))
|
base, custom = (set(x.keys()) for x in (base, custom))
|
||||||
mismatched = [x for x in custom if x not in base]
|
mismatched = [x for x in custom if x not in base]
|
||||||
for option in mismatched:
|
|
||||||
LOGGER.info(f"{colorstr(option)} is not a valid key. Similar keys: {get_close_matches(option, base, 3, 0.6)}")
|
|
||||||
if mismatched:
|
if mismatched:
|
||||||
|
for x in mismatched:
|
||||||
|
matches = get_close_matches(x, base, 3, 0.6)
|
||||||
|
match_str = f"Similar arguments are {matches}." if matches else 'There are no similar arguments.'
|
||||||
|
LOGGER.warning(f"'{colorstr('red', 'bold', x)}' is not a valid YOLO argument. {match_str}")
|
||||||
|
LOGGER.warning(CLI_HELP_MSG)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def argument_error(arg):
|
||||||
|
return SyntaxError(f"'{arg}' is not a valid YOLO argument.\n{CLI_HELP_MSG}")
|
||||||
|
|
||||||
|
|
||||||
def entrypoint(debug=False):
|
def entrypoint(debug=False):
|
||||||
"""
|
"""
|
||||||
This function is the ultralytics package entrypoint, it's responsible for parsing the command line arguments passed
|
This function is the ultralytics package entrypoint, it's responsible for parsing the command line arguments passed
|
||||||
@ -139,67 +129,61 @@ def entrypoint(debug=False):
|
|||||||
It uses the package's default cfg and initializes it using the passed overrides.
|
It uses the package's default cfg and initializes it using the passed overrides.
|
||||||
Then it calls the CLI function with the composed cfg
|
Then it calls the CLI function with the composed cfg
|
||||||
"""
|
"""
|
||||||
if debug:
|
args = ['train', 'predict', 'model=yolov8n.pt'] if debug else sys.argv[1:]
|
||||||
args = ['train', 'predict', 'model=yolov8n.pt'] # for testing
|
if not args: # no arguments passed
|
||||||
else:
|
LOGGER.info(CLI_HELP_MSG)
|
||||||
if len(sys.argv) == 1: # no arguments passed
|
return
|
||||||
LOGGER.info(CLI_HELP_MSG)
|
|
||||||
return
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='YOLO parser')
|
|
||||||
parser.add_argument('args', type=str, nargs='+', help='YOLO args')
|
|
||||||
args = parser.parse_args().args
|
|
||||||
args = re.sub(r'\s*=\s*', '=', ' '.join(args)).split(' ') # remove whitespaces around = sign
|
|
||||||
|
|
||||||
tasks = 'detect', 'segment', 'classify'
|
tasks = 'detect', 'segment', 'classify'
|
||||||
modes = 'train', 'val', 'predict', 'export'
|
modes = 'train', 'val', 'predict', 'export'
|
||||||
special_modes = {
|
special = {
|
||||||
'help': lambda: LOGGER.info(CLI_HELP_MSG),
|
'help': lambda: LOGGER.info(CLI_HELP_MSG),
|
||||||
'checks': checks.check_yolo,
|
'checks': checks.check_yolo,
|
||||||
'version': lambda: LOGGER.info(__version__),
|
'version': lambda: LOGGER.info(__version__),
|
||||||
'settings': print_settings,
|
'settings': lambda: yaml_print(USER_CONFIG_DIR / 'settings.yaml'),
|
||||||
|
'cfg': lambda: yaml_print(DEFAULT_CFG_PATH),
|
||||||
'copy-cfg': copy_default_config}
|
'copy-cfg': copy_default_config}
|
||||||
|
|
||||||
overrides = {} # basic overrides, i.e. imgsz=320
|
overrides = {} # basic overrides, i.e. imgsz=320
|
||||||
defaults = yaml_load(DEFAULT_CFG_PATH)
|
|
||||||
for a in args:
|
for a in args:
|
||||||
if '=' in a:
|
if '=' in a:
|
||||||
if a.startswith('cfg='): # custom.yaml passed
|
try:
|
||||||
custom_config = Path(a.split('=')[-1])
|
re.sub(r' *= *', '=', a) # remove spaces around equals sign
|
||||||
LOGGER.info(f"{PREFIX}Overriding {DEFAULT_CFG_PATH} with {custom_config}")
|
|
||||||
overrides = {k: v for k, v in yaml_load(custom_config).items() if k not in {'cfg'}}
|
|
||||||
else:
|
|
||||||
k, v = a.split('=')
|
k, v = a.split('=')
|
||||||
try:
|
if k == 'cfg': # custom.yaml passed
|
||||||
if k == 'device': # special DDP handling, i.e. device='0,1,2,3'
|
LOGGER.info(f"{PREFIX}Overriding {DEFAULT_CFG_PATH} with {v}")
|
||||||
v = v.replace('[', '').replace(']', '') # handle device=[0,1,2,3]
|
overrides = {k: val for k, val in yaml_load(v).items() if k != 'cfg'}
|
||||||
v = v.replace(" ", "") # handle device=[0, 1, 2, 3]
|
else:
|
||||||
v = v.replace('\\', '') # handle device=\'0,1,2,3\'
|
if v.isnumeric():
|
||||||
overrides[k] = v
|
v = eval(v)
|
||||||
else:
|
elif v.lower() == 'none':
|
||||||
overrides[k] = eval(v) # convert strings to integers, floats, bools, etc.
|
v = None
|
||||||
except (NameError, SyntaxError):
|
elif v.lower() == 'true':
|
||||||
|
v = True
|
||||||
|
elif v.lower() == 'false':
|
||||||
|
v = False
|
||||||
|
elif ',' in v:
|
||||||
|
v = eval(v)
|
||||||
overrides[k] = v
|
overrides[k] = v
|
||||||
|
except (NameError, SyntaxError, ValueError) as e:
|
||||||
|
raise argument_error(a) from e
|
||||||
|
|
||||||
elif a in tasks:
|
elif a in tasks:
|
||||||
overrides['task'] = a
|
overrides['task'] = a
|
||||||
elif a in modes:
|
elif a in modes:
|
||||||
overrides['mode'] = a
|
overrides['mode'] = a
|
||||||
elif a in special_modes:
|
elif a in special:
|
||||||
special_modes[a]()
|
special[a]()
|
||||||
return
|
return
|
||||||
elif a in defaults and defaults[a] is False:
|
elif a in DEFAULT_CFG_DICT and DEFAULT_CFG_DICT[a] is False:
|
||||||
overrides[a] = True # auto-True for default False args, i.e. 'yolo show' sets show=True
|
overrides[a] = True # auto-True for default False args, i.e. 'yolo show' sets show=True
|
||||||
elif a in defaults:
|
elif a in DEFAULT_CFG_DICT:
|
||||||
raise SyntaxError(f"'{a}' is a valid YOLO argument but is missing an '=' sign to set its value, "
|
raise SyntaxError(f"'{colorstr('red', 'bold', a)}' is a valid YOLO argument but is missing an '=' sign "
|
||||||
f"i.e. try '{a}={defaults[a]}'"
|
f"to set its value, i.e. try '{a}={DEFAULT_CFG_DICT[a]}'\n{CLI_HELP_MSG}")
|
||||||
f"\n{CLI_HELP_MSG}")
|
|
||||||
else:
|
else:
|
||||||
raise SyntaxError(
|
raise argument_error(a)
|
||||||
f"'{a}' is not a valid YOLO argument. For a full list of valid arguments see "
|
|
||||||
f"https://github.com/ultralytics/ultralytics/blob/main/ultralytics/yolo/configs/default.yaml"
|
|
||||||
f"\n{CLI_HELP_MSG}")
|
|
||||||
|
|
||||||
cfg = get_cfg(defaults, overrides) # create CFG instance
|
cfg = get_cfg(DEFAULT_CFG_DICT, overrides) # create CFG instance
|
||||||
|
|
||||||
# Mapping from task to module
|
# Mapping from task to module
|
||||||
module = {"detect": yolo.v8.detect, "segment": yolo.v8.segment, "classify": yolo.v8.classify}.get(cfg.task)
|
module = {"detect": yolo.v8.detect, "segment": yolo.v8.segment, "classify": yolo.v8.classify}.get(cfg.task)
|
||||||
@ -223,8 +207,8 @@ def copy_default_config():
|
|||||||
new_file = Path.cwd() / DEFAULT_CFG_PATH.name.replace('.yaml', '_copy.yaml')
|
new_file = Path.cwd() / DEFAULT_CFG_PATH.name.replace('.yaml', '_copy.yaml')
|
||||||
shutil.copy2(DEFAULT_CFG_PATH, new_file)
|
shutil.copy2(DEFAULT_CFG_PATH, new_file)
|
||||||
LOGGER.info(f"{PREFIX}{DEFAULT_CFG_PATH} copied to {new_file}\n"
|
LOGGER.info(f"{PREFIX}{DEFAULT_CFG_PATH} copied to {new_file}\n"
|
||||||
f"Usage for running YOLO with this new custom cfg:\nyolo cfg={new_file} args...")
|
f"Example YOLO command with this new custom cfg:\n yolo cfg='{new_file}' imgsz=320 batch=8")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
entrypoint()
|
entrypoint(debug=True)
|
||||||
|
@ -93,7 +93,7 @@ class BaseDataset(Dataset):
|
|||||||
# self.img_files = sorted([x for x in f if x.suffix[1:].lower() in IMG_FORMATS]) # pathlib
|
# self.img_files = sorted([x for x in f if x.suffix[1:].lower() in IMG_FORMATS]) # pathlib
|
||||||
assert im_files, f"{self.prefix}No images found"
|
assert im_files, f"{self.prefix}No images found"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise FileNotFoundError(f"{self.prefix}Error loading data from {img_path}: {e}\n{HELP_URL}") from e
|
raise FileNotFoundError(f"{self.prefix}Error loading data from {img_path}\n{HELP_URL}") from e
|
||||||
return im_files
|
return im_files
|
||||||
|
|
||||||
def update_labels(self, include_class: Optional[list]):
|
def update_labels(self, include_class: Optional[list]):
|
||||||
@ -134,16 +134,17 @@ class BaseDataset(Dataset):
|
|||||||
gb = 0 # Gigabytes of cached images
|
gb = 0 # Gigabytes of cached images
|
||||||
self.im_hw0, self.im_hw = [None] * self.ni, [None] * self.ni
|
self.im_hw0, self.im_hw = [None] * self.ni, [None] * self.ni
|
||||||
fcn = self.cache_images_to_disk if cache == "disk" else self.load_image
|
fcn = self.cache_images_to_disk if cache == "disk" else self.load_image
|
||||||
results = ThreadPool(NUM_THREADS).imap(fcn, range(self.ni))
|
with ThreadPool(NUM_THREADS) as pool:
|
||||||
pbar = tqdm(enumerate(results), total=self.ni, bar_format=TQDM_BAR_FORMAT, disable=LOCAL_RANK > 0)
|
results = pool.imap(fcn, range(self.ni))
|
||||||
for i, x in pbar:
|
pbar = tqdm(enumerate(results), total=self.ni, bar_format=TQDM_BAR_FORMAT, disable=LOCAL_RANK > 0)
|
||||||
if cache == "disk":
|
for i, x in pbar:
|
||||||
gb += self.npy_files[i].stat().st_size
|
if cache == "disk":
|
||||||
else: # 'ram'
|
gb += self.npy_files[i].stat().st_size
|
||||||
self.ims[i], self.im_hw0[i], self.im_hw[i] = x # im, hw_orig, hw_resized = load_image(self, i)
|
else: # 'ram'
|
||||||
gb += self.ims[i].nbytes
|
self.ims[i], self.im_hw0[i], self.im_hw[i] = x # im, hw_orig, hw_resized = load_image(self, i)
|
||||||
pbar.desc = f"{self.prefix}Caching images ({gb / 1E9:.1f}GB {cache})"
|
gb += self.ims[i].nbytes
|
||||||
pbar.close()
|
pbar.desc = f"{self.prefix}Caching images ({gb / 1E9:.1f}GB {cache})"
|
||||||
|
pbar.close()
|
||||||
|
|
||||||
def cache_images_to_disk(self, i):
|
def cache_images_to_disk(self, i):
|
||||||
# Saves an image as an *.npy file for faster loading
|
# Saves an image as an *.npy file for faster loading
|
||||||
|
@ -13,7 +13,7 @@ import random
|
|||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from multiprocessing.pool import Pool, ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
@ -580,7 +580,7 @@ class LoadImagesAndLabels(Dataset):
|
|||||||
b, gb = 0, 1 << 30 # bytes of cached images, bytes per gigabytes
|
b, gb = 0, 1 << 30 # bytes of cached images, bytes per gigabytes
|
||||||
self.im_hw0, self.im_hw = [None] * n, [None] * n
|
self.im_hw0, self.im_hw = [None] * n, [None] * n
|
||||||
fcn = self.cache_images_to_disk if cache_images == 'disk' else self.load_image
|
fcn = self.cache_images_to_disk if cache_images == 'disk' else self.load_image
|
||||||
with (Pool if n > 10000 else ThreadPool)(NUM_THREADS) as pool:
|
with ThreadPool(NUM_THREADS) as pool:
|
||||||
results = pool.imap(fcn, range(n))
|
results = pool.imap(fcn, range(n))
|
||||||
pbar = tqdm(enumerate(results), total=n, bar_format=TQDM_BAR_FORMAT, disable=LOCAL_RANK > 0)
|
pbar = tqdm(enumerate(results), total=n, bar_format=TQDM_BAR_FORMAT, disable=LOCAL_RANK > 0)
|
||||||
for i, x in pbar:
|
for i, x in pbar:
|
||||||
@ -1150,7 +1150,7 @@ class HUBDatasetStats():
|
|||||||
dataset = LoadImagesAndLabels(self.data[split]) # load dataset
|
dataset = LoadImagesAndLabels(self.data[split]) # load dataset
|
||||||
desc = f'{split} images'
|
desc = f'{split} images'
|
||||||
total = dataset.n
|
total = dataset.n
|
||||||
with (Pool if total > 10000 else ThreadPool)(NUM_THREADS) as pool:
|
with ThreadPool(NUM_THREADS) as pool:
|
||||||
for _ in tqdm(pool.imap(self._hub_ops, dataset.im_files), total=total, desc=desc):
|
for _ in tqdm(pool.imap(self._hub_ops, dataset.im_files), total=total, desc=desc):
|
||||||
pass
|
pass
|
||||||
print(f'Done. All images saved to {self.im_dir}')
|
print(f'Done. All images saved to {self.im_dir}')
|
||||||
|
@ -185,9 +185,9 @@ def polygons2masks_overlap(imgsz, segments, downsample_ratio=1):
|
|||||||
return masks, index
|
return masks, index
|
||||||
|
|
||||||
|
|
||||||
def check_dataset_yaml(data, autodownload=True):
|
def check_dataset_yaml(dataset, autodownload=True):
|
||||||
# Download, check and/or unzip dataset if not found locally
|
# Download, check and/or unzip dataset if not found locally
|
||||||
data = check_file(data)
|
data = check_file(dataset)
|
||||||
|
|
||||||
# Download (optional)
|
# Download (optional)
|
||||||
extract_dir = ''
|
extract_dir = ''
|
||||||
@ -227,9 +227,11 @@ def check_dataset_yaml(data, autodownload=True):
|
|||||||
if val:
|
if val:
|
||||||
val = [Path(x).resolve() for x in (val if isinstance(val, list) else [val])] # val path
|
val = [Path(x).resolve() for x in (val if isinstance(val, list) else [val])] # val path
|
||||||
if not all(x.exists() for x in val):
|
if not all(x.exists() for x in val):
|
||||||
LOGGER.info('\nDataset not found ⚠️, missing paths %s' % [str(x) for x in val if not x.exists()])
|
msg = f"\nDataset '{dataset}' not found ⚠️, missing paths %s" % [str(x) for x in val if not x.exists()]
|
||||||
if not s or not autodownload:
|
if s and autodownload:
|
||||||
raise FileNotFoundError('Dataset not found ❌')
|
LOGGER.warning(msg)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(s)
|
||||||
t = time.time()
|
t = time.time()
|
||||||
if s.startswith('http') and s.endswith('.zip'): # URL
|
if s.startswith('http') and s.endswith('.zip'): # URL
|
||||||
f = Path(s).name # filename
|
f = Path(s).name # filename
|
||||||
|
@ -126,15 +126,15 @@ class Exporter:
|
|||||||
save_dir (Path): Directory to save results.
|
save_dir (Path): Directory to save results.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config=DEFAULT_CFG, overrides=None):
|
def __init__(self, cfg=DEFAULT_CFG, overrides=None):
|
||||||
"""
|
"""
|
||||||
Initializes the Exporter class.
|
Initializes the Exporter class.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (str, optional): Path to a configuration file. Defaults to DEFAULT_CONFIG.
|
cfg (str, optional): Path to a configuration file. Defaults to DEFAULT_CONFIG.
|
||||||
overrides (dict, optional): Configuration overrides. Defaults to None.
|
overrides (dict, optional): Configuration overrides. Defaults to None.
|
||||||
"""
|
"""
|
||||||
self.args = get_cfg(config, overrides)
|
self.args = get_cfg(cfg, overrides)
|
||||||
self.callbacks = defaultdict(list, {k: [v] for k, v in callbacks.default_callbacks.items()}) # add callbacks
|
self.callbacks = defaultdict(list, {k: [v] for k, v in callbacks.default_callbacks.items()}) # add callbacks
|
||||||
callbacks.add_integration_callbacks(self)
|
callbacks.add_integration_callbacks(self)
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ class Exporter:
|
|||||||
# Load PyTorch model
|
# Load PyTorch model
|
||||||
self.device = select_device('cpu' if self.args.device is None else self.args.device)
|
self.device = select_device('cpu' if self.args.device is None else self.args.device)
|
||||||
if self.args.half:
|
if self.args.half:
|
||||||
if self.device.type == 'cpu' and not coreml:
|
if self.device.type == 'cpu' and not coreml and not xml:
|
||||||
LOGGER.info('half=True only compatible with GPU or CoreML export, i.e. use device=0 or format=coreml')
|
LOGGER.info('half=True only compatible with GPU or CoreML export, i.e. use device=0 or format=coreml')
|
||||||
self.args.half = False
|
self.args.half = False
|
||||||
assert not self.args.dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic'
|
assert not self.args.dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic'
|
||||||
@ -184,7 +184,7 @@ class Exporter:
|
|||||||
y = None
|
y = None
|
||||||
for _ in range(2):
|
for _ in range(2):
|
||||||
y = model(im) # dry runs
|
y = model(im) # dry runs
|
||||||
if self.args.half and not coreml:
|
if self.args.half and not coreml and not xml:
|
||||||
im, model = im.half(), model.half() # to FP16
|
im, model = im.half(), model.half() # to FP16
|
||||||
shape = tuple((y[0] if isinstance(y, tuple) else y).shape) # model output shape
|
shape = tuple((y[0] if isinstance(y, tuple) else y).shape) # model output shape
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
@ -332,7 +332,7 @@ class Exporter:
|
|||||||
f = str(self.file).replace(self.file.suffix, f'_openvino_model{os.sep}')
|
f = str(self.file).replace(self.file.suffix, f'_openvino_model{os.sep}')
|
||||||
f_onnx = self.file.with_suffix('.onnx')
|
f_onnx = self.file.with_suffix('.onnx')
|
||||||
|
|
||||||
cmd = f"mo --input_model {f_onnx} --output_dir {f} --data_type {'FP16' if self.args.half else 'FP32'}"
|
cmd = f"mo --input_model {f_onnx} --output_dir {f} {'--compress_to_fp16' * self.args.half}"
|
||||||
subprocess.run(cmd.split(), check=True, env=os.environ) # export
|
subprocess.run(cmd.split(), check=True, env=os.environ) # export
|
||||||
yaml_save(Path(f) / self.file.with_suffix('.yaml').name, self.metadata) # add metadata.yaml
|
yaml_save(Path(f) / self.file.with_suffix('.yaml').name, self.metadata) # add metadata.yaml
|
||||||
return f, None
|
return f, None
|
||||||
|
@ -6,7 +6,7 @@ from ultralytics import yolo # noqa
|
|||||||
from ultralytics.nn.tasks import ClassificationModel, DetectionModel, SegmentationModel, attempt_load_one_weight
|
from ultralytics.nn.tasks import ClassificationModel, DetectionModel, SegmentationModel, attempt_load_one_weight
|
||||||
from ultralytics.yolo.cfg import get_cfg
|
from ultralytics.yolo.cfg import get_cfg
|
||||||
from ultralytics.yolo.engine.exporter import Exporter
|
from ultralytics.yolo.engine.exporter import Exporter
|
||||||
from ultralytics.yolo.utils import DEFAULT_CFG_PATH, LOGGER, yaml_load
|
from ultralytics.yolo.utils import DEFAULT_CFG, LOGGER, yaml_load
|
||||||
from ultralytics.yolo.utils.checks import check_yaml
|
from ultralytics.yolo.utils.checks import check_yaml
|
||||||
from ultralytics.yolo.utils.torch_utils import guess_task_from_head, smart_inference_mode
|
from ultralytics.yolo.utils.torch_utils import guess_task_from_head, smart_inference_mode
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ class YOLO:
|
|||||||
overrides = self.overrides.copy()
|
overrides = self.overrides.copy()
|
||||||
overrides.update(kwargs)
|
overrides.update(kwargs)
|
||||||
overrides["mode"] = "val"
|
overrides["mode"] = "val"
|
||||||
args = get_cfg(cfg=DEFAULT_CFG_PATH, overrides=overrides)
|
args = get_cfg(cfg=DEFAULT_CFG, overrides=overrides)
|
||||||
args.data = data or args.data
|
args.data = data or args.data
|
||||||
args.task = self.task
|
args.task = self.task
|
||||||
|
|
||||||
@ -169,7 +169,7 @@ class YOLO:
|
|||||||
|
|
||||||
overrides = self.overrides.copy()
|
overrides = self.overrides.copy()
|
||||||
overrides.update(kwargs)
|
overrides.update(kwargs)
|
||||||
args = get_cfg(cfg=DEFAULT_CFG_PATH, overrides=overrides)
|
args = get_cfg(cfg=DEFAULT_CFG, overrides=overrides)
|
||||||
args.task = self.task
|
args.task = self.task
|
||||||
|
|
||||||
print(args)
|
print(args)
|
||||||
@ -181,8 +181,7 @@ class YOLO:
|
|||||||
Trains the model on a given dataset.
|
Trains the model on a given dataset.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
**kwargs (Any): Any number of arguments representing the training configuration. List of all args can be found in 'config' section.
|
**kwargs (Any): Any number of arguments representing the training configuration.
|
||||||
You can pass all arguments as a yaml file in `cfg`. Other args are ignored if `cfg` file is passed
|
|
||||||
"""
|
"""
|
||||||
overrides = self.overrides.copy()
|
overrides = self.overrides.copy()
|
||||||
overrides.update(kwargs)
|
overrides.update(kwargs)
|
||||||
@ -192,7 +191,7 @@ class YOLO:
|
|||||||
overrides["task"] = self.task
|
overrides["task"] = self.task
|
||||||
overrides["mode"] = "train"
|
overrides["mode"] = "train"
|
||||||
if not overrides.get("data"):
|
if not overrides.get("data"):
|
||||||
raise AttributeError("dataset not provided! Please define `data` in config.yaml or pass as an argument.")
|
raise AttributeError("Dataset required but missing, i.e. pass 'data=coco128.yaml'")
|
||||||
if overrides.get("resume"):
|
if overrides.get("resume"):
|
||||||
overrides["resume"] = self.ckpt_path
|
overrides["resume"] = self.ckpt_path
|
||||||
|
|
||||||
@ -223,6 +222,13 @@ class YOLO:
|
|||||||
|
|
||||||
return model_class, trainer_class, validator_class, predictor_class
|
return model_class, trainer_class, validator_class, predictor_class
|
||||||
|
|
||||||
|
@property
|
||||||
|
def names(self):
|
||||||
|
"""
|
||||||
|
Returns class names of the loaded model.
|
||||||
|
"""
|
||||||
|
return self.model.names
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _reset_ckpt_args(args):
|
def _reset_ckpt_args(args):
|
||||||
args.pop("project", None)
|
args.pop("project", None)
|
||||||
|
@ -27,7 +27,6 @@ Usage - formats:
|
|||||||
"""
|
"""
|
||||||
import platform
|
import platform
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import chain
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
@ -62,15 +61,15 @@ class BasePredictor:
|
|||||||
data_path (str): Path to data.
|
data_path (str): Path to data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config=DEFAULT_CFG_PATH, overrides=None):
|
def __init__(self, cfg=DEFAULT_CFG_PATH, overrides=None):
|
||||||
"""
|
"""
|
||||||
Initializes the BasePredictor class.
|
Initializes the BasePredictor class.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (str, optional): Path to a configuration file. Defaults to DEFAULT_CONFIG.
|
cfg (str, optional): Path to a configuration file. Defaults to DEFAULT_CONFIG.
|
||||||
overrides (dict, optional): Configuration overrides. Defaults to None.
|
overrides (dict, optional): Configuration overrides. Defaults to None.
|
||||||
"""
|
"""
|
||||||
self.args = get_cfg(config, overrides)
|
self.args = get_cfg(cfg, overrides)
|
||||||
project = self.args.project or Path(SETTINGS['runs_dir']) / self.args.task
|
project = self.args.project or Path(SETTINGS['runs_dir']) / self.args.task
|
||||||
name = self.args.name or f"{self.args.mode}"
|
name = self.args.name or f"{self.args.mode}"
|
||||||
self.save_dir = increment_path(Path(project) / name, exist_ok=self.args.exist_ok)
|
self.save_dir = increment_path(Path(project) / name, exist_ok=self.args.exist_ok)
|
||||||
@ -219,7 +218,7 @@ class BasePredictor:
|
|||||||
self.run_callbacks("on_predict_batch_end")
|
self.run_callbacks("on_predict_batch_end")
|
||||||
|
|
||||||
# Print results
|
# Print results
|
||||||
if verbose:
|
if verbose and self.seen:
|
||||||
t = tuple(x.t / self.seen * 1E3 for x in self.dt) # speeds per image
|
t = tuple(x.t / self.seen * 1E3 for x in self.dt) # speeds per image
|
||||||
LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms postprocess per image at shape '
|
LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms postprocess per image at shape '
|
||||||
f'{(1, 3, *self.imgsz)}' % t)
|
f'{(1, 3, *self.imgsz)}' % t)
|
||||||
|
@ -31,7 +31,8 @@ from ultralytics.yolo.utils.autobatch import check_train_batch_size
|
|||||||
from ultralytics.yolo.utils.checks import check_file, check_imgsz, print_args
|
from ultralytics.yolo.utils.checks import check_file, check_imgsz, print_args
|
||||||
from ultralytics.yolo.utils.dist import ddp_cleanup, generate_ddp_command
|
from ultralytics.yolo.utils.dist import ddp_cleanup, generate_ddp_command
|
||||||
from ultralytics.yolo.utils.files import get_latest_run, increment_path
|
from ultralytics.yolo.utils.files import get_latest_run, increment_path
|
||||||
from ultralytics.yolo.utils.torch_utils import ModelEMA, de_parallel, init_seeds, one_cycle, strip_optimizer
|
from ultralytics.yolo.utils.torch_utils import (EarlyStopping, ModelEMA, de_parallel, init_seeds, one_cycle,
|
||||||
|
strip_optimizer)
|
||||||
|
|
||||||
|
|
||||||
class BaseTrainer:
|
class BaseTrainer:
|
||||||
@ -71,15 +72,15 @@ class BaseTrainer:
|
|||||||
csv (Path): Path to results CSV file.
|
csv (Path): Path to results CSV file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config=DEFAULT_CFG_PATH, overrides=None):
|
def __init__(self, cfg=DEFAULT_CFG_PATH, overrides=None):
|
||||||
"""
|
"""
|
||||||
Initializes the BaseTrainer class.
|
Initializes the BaseTrainer class.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (str, optional): Path to a configuration file. Defaults to DEFAULT_CONFIG.
|
cfg (str, optional): Path to a configuration file. Defaults to DEFAULT_CONFIG.
|
||||||
overrides (dict, optional): Configuration overrides. Defaults to None.
|
overrides (dict, optional): Configuration overrides. Defaults to None.
|
||||||
"""
|
"""
|
||||||
self.args = get_cfg(config, overrides)
|
self.args = get_cfg(cfg, overrides)
|
||||||
self.device = utils.torch_utils.select_device(self.args.device, self.args.batch)
|
self.device = utils.torch_utils.select_device(self.args.device, self.args.batch)
|
||||||
self.check_resume()
|
self.check_resume()
|
||||||
self.console = LOGGER
|
self.console = LOGGER
|
||||||
@ -225,6 +226,7 @@ class BaseTrainer:
|
|||||||
self.lf = lambda x: (1 - x / self.epochs) * (1.0 - self.args.lrf) + self.args.lrf # linear
|
self.lf = lambda x: (1 - x / self.epochs) * (1.0 - self.args.lrf) + self.args.lrf # linear
|
||||||
self.scheduler = lr_scheduler.LambdaLR(self.optimizer, lr_lambda=self.lf)
|
self.scheduler = lr_scheduler.LambdaLR(self.optimizer, lr_lambda=self.lf)
|
||||||
self.scheduler.last_epoch = self.start_epoch - 1 # do not move
|
self.scheduler.last_epoch = self.start_epoch - 1 # do not move
|
||||||
|
self.stopper, self.stop = EarlyStopping(patience=self.args.patience), False
|
||||||
|
|
||||||
# dataloaders
|
# dataloaders
|
||||||
batch_size = self.batch_size // world_size if world_size > 1 else self.batch_size
|
batch_size = self.batch_size // world_size if world_size > 1 else self.batch_size
|
||||||
@ -333,10 +335,12 @@ class BaseTrainer:
|
|||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
self.ema.update_attr(self.model, include=['yaml', 'nc', 'args', 'names', 'stride', 'class_weights'])
|
self.ema.update_attr(self.model, include=['yaml', 'nc', 'args', 'names', 'stride', 'class_weights'])
|
||||||
final_epoch = (epoch + 1 == self.epochs)
|
final_epoch = (epoch + 1 == self.epochs) or self.stopper.possible_stop
|
||||||
|
|
||||||
if self.args.val or final_epoch:
|
if self.args.val or final_epoch:
|
||||||
self.metrics, self.fitness = self.validate()
|
self.metrics, self.fitness = self.validate()
|
||||||
self.save_metrics(metrics={**self.label_loss_items(self.tloss), **self.metrics, **self.lr})
|
self.save_metrics(metrics={**self.label_loss_items(self.tloss), **self.metrics, **self.lr})
|
||||||
|
self.stop = self.stopper(epoch + 1, self.fitness)
|
||||||
|
|
||||||
# Save model
|
# Save model
|
||||||
if self.args.save or (epoch + 1 == self.epochs):
|
if self.args.save or (epoch + 1 == self.epochs):
|
||||||
@ -347,7 +351,15 @@ class BaseTrainer:
|
|||||||
self.epoch_time = tnow - self.epoch_time_start
|
self.epoch_time = tnow - self.epoch_time_start
|
||||||
self.epoch_time_start = tnow
|
self.epoch_time_start = tnow
|
||||||
self.run_callbacks("on_fit_epoch_end")
|
self.run_callbacks("on_fit_epoch_end")
|
||||||
# TODO: termination condition
|
|
||||||
|
# Early Stopping
|
||||||
|
if RANK != -1: # if DDP training
|
||||||
|
broadcast_list = [self.stop if RANK == 0 else None]
|
||||||
|
dist.broadcast_object_list(broadcast_list, 0) # broadcast 'stop' to all ranks
|
||||||
|
if RANK != 0:
|
||||||
|
self.stop = broadcast_list[0]
|
||||||
|
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
|
# Do final val with best.pt
|
||||||
|
@ -8,9 +8,9 @@ import platform
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
import types
|
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
@ -55,10 +55,34 @@ HELP_MSG = \
|
|||||||
|
|
||||||
3. Use the command line interface (CLI):
|
3. Use the command line interface (CLI):
|
||||||
|
|
||||||
yolo task=detect mode=train model=yolov8n.yaml args...
|
YOLOv8 'yolo' CLI commands use the following syntax:
|
||||||
classify predict yolov8n-cls.yaml args...
|
|
||||||
segment val yolov8n-seg.yaml args...
|
yolo TASK MODE ARGS
|
||||||
export yolov8n.pt format=onnx args...
|
|
||||||
|
Where TASK (optional) is one of [detect, segment, classify]
|
||||||
|
MODE (required) is one of [train, val, predict, export]
|
||||||
|
ARGS (optional) are any number of custom 'arg=value' pairs like 'imgsz=320' that override defaults.
|
||||||
|
See all ARGS at https://docs.ultralytics.com/cfg or with 'yolo cfg'
|
||||||
|
|
||||||
|
- Train a detection model for 10 epochs with an initial learning_rate of 0.01
|
||||||
|
yolo detect train data=coco128.yaml model=yolov8n.pt epochs=10 lr0=0.01
|
||||||
|
|
||||||
|
- Predict a YouTube video using a pretrained segmentation model at image size 320:
|
||||||
|
yolo segment predict model=yolov8n-seg.pt source=https://youtu.be/Zgi9g1ksQHc imgsz=320
|
||||||
|
|
||||||
|
- Val a pretrained detection model at batch-size 1 and image size 640:
|
||||||
|
yolo detect val model=yolov8n.pt data=coco128.yaml batch=1 imgsz=640
|
||||||
|
|
||||||
|
- Export a YOLOv8n classification model to ONNX format at image size 224 by 128 (no TASK required)
|
||||||
|
yolo export model=yolov8n-cls.pt format=onnx imgsz=224,128
|
||||||
|
|
||||||
|
- Run special commands:
|
||||||
|
yolo help
|
||||||
|
yolo checks
|
||||||
|
yolo version
|
||||||
|
yolo settings
|
||||||
|
yolo copy-cfg
|
||||||
|
yolo cfg
|
||||||
|
|
||||||
Docs: https://docs.ultralytics.com
|
Docs: https://docs.ultralytics.com
|
||||||
Community: https://community.ultralytics.com
|
Community: https://community.ultralytics.com
|
||||||
@ -73,11 +97,24 @@ cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with Py
|
|||||||
os.environ['NUMEXPR_MAX_THREADS'] = str(NUM_THREADS) # NumExpr max threads
|
os.environ['NUMEXPR_MAX_THREADS'] = str(NUM_THREADS) # NumExpr max threads
|
||||||
os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8' # for deterministic training
|
os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8' # for deterministic training
|
||||||
|
|
||||||
# Default config dictionary
|
|
||||||
|
class IterableSimpleNamespace(SimpleNamespace):
|
||||||
|
"""
|
||||||
|
Iterable SimpleNamespace class to allow SimpleNamespace to be used with dict() and in for loops
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(vars(self).items())
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '\n'.join(f"{k}={v}" for k, v in vars(self).items())
|
||||||
|
|
||||||
|
|
||||||
|
# Default configuration
|
||||||
with open(DEFAULT_CFG_PATH, errors='ignore') as f:
|
with open(DEFAULT_CFG_PATH, errors='ignore') as f:
|
||||||
DEFAULT_CFG_DICT = yaml.safe_load(f)
|
DEFAULT_CFG_DICT = yaml.safe_load(f)
|
||||||
DEFAULT_CFG_KEYS = DEFAULT_CFG_DICT.keys()
|
DEFAULT_CFG_KEYS = DEFAULT_CFG_DICT.keys()
|
||||||
DEFAULT_CFG = types.SimpleNamespace(**DEFAULT_CFG_DICT)
|
DEFAULT_CFG = IterableSimpleNamespace(**DEFAULT_CFG_DICT)
|
||||||
|
|
||||||
|
|
||||||
def is_colab():
|
def is_colab():
|
||||||
@ -307,14 +344,15 @@ def set_logging(name=LOGGING_NAME, verbose=True):
|
|||||||
|
|
||||||
class TryExcept(contextlib.ContextDecorator):
|
class TryExcept(contextlib.ContextDecorator):
|
||||||
# YOLOv8 TryExcept class. Usage: @TryExcept() decorator or 'with TryExcept():' context manager
|
# YOLOv8 TryExcept class. Usage: @TryExcept() decorator or 'with TryExcept():' context manager
|
||||||
def __init__(self, msg=''):
|
def __init__(self, msg='', verbose=True):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
self.verbose = verbose
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __exit__(self, exc_type, value, traceback):
|
def __exit__(self, exc_type, value, traceback):
|
||||||
if value:
|
if self.verbose and value:
|
||||||
print(emojis(f"{self.msg}{': ' if self.msg else ''}{value}"))
|
print(emojis(f"{self.msg}{': ' if self.msg else ''}{value}"))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -366,6 +404,21 @@ def yaml_load(file='data.yaml', append_filename=False):
|
|||||||
return {**yaml.safe_load(f), 'yaml_file': str(file)} if append_filename else yaml.safe_load(f)
|
return {**yaml.safe_load(f), 'yaml_file': str(file)} if append_filename else yaml.safe_load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def yaml_print(yaml_file: Union[str, Path, dict]) -> None:
|
||||||
|
"""
|
||||||
|
Pretty prints a yaml file or a yaml-formatted dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
yaml_file: The file path of the yaml file or a yaml-formatted dictionary.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
yaml_dict = yaml_load(yaml_file) if isinstance(yaml_file, (str, Path)) else yaml_file
|
||||||
|
dump = yaml.dump(yaml_dict, default_flow_style=False)
|
||||||
|
LOGGER.info(f"Printing '{colorstr('bold', 'black', yaml_file)}'\n\n{dump}")
|
||||||
|
|
||||||
|
|
||||||
def set_sentry(dsn=None):
|
def set_sentry(dsn=None):
|
||||||
"""
|
"""
|
||||||
Initialize the Sentry SDK for error tracking and reporting if pytest is not currently running.
|
Initialize the Sentry SDK for error tracking and reporting if pytest is not currently running.
|
||||||
@ -379,7 +432,6 @@ def set_sentry(dsn=None):
|
|||||||
debug=False,
|
debug=False,
|
||||||
traces_sample_rate=1.0,
|
traces_sample_rate=1.0,
|
||||||
release=ultralytics.__version__,
|
release=ultralytics.__version__,
|
||||||
send_default_pii=True,
|
|
||||||
environment='production', # 'dev' or 'production'
|
environment='production', # 'dev' or 'production'
|
||||||
ignore_errors=[KeyboardInterrupt])
|
ignore_errors=[KeyboardInterrupt])
|
||||||
|
|
||||||
@ -439,17 +491,6 @@ def set_settings(kwargs, file=USER_CONFIG_DIR / 'settings.yaml'):
|
|||||||
yaml_save(file, SETTINGS)
|
yaml_save(file, SETTINGS)
|
||||||
|
|
||||||
|
|
||||||
def print_settings():
|
|
||||||
"""
|
|
||||||
Function that prints Ultralytics settings
|
|
||||||
"""
|
|
||||||
import json
|
|
||||||
s = f'\n{PREFIX}Settings:\n'
|
|
||||||
s += json.dumps(SETTINGS, indent=2)
|
|
||||||
s += f"\n\nUpdate settings at {USER_CONFIG_DIR / 'settings.yaml'}"
|
|
||||||
LOGGER.info(s)
|
|
||||||
|
|
||||||
|
|
||||||
# Run below code on utils init -----------------------------------------------------------------------------------------
|
# Run below code on utils init -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
# Set logger
|
# Set logger
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import json
|
import json
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from ultralytics.hub.utils import PREFIX, sync_analytics
|
from ultralytics.hub.utils import PREFIX, traces
|
||||||
from ultralytics.yolo.utils import LOGGER
|
from ultralytics.yolo.utils import LOGGER
|
||||||
|
|
||||||
|
|
||||||
@ -43,24 +43,24 @@ def on_train_end(trainer):
|
|||||||
LOGGER.info(f"{PREFIX}Training completed successfully ✅\n"
|
LOGGER.info(f"{PREFIX}Training completed successfully ✅\n"
|
||||||
f"{PREFIX}Uploading final {session.model_id}")
|
f"{PREFIX}Uploading final {session.model_id}")
|
||||||
session.upload_model(trainer.epoch, trainer.best, map=trainer.metrics['metrics/mAP50-95(B)'], final=True)
|
session.upload_model(trainer.epoch, trainer.best, map=trainer.metrics['metrics/mAP50-95(B)'], final=True)
|
||||||
session.alive = False # stop heartbeats
|
session.shutdown() # stop heartbeats
|
||||||
LOGGER.info(f"{PREFIX}View model at https://hub.ultralytics.com/models/{session.model_id} 🚀")
|
LOGGER.info(f"{PREFIX}View model at https://hub.ultralytics.com/models/{session.model_id} 🚀")
|
||||||
|
|
||||||
|
|
||||||
def on_train_start(trainer):
|
def on_train_start(trainer):
|
||||||
sync_analytics(trainer.args)
|
traces(trainer.args, traces_sample_rate=0.0)
|
||||||
|
|
||||||
|
|
||||||
def on_val_start(validator):
|
def on_val_start(validator):
|
||||||
sync_analytics(validator.args)
|
traces(validator.args, traces_sample_rate=0.0)
|
||||||
|
|
||||||
|
|
||||||
def on_predict_start(predictor):
|
def on_predict_start(predictor):
|
||||||
sync_analytics(predictor.args)
|
traces(predictor.args, traces_sample_rate=0.0)
|
||||||
|
|
||||||
|
|
||||||
def on_export_start(exporter):
|
def on_export_start(exporter):
|
||||||
sync_analytics(exporter.args)
|
traces(exporter.args, traces_sample_rate=0.0)
|
||||||
|
|
||||||
|
|
||||||
callbacks = {
|
callbacks = {
|
||||||
|
@ -154,7 +154,7 @@ def check_python(minimum: str = '3.7.0') -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
check_version(platform.python_version(), minimum, name='Python ', hard=True)
|
return check_version(platform.python_version(), minimum, name='Python ', hard=True)
|
||||||
|
|
||||||
|
|
||||||
@TryExcept()
|
@TryExcept()
|
||||||
@ -223,8 +223,10 @@ def check_file(file, suffix=''):
|
|||||||
files = []
|
files = []
|
||||||
for d in 'models', 'yolo/data': # search directories
|
for d in 'models', 'yolo/data': # search directories
|
||||||
files.extend(glob.glob(str(ROOT / d / '**' / file), recursive=True)) # find file
|
files.extend(glob.glob(str(ROOT / d / '**' / file), recursive=True)) # find file
|
||||||
assert len(files), f'File not found: {file}' # assert file was found
|
if not files:
|
||||||
assert len(files) == 1, f"Multiple files match '{file}', specify exact path: {files}" # assert unique
|
raise FileNotFoundError(f"{file} does not exist")
|
||||||
|
elif len(files) > 1:
|
||||||
|
raise FileNotFoundError(f"Multiple files match '{file}', specify exact path: {files}")
|
||||||
return files[0] # return file
|
return files[0] # return file
|
||||||
|
|
||||||
|
|
||||||
|
@ -141,10 +141,14 @@ def download(url, dir=Path.cwd(), unzip=True, delete=True, curl=False, threads=1
|
|||||||
dir = Path(dir)
|
dir = Path(dir)
|
||||||
dir.mkdir(parents=True, exist_ok=True) # make directory
|
dir.mkdir(parents=True, exist_ok=True) # make directory
|
||||||
if threads > 1:
|
if threads > 1:
|
||||||
pool = ThreadPool(threads)
|
# pool = ThreadPool(threads)
|
||||||
pool.imap(lambda x: download_one(*x), zip(url, repeat(dir))) # multithreaded
|
# pool.imap(lambda x: download_one(*x), zip(url, repeat(dir))) # multithreaded
|
||||||
pool.close()
|
# pool.close()
|
||||||
pool.join()
|
# pool.join()
|
||||||
|
with ThreadPool(threads) as pool:
|
||||||
|
pool.imap(lambda x: download_one(*x), zip(url, repeat(dir))) # multithreaded
|
||||||
|
pool.close()
|
||||||
|
pool.join()
|
||||||
else:
|
else:
|
||||||
for u in [url] if isinstance(url, (str, Path)) else url:
|
for u in [url] if isinstance(url, (str, Path)) else url:
|
||||||
download_one(u, dir)
|
download_one(u, dir)
|
||||||
|
@ -62,7 +62,9 @@ def select_device(device='', batch_size=0, newline=False):
|
|||||||
# device = None or 'cpu' or 0 or '0' or '0,1,2,3'
|
# device = None or 'cpu' or 0 or '0' or '0,1,2,3'
|
||||||
ver = git_describe() or ultralytics.__version__ # git commit or pip package version
|
ver = git_describe() or ultralytics.__version__ # git commit or pip package version
|
||||||
s = f'Ultralytics YOLOv{ver} 🚀 Python-{platform.python_version()} torch-{torch.__version__} '
|
s = f'Ultralytics YOLOv{ver} 🚀 Python-{platform.python_version()} torch-{torch.__version__} '
|
||||||
device = str(device).strip().lower().replace('cuda:', '').replace('none', '') # to string, 'cuda:0' to '0'
|
device = str(device).lower()
|
||||||
|
for remove in 'cuda:', 'none', '(', ')', '[', ']', "'", ' ':
|
||||||
|
device = device.replace(remove, '') # to string, 'cuda:0' -> '0' and '(0, 1)' -> '0,1'
|
||||||
cpu = device == 'cpu'
|
cpu = device == 'cpu'
|
||||||
mps = device == 'mps' # Apple Metal Performance Shaders (MPS)
|
mps = device == 'mps' # Apple Metal Performance Shaders (MPS)
|
||||||
if cpu or mps:
|
if cpu or mps:
|
||||||
@ -369,3 +371,26 @@ def profile(input, ops, n=10, device=None):
|
|||||||
results.append(None)
|
results.append(None)
|
||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
class EarlyStopping:
|
||||||
|
# early stopper
|
||||||
|
def __init__(self, patience=30):
|
||||||
|
self.best_fitness = 0.0 # i.e. mAP
|
||||||
|
self.best_epoch = 0
|
||||||
|
self.patience = patience or float('inf') # epochs to wait after fitness stops improving to stop
|
||||||
|
self.possible_stop = False # possible stop may occur next epoch
|
||||||
|
|
||||||
|
def __call__(self, epoch, fitness):
|
||||||
|
if fitness >= self.best_fitness: # >= 0 to allow for early zero-fitness stage of training
|
||||||
|
self.best_epoch = epoch
|
||||||
|
self.best_fitness = fitness
|
||||||
|
delta = epoch - self.best_epoch # epochs without improvement
|
||||||
|
self.possible_stop = delta >= (self.patience - 1) # possible stop may occur next epoch
|
||||||
|
stop = delta >= self.patience # stop training if patience exceeded
|
||||||
|
if stop:
|
||||||
|
LOGGER.info(f'Stopping training early as no improvement observed in last {self.patience} epochs. '
|
||||||
|
f'Best results observed at epoch {self.best_epoch}, best model saved as best.pt.\n'
|
||||||
|
f'To update EarlyStopping(patience={self.patience}) pass a new patience value, '
|
||||||
|
f'i.e. `patience=300` or use `patience=0` to disable EarlyStopping.')
|
||||||
|
return stop
|
||||||
|
@ -4,7 +4,7 @@ import torch
|
|||||||
|
|
||||||
from ultralytics.yolo.engine.predictor import BasePredictor
|
from ultralytics.yolo.engine.predictor import BasePredictor
|
||||||
from ultralytics.yolo.engine.results import Results
|
from ultralytics.yolo.engine.results import Results
|
||||||
from ultralytics.yolo.utils import DEFAULT_CFG, ROOT, is_git_directory
|
from ultralytics.yolo.utils import DEFAULT_CFG, ROOT
|
||||||
from ultralytics.yolo.utils.plotting import Annotator
|
from ultralytics.yolo.utils.plotting import Annotator
|
||||||
|
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ class ClassificationPredictor(BasePredictor):
|
|||||||
|
|
||||||
def predict(cfg=DEFAULT_CFG):
|
def predict(cfg=DEFAULT_CFG):
|
||||||
cfg.model = cfg.model or "yolov8n-cls.pt" # or "resnet18"
|
cfg.model = cfg.model or "yolov8n-cls.pt" # or "resnet18"
|
||||||
cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if is_git_directory() \
|
cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
|
||||||
else "https://ultralytics.com/images/bus.jpg"
|
else "https://ultralytics.com/images/bus.jpg"
|
||||||
predictor = ClassificationPredictor(cfg)
|
predictor = ClassificationPredictor(cfg)
|
||||||
predictor.predict_cli()
|
predictor.predict_cli()
|
||||||
|
@ -4,7 +4,7 @@ import torch
|
|||||||
|
|
||||||
from ultralytics.yolo.engine.predictor import BasePredictor
|
from ultralytics.yolo.engine.predictor import BasePredictor
|
||||||
from ultralytics.yolo.engine.results import Results
|
from ultralytics.yolo.engine.results import Results
|
||||||
from ultralytics.yolo.utils import DEFAULT_CFG, ROOT, is_git_directory, ops
|
from ultralytics.yolo.utils import DEFAULT_CFG, ROOT, ops
|
||||||
from ultralytics.yolo.utils.plotting import Annotator, colors, save_one_box
|
from ultralytics.yolo.utils.plotting import Annotator, colors, save_one_box
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ class DetectionPredictor(BasePredictor):
|
|||||||
|
|
||||||
def predict(cfg=DEFAULT_CFG):
|
def predict(cfg=DEFAULT_CFG):
|
||||||
cfg.model = cfg.model or "yolov8n.pt"
|
cfg.model = cfg.model or "yolov8n.pt"
|
||||||
cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if is_git_directory() \
|
cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
|
||||||
else "https://ultralytics.com/images/bus.jpg"
|
else "https://ultralytics.com/images/bus.jpg"
|
||||||
predictor = DetectionPredictor(cfg)
|
predictor = DetectionPredictor(cfg)
|
||||||
predictor.predict_cli()
|
predictor.predict_cli()
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import torch
|
import torch
|
||||||
|
|
||||||
from ultralytics.yolo.engine.results import Results
|
from ultralytics.yolo.engine.results import Results
|
||||||
from ultralytics.yolo.utils import DEFAULT_CFG, ROOT, is_git_directory, ops
|
from ultralytics.yolo.utils import DEFAULT_CFG, ROOT, ops
|
||||||
from ultralytics.yolo.utils.plotting import colors, save_one_box
|
from ultralytics.yolo.utils.plotting import colors, save_one_box
|
||||||
from ultralytics.yolo.v8.detect.predict import DetectionPredictor
|
from ultralytics.yolo.v8.detect.predict import DetectionPredictor
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ class SegmentationPredictor(DetectionPredictor):
|
|||||||
|
|
||||||
def predict(cfg=DEFAULT_CFG):
|
def predict(cfg=DEFAULT_CFG):
|
||||||
cfg.model = cfg.model or "yolov8n-seg.pt"
|
cfg.model = cfg.model or "yolov8n-seg.pt"
|
||||||
cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if is_git_directory() \
|
cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
|
||||||
else "https://ultralytics.com/images/bus.jpg"
|
else "https://ultralytics.com/images/bus.jpg"
|
||||||
predictor = SegmentationPredictor(cfg)
|
predictor = SegmentationPredictor(cfg)
|
||||||
predictor.predict_cli()
|
predictor.predict_cli()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user