ultralytics 8.0.181 RTDETR, MLFlow fixes and Examples updates (#4927)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Jordane Sikati <jordanesikati@pusan.ac.kr>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Akash A Desai <62583018+akashAD98@users.noreply.github.com>
Co-authored-by: Lukas Hennies <45569834+Gornoka@users.noreply.github.com>
Co-authored-by: 唐洁 <tangjie1953479@tongji.edu.cn>
This commit is contained in:
Glenn Jocher 2023-09-18 14:12:34 +02:00 committed by GitHub
parent 5702b2dccd
commit 742ec7fb1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 46 additions and 26 deletions

View File

@ -2,7 +2,6 @@ include *.md
include requirements.txt include requirements.txt
include LICENSE include LICENSE
include setup.py include setup.py
recursive-exclude __pycache__ *
include ultralytics/assets/bus.jpg include ultralytics/assets/bus.jpg
include ultralytics/assets/zidane.jpg include ultralytics/assets/zidane.jpg
recursive-include ultralytics *.yaml recursive-include ultralytics *.yaml

View File

@ -83,6 +83,7 @@ Without further ado, let's dive in!
3. Now, read the contents of the dataset YAML file and extract the indices of the class labels. 3. Now, read the contents of the dataset YAML file and extract the indices of the class labels.
```python ```python
yaml_file = 'path/to/data.yaml' # your data YAML with data directories and names dictionary
with open(yaml_file, 'r', encoding="utf8") as y: with open(yaml_file, 'r', encoding="utf8") as y:
classes = yaml.safe_load(y)['names'] classes = yaml.safe_load(y)['names']
cls_idx = sorted(classes.keys()) cls_idx = sorted(classes.keys())
@ -177,10 +178,18 @@ The ideal scenario is for all class ratios to be reasonably similar for each spl
4. Next, we create the directories and dataset YAML files for each split. 4. Next, we create the directories and dataset YAML files for each split.
```python ```python
supported_extensions = ['.jpg', '.jpeg', '.png']
# Initialize an empty list to store image file paths
images = []
# Loop through supported extensions and gather image files
for ext in supported_extensions:
images.extend(sorted((dataset_path / 'images').rglob(f"*{ext}")))
# Create the necessary directories and dataset YAML files (unchanged)
save_path = Path(dataset_path / f'{datetime.date.today().isoformat()}_{ksplit}-Fold_Cross-val') save_path = Path(dataset_path / f'{datetime.date.today().isoformat()}_{ksplit}-Fold_Cross-val')
save_path.mkdir(parents=True, exist_ok=True) save_path.mkdir(parents=True, exist_ok=True)
images = sorted((dataset_path / 'images').rglob("*.jpg")) # change file extension as needed
ds_yamls = [] ds_yamls = []
for split in folds_df.columns: for split in folds_df.columns:
@ -216,8 +225,7 @@ The ideal scenario is for all class ratios to be reasonably similar for each spl
img_to_path = save_path / split / k_split / 'images' img_to_path = save_path / split / k_split / 'images'
lbl_to_path = save_path / split / k_split / 'labels' lbl_to_path = save_path / split / k_split / 'labels'
# Copy image and label files to new directory # Copy image and label files to new directory (SamefileError if file already exists)
# Might throw a SamefileError if file already exists
shutil.copy(image, img_to_path / image.name) shutil.copy(image, img_to_path / image.name)
shutil.copy(label, lbl_to_path / label.name) shutil.copy(label, lbl_to_path / label.name)
``` ```
@ -244,9 +252,15 @@ fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")
```python ```python
results = {} results = {}
# Define your additional arguments here
batch = 16
project = 'kfold_demo'
epochs = 100
for k in range(ksplit): for k in range(ksplit):
dataset_yaml = ds_yamls[k] dataset_yaml = ds_yamls[k]
model.train(data=dataset_yaml, *args, **kwargs) # Include any training arguments model.train(data=dataset_yaml,epochs=epochs, batch=batch, project=project) # include any train arguments
results[k] = model.metrics # save output metrics for further analysis results[k] = model.metrics # save output metrics for further analysis
``` ```

View File

@ -2,7 +2,7 @@
# Example: pip install -r requirements.txt # Example: pip install -r requirements.txt
# Base ---------------------------------------- # Base ----------------------------------------
matplotlib>=3.2.2 matplotlib>=3.3.0
numpy>=1.22.2 # pinned by Snyk to avoid a vulnerability numpy>=1.22.2 # pinned by Snyk to avoid a vulnerability
opencv-python>=4.6.0 opencv-python>=4.6.0
pillow>=7.1.2 pillow>=7.1.2

View File

@ -6,7 +6,10 @@ from pathlib import Path
import pytest import pytest
from ultralytics.utils import ASSETS, SETTINGS from ultralytics.utils import ASSETS, SETTINGS
from ultralytics.utils.checks import cuda_device_count, cuda_is_available
CUDA_IS_AVAILABLE = cuda_is_available()
CUDA_DEVICE_COUNT = cuda_device_count()
WEIGHTS_DIR = Path(SETTINGS['weights_dir']) WEIGHTS_DIR = Path(SETTINGS['weights_dir'])
TASK_ARGS = [ TASK_ARGS = [
('detect', 'yolov8n', 'coco8.yaml'), ('detect', 'yolov8n', 'coco8.yaml'),
@ -117,6 +120,8 @@ def test_mobilesam():
# Slow Tests ----------------------------------------------------------------------------------------------------------- # Slow Tests -----------------------------------------------------------------------------------------------------------
@pytest.mark.slow @pytest.mark.slow
@pytest.mark.parametrize('task,model,data', TASK_ARGS) @pytest.mark.parametrize('task,model,data', TASK_ARGS)
@pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason='CUDA is not available')
@pytest.mark.skipif(CUDA_DEVICE_COUNT < 2, reason='DDP is not available')
def test_train_gpu(task, model, data): def test_train_gpu(task, model, data):
run(f'yolo train {task} model={model}.yaml data={data} imgsz=32 epochs=1 device=0') # single GPU run(f'yolo train {task} model={model}.yaml data={data} imgsz=32 epochs=1 device=0') # single GPU
run(f'yolo train {task} model={model}.pt data={data} imgsz=32 epochs=1 device=0,1') # multi GPU run(f'yolo train {task} model={model}.pt data={data} imgsz=32 epochs=1 device=0,1') # multi GPU

View File

@ -7,9 +7,10 @@ import torch
from ultralytics import YOLO, download from ultralytics import YOLO, download
from ultralytics.utils import ASSETS, SETTINGS from ultralytics.utils import ASSETS, SETTINGS
from ultralytics.utils.checks import cuda_device_count, cuda_is_available
CUDA_IS_AVAILABLE = torch.cuda.is_available() CUDA_IS_AVAILABLE = cuda_is_available()
CUDA_DEVICE_COUNT = torch.cuda.device_count() CUDA_DEVICE_COUNT = cuda_device_count()
DATASETS_DIR = Path(SETTINGS['datasets_dir']) DATASETS_DIR = Path(SETTINGS['datasets_dir'])
WEIGHTS_DIR = Path(SETTINGS['weights_dir']) WEIGHTS_DIR = Path(SETTINGS['weights_dir'])
@ -18,10 +19,8 @@ DATA = 'coco8.yaml'
def test_checks(): def test_checks():
from ultralytics.utils.checks import cuda_device_count, cuda_is_available assert torch.cuda.is_available() == CUDA_IS_AVAILABLE
assert torch.cuda.device_count() == CUDA_DEVICE_COUNT
assert cuda_device_count() == CUDA_DEVICE_COUNT
assert cuda_is_available() == CUDA_IS_AVAILABLE
@pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason='CUDA is not available') @pytest.mark.skipif(not CUDA_IS_AVAILABLE, reason='CUDA is not available')

View File

@ -14,7 +14,7 @@ from torchvision.transforms import ToTensor
from ultralytics import RTDETR, YOLO from ultralytics import RTDETR, YOLO
from ultralytics.cfg import TASK2DATA from ultralytics.cfg import TASK2DATA
from ultralytics.data.build import load_inference_source from ultralytics.data.build import load_inference_source
from ultralytics.utils import ASSETS, DEFAULT_CFG, LINUX, MACOS, ONLINE, ROOT, SETTINGS, WINDOWS from ultralytics.utils import ASSETS, DEFAULT_CFG, LINUX, MACOS, ONLINE, ROOT, SETTINGS, WINDOWS, is_dir_writeable
from ultralytics.utils.downloads import download from ultralytics.utils.downloads import download
from ultralytics.utils.torch_utils import TORCH_1_9 from ultralytics.utils.torch_utils import TORCH_1_9
@ -23,6 +23,7 @@ MODEL = WEIGHTS_DIR / 'path with spaces' / 'yolov8n.pt' # test spaces in path
CFG = 'yolov8n.yaml' CFG = 'yolov8n.yaml'
SOURCE = ASSETS / 'bus.jpg' SOURCE = ASSETS / 'bus.jpg'
TMP = (ROOT / '../tests/tmp').resolve() # temp directory for test files TMP = (ROOT / '../tests/tmp').resolve() # temp directory for test files
IS_TMP_WRITEABLE = is_dir_writeable(TMP)
def test_model_forward(): def test_model_forward():
@ -58,6 +59,7 @@ def test_model_profile():
_ = model.predict(im, profile=True) _ = model.predict(im, profile=True)
@pytest.mark.skipif(not IS_TMP_WRITEABLE, reason='directory is not writeable')
def test_predict_txt(): def test_predict_txt():
# Write a list of sources (file, dir, glob, recursive glob) to a txt file # Write a list of sources (file, dir, glob, recursive glob) to a txt file
txt_file = TMP / 'sources.txt' txt_file = TMP / 'sources.txt'
@ -128,6 +130,7 @@ def test_predict_grey_and_4ch():
@pytest.mark.skipif(not ONLINE, reason='environment is offline') @pytest.mark.skipif(not ONLINE, reason='environment is offline')
@pytest.mark.skipif(not IS_TMP_WRITEABLE, reason='directory is not writeable')
def test_track_stream(): def test_track_stream():
# Test YouTube streaming inference (short 10 frame video) with non-default ByteTrack tracker # Test YouTube streaming inference (short 10 frame video) with non-default ByteTrack tracker
# imgsz=160 required for tracking for higher confidence and better matches # imgsz=160 required for tracking for higher confidence and better matches

View File

@ -1,6 +1,6 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
__version__ = '8.0.180' __version__ = '8.0.181'
from ultralytics.models import RTDETR, SAM, YOLO from ultralytics.models import RTDETR, SAM, YOLO
from ultralytics.models.fastsam import FastSAM from ultralytics.models.fastsam import FastSAM

View File

@ -103,6 +103,9 @@ class HungarianMatcher(nn.Module):
if self.with_mask: if self.with_mask:
C += self._cost_mask(bs, gt_groups, masks, gt_mask) C += self._cost_mask(bs, gt_groups, masks, gt_mask)
# Set invalid values (NaNs and infinities) to 0 (fixes ValueError: matrix contains invalid numeric entries)
C[C.isnan() | C.isinf()] = 0.0
C = C.view(bs, nq, -1).cpu() C = C.view(bs, nq, -1).cpu()
indices = [linear_sum_assignment(c[i]) for i, c in enumerate(C.split(gt_groups, -1))] indices = [linear_sum_assignment(c[i]) for i, c in enumerate(C.split(gt_groups, -1))]
gt_groups = torch.as_tensor([0, *gt_groups[:-1]]).cumsum_(0) gt_groups = torch.as_tensor([0, *gt_groups[:-1]]).cumsum_(0)

View File

@ -1,6 +1,6 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
from ultralytics.utils import LOGGER, ROOT, SETTINGS, TESTS_RUNNING, colorstr from ultralytics.utils import LOGGER, SETTINGS, TESTS_RUNNING, colorstr
try: try:
assert not TESTS_RUNNING # do not log pytest assert not TESTS_RUNNING # do not log pytest
@ -8,7 +8,7 @@ try:
import mlflow import mlflow
assert hasattr(mlflow, '__version__') # verify package is not directory assert hasattr(mlflow, '__version__') # verify package is not directory
PREFIX = colorstr('MLFlow:')
import os import os
import re import re
@ -25,15 +25,13 @@ def on_pretrain_routine_end(trainer):
if mlflow: if mlflow:
mlflow_location = os.environ['MLFLOW_TRACKING_URI'] # "http://192.168.xxx.xxx:5000" mlflow_location = os.environ['MLFLOW_TRACKING_URI'] # "http://192.168.xxx.xxx:5000"
LOGGER.debug(f'{PREFIX} tracking uri: {mlflow_location}')
mlflow.set_tracking_uri(mlflow_location) mlflow.set_tracking_uri(mlflow_location)
experiment_name = os.environ.get('MLFLOW_EXPERIMENT_NAME') or trainer.args.project or '/Shared/YOLOv8' experiment_name = os.environ.get('MLFLOW_EXPERIMENT_NAME') or trainer.args.project or '/Shared/YOLOv8'
run_name = os.environ.get('MLFLOW_RUN') or trainer.args.name run_name = os.environ.get('MLFLOW_RUN') or trainer.args.name
experiment = mlflow.get_experiment_by_name(experiment_name) experiment = mlflow.set_experiment(experiment_name) # change since mlflow does this now by default
if experiment is None:
mlflow.create_experiment(experiment_name)
mlflow.set_experiment(experiment_name)
mlflow.autolog()
prefix = colorstr('MLFlow: ') prefix = colorstr('MLFlow: ')
try: try:
run, active_run = mlflow, mlflow.active_run() run, active_run = mlflow, mlflow.active_run()
@ -58,10 +56,9 @@ def on_train_end(trainer):
if mlflow: if mlflow:
run.log_artifact(trainer.last) run.log_artifact(trainer.last)
run.log_artifact(trainer.best) run.log_artifact(trainer.best)
run.pyfunc.log_model(artifact_path=experiment_name, run.log_artifact(trainer.save_dir)
code_path=[str(ROOT.parent)], mlflow.end_run()
artifacts={'model_path': str(trainer.save_dir)}, LOGGER.debug(f'{PREFIX} ending run')
python_model=run.pyfunc.PythonModel())
callbacks = { callbacks = {