From 15b3b0365ab2f12993a58985f3cb7f2137409a0c Mon Sep 17 00:00:00 2001
From: Glenn Jocher <glenn.jocher@ultralytics.com>
Date: Wed, 25 Jan 2023 21:21:39 +0100
Subject: [PATCH] `ultralytics 8.0.20` CLI `yolo` simplifications, DDP and ONNX
 fixes (#608)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sid Prabhakaran <s2siddhu@gmail.com>
---
 ultralytics/__init__.py                 |  2 +-
 ultralytics/hub/utils.py                | 75 +++++++++++++++++++------
 ultralytics/nn/tasks.py                 | 26 ++++-----
 ultralytics/yolo/cfg/__init__.py        | 54 +++++++++++-------
 ultralytics/yolo/cfg/default.yaml       | 28 ++++-----
 ultralytics/yolo/utils/__init__.py      | 11 ++--
 ultralytics/yolo/utils/callbacks/hub.py |  8 +--
 ultralytics/yolo/utils/torch_utils.py   |  2 +-
 ultralytics/yolo/v8/classify/predict.py | 17 ++++--
 ultralytics/yolo/v8/classify/train.py   | 27 ++++-----
 ultralytics/yolo/v8/classify/val.py     | 17 ++++--
 ultralytics/yolo/v8/detect/predict.py   | 17 ++++--
 ultralytics/yolo/v8/detect/train.py     | 23 ++++----
 ultralytics/yolo/v8/detect/val.py       | 17 ++++--
 ultralytics/yolo/v8/segment/predict.py  | 18 ++++--
 ultralytics/yolo/v8/segment/train.py    | 23 ++++----
 ultralytics/yolo/v8/segment/val.py      | 16 ++++--
 17 files changed, 242 insertions(+), 139 deletions(-)

diff --git a/ultralytics/__init__.py b/ultralytics/__init__.py
index f4298659..6d1cc9f2 100644
--- a/ultralytics/__init__.py
+++ b/ultralytics/__init__.py
@@ -1,6 +1,6 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
 
-__version__ = "8.0.19"
+__version__ = "8.0.20"
 
 from ultralytics.yolo.engine.model import YOLO
 from ultralytics.yolo.utils import ops
diff --git a/ultralytics/hub/utils.py b/ultralytics/hub/utils.py
index 54fdfed0..50d6f42b 100644
--- a/ultralytics/hub/utils.py
+++ b/ultralytics/hub/utils.py
@@ -1,14 +1,19 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
 
 import os
+import platform
 import shutil
+import sys
 import threading
 import time
+from pathlib import Path
 from random import random
 
 import requests
 
-from ultralytics.yolo.utils import DEFAULT_CFG_DICT, LOGGER, RANK, SETTINGS, TryExcept, colorstr, emojis
+from ultralytics.yolo.utils import (DEFAULT_CFG_DICT, LOGGER, RANK, SETTINGS, TryExcept, colorstr, emojis,
+                                    get_git_origin_url, is_colab, is_docker, is_git_dir, is_github_actions_ci,
+                                    is_jupyter, is_kaggle, is_pip_package, is_pytest_running)
 
 PREFIX = colorstr('Ultralytics: ')
 HELP_MSG = 'If this issue persists please visit https://github.com/ultralytics/hub/issues for assistance.'
@@ -131,21 +136,57 @@ def smart_request(*args, retry=3, timeout=30, thread=True, code=-1, method="post
         return func(*args, **kwargs)
 
 
-@TryExcept(verbose=False)
-def traces(cfg, all_keys=False, traces_sample_rate=0.0):
-    """
-   Sync traces data if enabled in the global settings
+class Traces:
 
-    Args:
-        cfg (IterableSimpleNamespace): Configuration for the task and mode.
-        all_keys (bool): Sync all items, not just non-default values.
-        traces_sample_rate (float): Fraction of traces captured from 0.0 to 1.0
-    """
-    if SETTINGS['sync'] and RANK in {-1, 0} and (random() < traces_sample_rate):
-        cfg = vars(cfg)  # convert type from IterableSimpleNamespace to dict
-        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['uuid'] = SETTINGS['uuid']  # add the device UUID to the configuration data
+    def __init__(self):
+        """
+        Initialize Traces for error tracking and reporting if tests are not currently running.
+        """
+        from ultralytics import __version__
+        env = 'Colab' if is_colab() else 'Kaggle' if is_kaggle() else 'Jupyter' if is_jupyter() else \
+            'Docker' if is_docker() else platform.system()
+        self.rate_limit = 3.0  # rate limit (seconds)
+        self.t = time.time()  # rate limit timer (seconds)
+        self.metadata = {
+            "sys_argv_name": Path(sys.argv[0]).name,
+            "install": 'git' if is_git_dir() else 'pip' if is_pip_package() else 'other',
+            "python": platform.python_version(),
+            "release": __version__,
+            "environment": env}
+        self.enabled = SETTINGS['sync'] and \
+                       RANK in {-1, 0} and \
+                       not is_pytest_running() and \
+                       not is_github_actions_ci() and \
+                       (is_pip_package() or get_git_origin_url() == "https://github.com/ultralytics/ultralytics.git")
 
-        # Send a request to the HUB API to sync analytics
-        smart_request(f'{HUB_API_ROOT}/v1/usage/anonymous', json=cfg, headers=None, code=3, retry=0, verbose=False)
+    @TryExcept(verbose=False)
+    def __call__(self, cfg, all_keys=False, traces_sample_rate=1.0):
+        """
+       Sync traces data if enabled in the global settings
+
+        Args:
+            cfg (IterableSimpleNamespace): Configuration for the task and mode.
+            all_keys (bool): Sync all items, not just non-default values.
+            traces_sample_rate (float): Fraction of traces captured from 0.0 to 1.0
+        """
+        t = time.time()  # current time
+        if self.enabled and random() < traces_sample_rate and (t - self.t) > self.rate_limit:
+            self.t = t  # reset rate limit timer
+            cfg = vars(cfg)  # convert type from IterableSimpleNamespace to dict
+            if not all_keys:  # filter cfg
+                include_keys = {'task', 'mode'}  # always include
+                cfg = {k: v for k, v in cfg.items() if v != DEFAULT_CFG_DICT.get(k, None) or k in include_keys}
+            trace = {'uuid': SETTINGS['uuid'], 'cfg': cfg, 'metadata': self.metadata}
+
+            # Send a request to the HUB API to sync analytics
+            smart_request(f'{HUB_API_ROOT}/v1/usage/anonymous',
+                          json=trace,
+                          headers=None,
+                          code=3,
+                          retry=0,
+                          verbose=False)
+
+
+# Run below code on hub/utils init -------------------------------------------------------------------------------------
+
+traces = Traces()
diff --git a/ultralytics/nn/tasks.py b/ultralytics/nn/tasks.py
index 2f18597a..472e1e19 100644
--- a/ultralytics/nn/tasks.py
+++ b/ultralytics/nn/tasks.py
@@ -472,10 +472,13 @@ def guess_model_task(model):
     Raises:
         SyntaxError: If the task of the model could not be determined.
     """
-    cfg, task = None, None
+    cfg = None
     if isinstance(model, dict):
         cfg = model
     elif isinstance(model, nn.Module):  # PyTorch model
+        for x in 'model.args', 'model.model.args', 'model.model.model.args':
+            with contextlib.suppress(Exception):
+                return eval(x)['task']
         for x in 'model.yaml', 'model.model.yaml', 'model.model.model.yaml':
             with contextlib.suppress(Exception):
                 cfg = eval(x)
@@ -485,25 +488,22 @@ def guess_model_task(model):
     if cfg:
         m = cfg["head"][-1][-2].lower()  # output module name
         if m in ["classify", "classifier", "cls", "fc"]:
-            task = "classify"
+            return "classify"
         if m in ["detect"]:
-            task = "detect"
+            return "detect"
         if m in ["segment"]:
-            task = "segment"
+            return "segment"
 
     # Guess from PyTorch model
-    if task is None and isinstance(model, nn.Module):
+    if isinstance(model, nn.Module):
         for m in model.modules():
             if isinstance(m, Detect):
-                task = "detect"
+                return "detect"
             elif isinstance(m, Segment):
-                task = "segment"
+                return "segment"
             elif isinstance(m, Classify):
-                task = "classify"
+                return "classify"
 
     # Unable to determine task from model
-    if task is None:
-        raise SyntaxError("YOLO is unable to automatically guess model task. Explicitly define task for your model, "
-                          "i.e. 'task=detect', 'task=segment' or 'task=classify'.")
-    else:
-        return task
+    raise SyntaxError("YOLO is unable to automatically guess model task. Explicitly define task for your model, "
+                      "i.e. 'task=detect', 'task=segment' or 'task=classify'.")
diff --git a/ultralytics/yolo/cfg/__init__.py b/ultralytics/yolo/cfg/__init__.py
index a14d67bb..12934639 100644
--- a/ultralytics/yolo/cfg/__init__.py
+++ b/ultralytics/yolo/cfg/__init__.py
@@ -8,8 +8,8 @@ from pathlib import Path
 from types import SimpleNamespace
 from typing import Dict, List, Union
 
-from ultralytics import __version__, yolo
-from ultralytics.yolo.utils import (DEFAULT_CFG_DICT, DEFAULT_CFG_PATH, LOGGER, PREFIX, USER_CONFIG_DIR,
+from ultralytics import __version__
+from ultralytics.yolo.utils import (DEFAULT_CFG_DICT, DEFAULT_CFG_PATH, LOGGER, PREFIX, ROOT, USER_CONFIG_DIR,
                                     IterableSimpleNamespace, colorstr, yaml_load, yaml_print)
 from ultralytics.yolo.utils.checks import check_yolo
 
@@ -211,30 +211,42 @@ def entrypoint(debug=False):
         else:
             raise argument_error(a)
 
-    cfg = get_cfg(DEFAULT_CFG_DICT, overrides)  # create CFG instance
-
-    # Checks error catch
-    if cfg.mode == 'checks':
-        LOGGER.warning(
-            "WARNING ⚠️ 'yolo mode=checks' is deprecated and will be removed in the future. Use 'yolo checks' instead.")
+    # Mode
+    mode = overrides.pop('mode', None)
+    model = overrides.pop('model', None)
+    if mode == 'checks':
+        LOGGER.warning("WARNING ⚠️ 'yolo mode=checks' is deprecated. Use 'yolo checks' instead.")
         check_yolo()
         return
+    elif mode is None:
+        mode = DEFAULT_CFG_DICT['mode'] or 'predict'
+        LOGGER.warning(f"WARNING ⚠️ 'mode' is missing. Valid modes are {modes}. Using default 'mode={mode}'.")
 
-    # Mapping from task to module
-    module = {"detect": yolo.v8.detect, "segment": yolo.v8.segment, "classify": yolo.v8.classify}.get(cfg.task)
-    if not module:
-        raise SyntaxError(f"yolo task={cfg.task} is invalid. Valid tasks are: {', '.join(tasks)}\n{CLI_HELP_MSG}")
+    # Model
+    if model is None:
+        model = DEFAULT_CFG_DICT['model'] or 'yolov8n.pt'
+        LOGGER.warning(f"WARNING ⚠️ 'model' is missing. Using default 'model={model}'.")
+    from ultralytics.yolo.engine.model import YOLO
+    model = YOLO(model)
+    task = model.task
 
-    # Mapping from mode to function
-    func = {
-        "train": module.train,
-        "val": module.val,
-        "predict": module.predict,
-        "export": yolo.engine.exporter.export}.get(cfg.mode)
-    if not func:
-        raise SyntaxError(f"yolo mode={cfg.mode} is invalid. Valid modes are: {', '.join(modes)}\n{CLI_HELP_MSG}")
+    # Task
+    if mode == 'predict' and 'source' not in overrides:
+        overrides['source'] = DEFAULT_CFG_DICT['source'] or ROOT / "assets" if (ROOT / "assets").exists() \
+            else "https://ultralytics.com/images/bus.jpg"
+        LOGGER.warning(f"WARNING ⚠️ 'source' is missing. Using default 'source={overrides['source']}'.")
+    elif mode in ('train', 'val'):
+        if 'data' not in overrides:
+            overrides['data'] = DEFAULT_CFG_DICT['data'] or 'mnist160' if task == 'classify' \
+                else 'coco128-seg.yaml' if task == 'segment' else 'coco128.yaml'
+            LOGGER.warning(f"WARNING ⚠️ 'data' is missing. Using default 'data={overrides['data']}'.")
+    elif mode == 'export':
+        if 'format' not in overrides:
+            overrides['format'] = DEFAULT_CFG_DICT['format'] or 'torchscript'
+            LOGGER.warning(f"WARNING ⚠️ 'format' is missing. Using default 'format={overrides['format']}'.")
 
-    func(cfg)
+    # Run command in python
+    getattr(model, mode)(verbose=True, **overrides)
 
 
 # Special modes --------------------------------------------------------------------------------------------------------
diff --git a/ultralytics/yolo/cfg/default.yaml b/ultralytics/yolo/cfg/default.yaml
index 55dc6701..789f2b70 100644
--- a/ultralytics/yolo/cfg/default.yaml
+++ b/ultralytics/yolo/cfg/default.yaml
@@ -1,26 +1,26 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
 # Default training settings and hyperparameters for medium-augmentation COCO training
 
-task: "detect"  # inference task, i.e. detect, segment, classify
-mode: "train"  # YOLO mode, i.e. train, val, predict, export
+task: detect  # inference task, i.e. detect, segment, classify
+mode: train  # YOLO mode, i.e. train, val, predict, export
 
 # Train settings -------------------------------------------------------------------------------------------------------
-model: null  # path to model file, i.e. yolov8n.pt, yolov8n.yaml
-data: null  # path to data file, i.e. i.e. coco128.yaml
+model:  # path to model file, i.e. yolov8n.pt, yolov8n.yaml
+data:  # path to data file, i.e. i.e. coco128.yaml
 epochs: 100  # number of epochs to train for
 patience: 50  # epochs to wait for no observable improvement for early stopping of training
 batch: 16  # number of images per batch (-1 for AutoBatch)
 imgsz: 640  # size of input images as integer or w,h
 save: True  # save train checkpoints and predict results
 cache: False  # True/ram, disk or False. Use cache for data loading
-device: null  # device to run on, i.e. cuda device=0 or device=0,1,2,3 or device=cpu
+device:  # device to run on, i.e. cuda device=0 or device=0,1,2,3 or device=cpu
 workers: 8  # number of worker threads for data loading (per RANK if DDP)
-project: null  # project name
-name: null  # experiment name
+project:  # project name
+name:  # experiment name
 exist_ok: False  # whether to overwrite existing experiment
 pretrained: False  # whether to use a pretrained model
-optimizer: 'SGD'  # optimizer to use, choices=['SGD', 'Adam', 'AdamW', 'RMSProp']
-verbose: False  # whether to print verbose output
+optimizer: SGD  # optimizer to use, choices=['SGD', 'Adam', 'AdamW', 'RMSProp']
+verbose: True  # whether to print verbose output
 seed: 0  # random seed for reproducibility
 deterministic: True  # whether to enable deterministic mode
 single_cls: False  # train multi-class data as single-class
@@ -39,7 +39,7 @@ dropout: 0.0  # use dropout regularization (classify train only)
 val: True  # validate/test during training
 save_json: False  # save results to JSON file
 save_hybrid: False  # save hybrid version of labels (labels + additional predictions)
-conf: null  # object confidence threshold for detection (default 0.25 predict, 0.001 val)
+conf:  # object confidence threshold for detection (default 0.25 predict, 0.001 val)
 iou: 0.7  # intersection over union (IoU) threshold for NMS
 max_det: 300  # maximum number of detections per image
 half: False  # use half precision (FP16)
@@ -47,7 +47,7 @@ dnn: False  # use OpenCV DNN for ONNX inference
 plots: True  # save plots during train/val
 
 # Prediction settings --------------------------------------------------------------------------------------------------
-source: null  # source directory for images or videos
+source:  # source directory for images or videos
 show: False  # show results if possible
 save_txt: False  # save results as .txt file
 save_conf: False  # save results with confidence scores
@@ -59,7 +59,7 @@ line_thickness: 3  # bounding box thickness (pixels)
 visualize: False  # visualize model features
 augment: False  # apply image augmentation to prediction sources
 agnostic_nms: False  # class-agnostic NMS
-classes: null  # filter results by class, i.e. class=0, or class=[0,2,3]
+classes:  # filter results by class, i.e. class=0, or class=[0,2,3]
 retina_masks: False  # use high-resolution segmentation masks
 boxes: True # Show boxes in segmentation predictions
 
@@ -70,7 +70,7 @@ optimize: False  # TorchScript: optimize for mobile
 int8: False  # CoreML/TF INT8 quantization
 dynamic: False  # ONNX/TF/TensorRT: dynamic axes
 simplify: False  # ONNX: simplify model
-opset: 17  # ONNX: opset version
+opset:  # ONNX: opset version (optional)
 workspace: 4  # TensorRT: workspace size (GB)
 nms: False  # CoreML: add NMS
 
@@ -103,7 +103,7 @@ mixup: 0.0  # image mixup (probability)
 copy_paste: 0.0  # segment copy-paste (probability)
 
 # Custom config.yaml ---------------------------------------------------------------------------------------------------
-cfg: null  # for overriding defaults.yaml
+cfg:  # for overriding defaults.yaml
 
 # Debug, do not modify -------------------------------------------------------------------------------------------------
 v5loader: False  # use legacy YOLOv5 dataloader
diff --git a/ultralytics/yolo/utils/__init__.py b/ultralytics/yolo/utils/__init__.py
index bf447e12..128f00f3 100644
--- a/ultralytics/yolo/utils/__init__.py
+++ b/ultralytics/yolo/utils/__init__.py
@@ -116,6 +116,9 @@ class IterableSimpleNamespace(SimpleNamespace):
 # Default configuration
 with open(DEFAULT_CFG_PATH, errors='ignore') as f:
     DEFAULT_CFG_DICT = yaml.safe_load(f)
+    for k, v in DEFAULT_CFG_DICT.items():
+        if isinstance(v, str) and v.lower() == 'none':
+            DEFAULT_CFG_DICT[k] = None
 DEFAULT_CFG_KEYS = DEFAULT_CFG_DICT.keys()
 DEFAULT_CFG = IterableSimpleNamespace(**DEFAULT_CFG_DICT)
 
@@ -448,13 +451,13 @@ def set_sentry():
     """
 
     def before_send(event, hint):
-        oss = 'colab' if is_colab() else 'kaggle' if is_kaggle() else 'jupyter' if is_jupyter() else \
-            'docker' if is_docker() else platform.system()
+        env = 'Colab' if is_colab() else 'Kaggle' if is_kaggle() else 'Jupyter' if is_jupyter() else \
+            'Docker' if is_docker() else platform.system()
         event['tags'] = {
             "sys_argv": sys.argv[0],
             "sys_argv_name": Path(sys.argv[0]).name,
             "install": 'git' if is_git_dir() else 'pip' if is_pip_package() else 'other',
-            "os": oss}
+            "os": env}
         return event
 
     if SETTINGS['sync'] and \
@@ -529,7 +532,7 @@ def set_settings(kwargs, file=USER_CONFIG_DIR / 'settings.yaml'):
     yaml_save(file, SETTINGS)
 
 
-# Run below code on utils init -----------------------------------------------------------------------------------------
+# Run below code on yolo/utils init ------------------------------------------------------------------------------------
 
 # Set logger
 set_logging(LOGGING_NAME)  # run before defining LOGGER
diff --git a/ultralytics/yolo/utils/callbacks/hub.py b/ultralytics/yolo/utils/callbacks/hub.py
index ffea13ed..3376945b 100644
--- a/ultralytics/yolo/utils/callbacks/hub.py
+++ b/ultralytics/yolo/utils/callbacks/hub.py
@@ -48,19 +48,19 @@ def on_train_end(trainer):
 
 
 def on_train_start(trainer):
-    traces(trainer.args, traces_sample_rate=0.0)
+    traces(trainer.args, traces_sample_rate=1.0)
 
 
 def on_val_start(validator):
-    traces(validator.args, traces_sample_rate=0.0)
+    traces(validator.args, traces_sample_rate=1.0)
 
 
 def on_predict_start(predictor):
-    traces(predictor.args, traces_sample_rate=0.0)
+    traces(predictor.args, traces_sample_rate=1.0)
 
 
 def on_export_start(exporter):
-    traces(exporter.args, traces_sample_rate=0.0)
+    traces(exporter.args, traces_sample_rate=1.0)
 
 
 callbacks = {
diff --git a/ultralytics/yolo/utils/torch_utils.py b/ultralytics/yolo/utils/torch_utils.py
index 280ad4fd..4010eca5 100644
--- a/ultralytics/yolo/utils/torch_utils.py
+++ b/ultralytics/yolo/utils/torch_utils.py
@@ -31,7 +31,7 @@ WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1))
 @contextmanager
 def torch_distributed_zero_first(local_rank: int):
     # Decorator to make all processes in distributed training wait for each local_master to do something
-    initialized = torch.distributed.is_initialized()  # prevent 'Default process group has not been initialized' errors
+    initialized = torch.distributed.is_available() and torch.distributed.is_initialized()
     if initialized and local_rank not in {-1, 0}:
         dist.barrier(device_ids=[local_rank])
     yield
diff --git a/ultralytics/yolo/v8/classify/predict.py b/ultralytics/yolo/v8/classify/predict.py
index 917d6064..f6c05cf8 100644
--- a/ultralytics/yolo/v8/classify/predict.py
+++ b/ultralytics/yolo/v8/classify/predict.py
@@ -1,4 +1,5 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
+import sys
 
 import torch
 
@@ -63,12 +64,18 @@ class ClassificationPredictor(BasePredictor):
         return log_string
 
 
-def predict(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n-cls.pt"  # or "resnet18"
-    cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
+def predict(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n-cls.pt"  # or "resnet18"
+    source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
         else "https://ultralytics.com/images/bus.jpg"
-    predictor = ClassificationPredictor(cfg)
-    predictor.predict_cli()
+
+    args = dict(model=model, source=source, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model)(**args)
+    else:
+        predictor = ClassificationPredictor(args)
+        predictor.predict_cli()
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/classify/train.py b/ultralytics/yolo/v8/classify/train.py
index 2e2a6b9b..c26524dc 100644
--- a/ultralytics/yolo/v8/classify/train.py
+++ b/ultralytics/yolo/v8/classify/train.py
@@ -1,4 +1,5 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
+import sys
 
 import torch
 import torchvision
@@ -135,22 +136,18 @@ class ClassificationTrainer(BaseTrainer):
                 #     self.run_callbacks('on_fit_epoch_end')
 
 
-def train(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n-cls.pt"  # or "resnet18"
-    cfg.data = cfg.data or "mnist160"  # or yolo.ClassificationDataset("mnist")
+def train(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n-cls.pt"  # or "resnet18"
+    data = cfg.data or "mnist160"  # or yolo.ClassificationDataset("mnist")
+    device = cfg.device if cfg.device is not None else ''
 
-    # Reproduce ImageNet results
-    # cfg.lr0 = 0.1
-    # cfg.weight_decay = 5e-5
-    # cfg.label_smoothing = 0.1
-    # cfg.warmup_epochs = 0.0
-
-    cfg.device = cfg.device if cfg.device is not None else ''
-    # trainer = ClassificationTrainer(cfg)
-    # trainer.train()
-    from ultralytics import YOLO
-    model = YOLO(cfg.model)
-    model.train(**vars(cfg))
+    args = dict(model=model, data=data, device=device, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model).train(**args)
+    else:
+        trainer = ClassificationTrainer(args)
+        trainer.train()
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/classify/val.py b/ultralytics/yolo/v8/classify/val.py
index b28b9edf..21cb1594 100644
--- a/ultralytics/yolo/v8/classify/val.py
+++ b/ultralytics/yolo/v8/classify/val.py
@@ -1,4 +1,5 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
+import sys
 
 from ultralytics.yolo.data import build_classification_dataloader
 from ultralytics.yolo.engine.validator import BaseValidator
@@ -45,11 +46,17 @@ class ClassificationValidator(BaseValidator):
         self.logger.info(pf % ("all", self.metrics.top1, self.metrics.top5))
 
 
-def val(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n-cls.pt"  # or "resnet18"
-    cfg.data = cfg.data or "mnist160"
-    validator = ClassificationValidator(args=cfg)
-    validator(model=cfg.model)
+def val(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n-cls.pt"  # or "resnet18"
+    data = cfg.data or "mnist160"
+
+    args = dict(model=model, data=data, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model).val(**args)
+    else:
+        validator = ClassificationValidator(args=args)
+        validator(model=args['model'])
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/detect/predict.py b/ultralytics/yolo/v8/detect/predict.py
index 581b686d..2131b836 100644
--- a/ultralytics/yolo/v8/detect/predict.py
+++ b/ultralytics/yolo/v8/detect/predict.py
@@ -1,4 +1,5 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
+import sys
 
 import torch
 
@@ -81,12 +82,18 @@ class DetectionPredictor(BasePredictor):
         return log_string
 
 
-def predict(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n.pt"
-    cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
+def predict(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n.pt"
+    source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
         else "https://ultralytics.com/images/bus.jpg"
-    predictor = DetectionPredictor(cfg)
-    predictor.predict_cli()
+
+    args = dict(model=model, source=source, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model)(**args)
+    else:
+        predictor = DetectionPredictor(args)
+        predictor.predict_cli()
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/detect/train.py b/ultralytics/yolo/v8/detect/train.py
index 6c5d6e98..719568b0 100644
--- a/ultralytics/yolo/v8/detect/train.py
+++ b/ultralytics/yolo/v8/detect/train.py
@@ -1,5 +1,5 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
-
+import sys
 from copy import copy
 
 import torch
@@ -194,15 +194,18 @@ class Loss:
         return loss.sum() * batch_size, loss.detach()  # loss(box, cls, dfl)
 
 
-def train(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n.pt"
-    cfg.data = cfg.data or "coco128.yaml"  # or yolo.ClassificationDataset("mnist")
-    cfg.device = cfg.device if cfg.device is not None else ''
-    # trainer = DetectionTrainer(cfg)
-    # trainer.train()
-    from ultralytics import YOLO
-    model = YOLO(cfg.model)
-    model.train(**vars(cfg))
+def train(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n.pt"
+    data = cfg.data or "coco128.yaml"  # or yolo.ClassificationDataset("mnist")
+    device = cfg.device if cfg.device is not None else ''
+
+    args = dict(model=model, data=data, device=device, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model).train(**args)
+    else:
+        trainer = DetectionTrainer(args)
+        trainer.train()
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/detect/val.py b/ultralytics/yolo/v8/detect/val.py
index e338964f..ab6e465a 100644
--- a/ultralytics/yolo/v8/detect/val.py
+++ b/ultralytics/yolo/v8/detect/val.py
@@ -1,6 +1,7 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
 
 import os
+import sys
 from pathlib import Path
 
 import numpy as np
@@ -232,11 +233,17 @@ class DetectionValidator(BaseValidator):
         return stats
 
 
-def val(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n.pt"
-    cfg.data = cfg.data or "coco128.yaml"
-    validator = DetectionValidator(args=cfg)
-    validator(model=cfg.model)
+def val(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n.pt"
+    data = cfg.data or "coco128.yaml"
+
+    args = dict(model=model, data=data, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model).val(**args)
+    else:
+        validator = DetectionValidator(args=args)
+        validator(model=args['model'])
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/segment/predict.py b/ultralytics/yolo/v8/segment/predict.py
index 78c48d61..76bd0ebf 100644
--- a/ultralytics/yolo/v8/segment/predict.py
+++ b/ultralytics/yolo/v8/segment/predict.py
@@ -1,5 +1,7 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
 
+import sys
+
 import torch
 
 from ultralytics.yolo.engine.results import Results
@@ -98,12 +100,18 @@ class SegmentationPredictor(DetectionPredictor):
         return log_string
 
 
-def predict(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n-seg.pt"
-    cfg.source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
+def predict(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n-seg.pt"
+    source = cfg.source if cfg.source is not None else ROOT / "assets" if (ROOT / "assets").exists() \
         else "https://ultralytics.com/images/bus.jpg"
-    predictor = SegmentationPredictor(cfg)
-    predictor.predict_cli()
+
+    args = dict(model=model, source=source, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model)(**args)
+    else:
+        predictor = SegmentationPredictor(args)
+        predictor.predict_cli()
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/segment/train.py b/ultralytics/yolo/v8/segment/train.py
index fbd96f80..8964e0b1 100644
--- a/ultralytics/yolo/v8/segment/train.py
+++ b/ultralytics/yolo/v8/segment/train.py
@@ -1,5 +1,5 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
-
+import sys
 from copy import copy
 
 import torch
@@ -140,15 +140,18 @@ class SegLoss(Loss):
         return (crop_mask(loss, xyxy).mean(dim=(1, 2)) / area).mean()
 
 
-def train(cfg=DEFAULT_CFG):
-    cfg.model = cfg.model or "yolov8n-seg.pt"
-    cfg.data = cfg.data or "coco128-seg.yaml"  # or yolo.ClassificationDataset("mnist")
-    cfg.device = cfg.device if cfg.device is not None else ''
-    # trainer = SegmentationTrainer(cfg)
-    # trainer.train()
-    from ultralytics import YOLO
-    model = YOLO(cfg.model)
-    model.train(**vars(cfg))
+def train(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n-seg.pt"
+    data = cfg.data or "coco128-seg.yaml"  # or yolo.ClassificationDataset("mnist")
+    device = cfg.device if cfg.device is not None else ''
+
+    args = dict(model=model, data=data, device=device, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model).train(**args)
+    else:
+        trainer = SegmentationTrainer(args)
+        trainer.train()
 
 
 if __name__ == "__main__":
diff --git a/ultralytics/yolo/v8/segment/val.py b/ultralytics/yolo/v8/segment/val.py
index 276a50b0..492a6825 100644
--- a/ultralytics/yolo/v8/segment/val.py
+++ b/ultralytics/yolo/v8/segment/val.py
@@ -1,6 +1,7 @@
 # Ultralytics YOLO 🚀, GPL-3.0 license
 
 import os
+import sys
 from multiprocessing.pool import ThreadPool
 from pathlib import Path
 
@@ -242,10 +243,17 @@ class SegmentationValidator(DetectionValidator):
         return stats
 
 
-def val(cfg=DEFAULT_CFG):
-    cfg.data = cfg.data or "coco128-seg.yaml"
-    validator = SegmentationValidator(args=cfg)
-    validator(model=cfg.model)
+def val(cfg=DEFAULT_CFG, use_python=False):
+    model = cfg.model or "yolov8n-seg.pt"
+    data = cfg.data or "coco128-seg.yaml"
+
+    args = dict(model=model, data=data, verbose=True)
+    if use_python:
+        from ultralytics import YOLO
+        YOLO(model).val(**args)
+    else:
+        validator = SegmentationValidator(args=args)
+        validator(model=args['model'])
 
 
 if __name__ == "__main__":