Override fixes and general updates (#129)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Kalen Michael <kalenmike@gmail.com>
main
Glenn Jocher 2 years ago committed by GitHub
parent d76d7af566
commit af6e3c536b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      tests/test_model.py
  2. 28
      ultralytics/hub/utils.py
  3. 8
      ultralytics/nn/tasks.py
  4. 4
      ultralytics/yolo/configs/default.yaml
  5. 4
      ultralytics/yolo/engine/exporter.py
  6. 3
      ultralytics/yolo/engine/model.py
  7. 2
      ultralytics/yolo/engine/trainer.py
  8. 39
      ultralytics/yolo/utils/__init__.py
  9. 2
      ultralytics/yolo/utils/callbacks/hub.py
  10. 2
      ultralytics/yolo/utils/torch_utils.py

@ -6,16 +6,11 @@ from ultralytics import YOLO
def test_model_init():
model = YOLO("yolov8n.yaml")
model.info()
try:
YOLO()
except Exception:
print("Successfully caught constructor assert!")
raise Exception("constructor error didn't occur")
def test_model_forward():
model = YOLO("yolov8n.yaml")
img = torch.rand(512 * 512 * 3).view(1, 3, 512, 512)
img = torch.rand(1, 3, 320, 320)
model.forward(img)
model(img)
@ -23,24 +18,24 @@ def test_model_forward():
def test_model_info():
model = YOLO("yolov8n.yaml")
model.info()
model = model.load("best.pt")
model = YOLO("yolov8n.pt")
model.info(verbose=True)
def test_model_fuse():
model = YOLO("yolov8n.yaml")
model.fuse()
model.load("best.pt")
model = YOLO("yolov8n.pt")
model.fuse()
def test_visualize_preds():
model = YOLO("best.pt")
model = YOLO("yolov8n.pt")
model.predict(source="ultralytics/assets")
def test_val():
model = YOLO("best.pt")
model = YOLO("yolov8n.pt")
model.val(data="coco128.yaml", imgsz=32)
@ -54,11 +49,11 @@ def test_model_resume():
def test_model_train_pretrained():
model = YOLO("best.pt")
model = YOLO("yolov8n.pt")
model.train(data="coco128.yaml", epochs=1, imgsz=32)
model = model.new("yolov8n.yaml")
model = YOLO("yolov8n.yaml")
model.train(data="coco128.yaml", epochs=1, imgsz=32)
img = torch.rand(512 * 512 * 3).view(1, 3, 512, 512)
img = torch.rand(1, 3, 320, 320)
model(img)
@ -78,7 +73,6 @@ def test_exports():
10 TensorFlow.js tfjs _web_model False False
11 PaddlePaddle paddle _paddle_model True True
"""
from ultralytics import YOLO
from ultralytics.yolo.engine.exporter import export_formats
print(export_formats())

@ -1,15 +1,15 @@
import shutil
import threading
import time
import uuid
import requests
from ultralytics.hub.config import HUB_API_ROOT
from ultralytics.yolo.utils import LOGGER, RANK, SETTINGS, colorstr, emojis
from ultralytics.yolo.utils import DEFAULT_CONFIG, LOGGER, RANK, SETTINGS, colorstr, emojis, yaml_load
PREFIX = colorstr('Ultralytics: ')
HELP_MSG = 'If this issue persists please visit https://github.com/ultralytics/hub/issues for assistance.'
DEFAULT_CONFIG_DICT = yaml_load(DEFAULT_CONFIG)
def check_dataset_disk_space(url='https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip', sf=2.0):
@ -26,7 +26,7 @@ def check_dataset_disk_space(url='https://github.com/ultralytics/yolov5/releases
def request_with_credentials(url: str) -> any:
""" Make a ajax request with cookies attached """
""" Make an ajax request with cookies attached """
from google.colab import output # noqa
from IPython import display # noqa
display.display(
@ -92,17 +92,18 @@ def smart_request(*args, retry=3, timeout=30, thread=True, code=-1, method="post
retry_codes = (408, 500) # retry only these codes
methods = {'post': requests.post, 'get': requests.get} # request methods
def fcn(*args, **kwargs):
t0 = time.time()
def func(*func_args, **func_kwargs):
r = None # response
t0 = time.time() # initial time for timer
for i in range(retry + 1):
if (time.time() - t0) > timeout:
break
r = methods[method](*args, **kwargs) # i.e. post(url, data, json, files)
r = methods[method](*func_args, **func_kwargs) # i.e. post(url, data, json, files)
if r.status_code == 200:
break
try:
m = r.json().get('message', 'No JSON message.')
except Exception:
except AttributeError:
m = 'Unable to read JSON.'
if i == 0:
if r.status_code in retry_codes:
@ -118,22 +119,25 @@ def smart_request(*args, retry=3, timeout=30, thread=True, code=-1, method="post
return r
if thread:
threading.Thread(target=fcn, args=args, kwargs=kwargs, daemon=True).start()
threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True).start()
else:
return fcn(*args, **kwargs)
return func(*args, **kwargs)
def sync_analytics(cfg, enabled=False):
def sync_analytics(cfg, all_keys=False, enabled=True):
"""
Sync analytics data if enabled in the global settings
Args:
cfg (DictConfig): Configuration for the task and mode.
all_keys (bool): Sync all items, not just non-default values.
enabled (bool): For debugging.
"""
if SETTINGS['sync'] and RANK in {-1, 0} and enabled:
cfg = dict(cfg) # convert type from DictConfig to dict
cfg['uuid'] = uuid.getnode() # add the device UUID to the configuration data
if not all_keys:
cfg = {k: v for k, v in cfg.items() if v != DEFAULT_CONFIG_DICT[k]} # retain only non-default values
cfg['uuid'] = SETTINGS['uuid'] # add the device UUID to the configuration data
# Send a request to the HUB API to sync the analytics data
smart_request(f'{HUB_API_ROOT}/analytics', data=cfg, headers=None, code=3, retry=0)
smart_request(f'{HUB_API_ROOT}/v1/usage/anonymous', data=cfg, headers=None, code=3, retry=0)

@ -12,8 +12,8 @@ from ultralytics.nn.modules import (C1, C2, C3, C3TR, SPP, SPPF, Bottleneck, Bot
GhostBottleneck, GhostConv, Segment)
from ultralytics.yolo.utils import LOGGER, colorstr, yaml_load
from ultralytics.yolo.utils.checks import check_yaml
from ultralytics.yolo.utils.torch_utils import (fuse_conv_and_bn, initialize_weights, intersect_state_dicts,
make_divisible, model_info, scale_img, time_sync)
from ultralytics.yolo.utils.torch_utils import (fuse_conv_and_bn, initialize_weights, intersect_dicts, make_divisible,
model_info, scale_img, time_sync)
class BaseModel(nn.Module):
@ -150,7 +150,7 @@ class DetectionModel(BaseModel):
def load(self, weights, verbose=True):
csd = weights['model'].float().state_dict() # checkpoint state_dict as FP32
csd = intersect_state_dicts(csd, self.state_dict()) # intersect
csd = intersect_dicts(csd, self.state_dict()) # intersect
self.load_state_dict(csd, strict=False) # load
if verbose:
LOGGER.info(f'Transferred {len(csd)}/{len(self.model.state_dict())} items from pretrained weights')
@ -191,7 +191,7 @@ class ClassificationModel(BaseModel):
def load(self, weights):
model = weights["model"] if isinstance(weights, dict) else weights # torchvision models are not dicts
csd = model.float().state_dict()
csd = intersect_state_dicts(csd, self.state_dict()) # intersect
csd = intersect_dicts(csd, self.state_dict()) # intersect
self.load_state_dict(csd, strict=False) # load
@staticmethod

@ -1,11 +1,11 @@
# YOLO 🚀 by Ultralytics, GPL-3.0 license
# Default training settings and hyperparameters for medium-augmentation COCO training
task: "classify" # choices=['detect', 'segment', 'classify', 'init'] # init is a special case. Specify task to run.
task: "detect" # choices=['detect', 'segment', 'classify', 'init'] # init is a special case. Specify task to run.
mode: "train" # choices=['train', 'val', 'predict'] # mode to run task in.
# Train settings -------------------------------------------------------------------------------------------------------
model: null # i.e. yolov5s.pt, yolo.yaml. Path to model file
model: null # i.e. yolov8n.pt, yolov8n.yaml. Path to model file
data: null # i.e. coco128.yaml. Path to data file
epochs: 100 # number of epochs to train for
patience: 50 # TODO: epochs to wait for no observable improvement for early stopping of training

@ -137,8 +137,6 @@ class Exporter:
"""
if overrides is None:
overrides = {}
if 'batch_size' not in overrides:
overrides['batch_size'] = 1 # set default export batch size
self.args = get_config(config, overrides)
project = self.args.project or f"runs/{self.args.task}"
name = self.args.name or "exp" # hardcode mode as export doesn't require it
@ -166,6 +164,8 @@ class Exporter:
assert not self.args.dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic'
# Checks
if self.args.batch_size == 16:
self.args.batch_size = 1 # TODO: resolve batch_size 16 default in config.yaml
self.imgsz = check_imgsz(self.args.imgsz, stride=model.stride, min_dim=2) # check image size
if self.args.optimize:
assert self.device.type == 'cpu', '--optimize not compatible with cuda devices, i.e. use --device cpu'

@ -1,6 +1,7 @@
import torch
from pathlib import Path
import torch
from ultralytics import yolo # noqa
from ultralytics.nn.tasks import ClassificationModel, DetectionModel, SegmentationModel, attempt_load_weights
from ultralytics.yolo.configs import get_config

@ -107,6 +107,8 @@ class BaseTrainer:
self.device = utils.torch_utils.select_device(self.args.device, self.batch_size)
self.amp = self.device.type != 'cpu'
self.scaler = amp.GradScaler(enabled=self.amp)
if self.device.type == 'cpu':
self.args.workers = 0 # faster CPU training as time dominated by inference, not dataloading
# Model and Dataloaders.
self.model = self.args.model

@ -6,6 +6,7 @@ import platform
import sys
import tempfile
import threading
import uuid
from pathlib import Path
import cv2
@ -160,7 +161,7 @@ def get_user_config_dir(sub_dir='Ultralytics'):
raise ValueError(f'Unsupported operating system: {os_name}')
# GCP and AWS lambda fix, only /tmp is writeable
if not is_dir_writeable(path.parent):
if not is_dir_writeable(str(path.parent)):
path = Path('/tmp') / sub_dir
# Create the subdirectory if it does not exist
@ -172,9 +173,9 @@ def get_user_config_dir(sub_dir='Ultralytics'):
USER_CONFIG_DIR = get_user_config_dir() # Ultralytics settings dir
def emojis(str=''):
def emojis(string=''):
# Return platform-dependent emoji-safe version of string
return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str
return string.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else string
def colorstr(*input):
@ -282,27 +283,39 @@ def yaml_load(file='data.yaml'):
"""
with open(file, errors='ignore') as f:
# Add YAML filename to dict and return
return {**yaml.safe_load(f), 'yaml_file': file}
return {**yaml.safe_load(f), 'yaml_file': str(file)}
def get_settings(file=USER_CONFIG_DIR / 'settings.yaml'):
"""
Function that loads a global settings YAML, or creates it and populates it with default values if it does not exist.
Loads a global settings YAML file or creates one with default values if it does not exist.
If the datasets or weights directories are set to None, the current working directory will be used.
The 'sync' setting determines whether analytics will be synced to help with YOLO development.
Args:
file (Path): Path to the settings YAML file. Defaults to 'settings.yaml' in the USER_CONFIG_DIR.
Returns:
dict: Dictionary of settings key-value pairs.
"""
from ultralytics.yolo.utils.torch_utils import torch_distributed_zero_first
defaults = {
'datasets_dir': None, # default datasets directory. If None, current working directory is used.
'weights_dir': None, # default weights directory. If None, current working directory is used.
'runs_dir': None, # default runs directory. If None, current working directory is used.
'sync': True, # sync analytics to help with YOLO development
'uuid': uuid.getnode(), # device UUID to align analytics
'yaml_file': str(file)} # setting YAML file path
with torch_distributed_zero_first(RANK):
if not file.exists():
settings = {
'datasets_dir': None, # default datasets directory. If None, current working directory is used.
'weights_dir': None, # default weights directory. If None, current working directory is used.
'sync': True} # sync analytics to help with YOLO development
yaml_save(file, settings)
yaml_save(file, defaults)
settings = yaml_load(file)
if settings.keys() != defaults.keys():
settings = {**defaults, **settings} # merge **defaults with **settings (prefer **settings)
yaml_save(file, settings) # save updated defaults
return yaml_load(file)
return settings
# Run below code on utils init -----------------------------------------------------------------------------------------

@ -48,7 +48,7 @@ def on_train_end(trainer):
# Upload final model and metrics with exponential standoff
LOGGER.info(f"{PREFIX}Training completed successfully ✅\n"
f"{PREFIX}Uploading final {session.model_id}")
session.upload_model(trainer.epoch, trainer.best, map=trainer.metrics['metrics/mAP50(B)'], final=True)
session.upload_model(trainer.epoch, trainer.best, map=trainer.metrics['metrics/mAP50-95(B)'], final=True)
session.alive = False # stop heartbeats
LOGGER.info(f"{PREFIX}View model at https://hub.ultralytics.com/models/{session.model_id} 🚀")

@ -201,7 +201,7 @@ def copy_attr(a, b, include=(), exclude=()):
setattr(a, k, v)
def intersect_state_dicts(da, db, exclude=()):
def intersect_dicts(da, db, exclude=()):
# Dictionary intersection of matching keys and shapes, omitting 'exclude' keys, using da values
return {k: v for k, v in da.items() if k in db and all(x not in k for x in exclude) and v.shape == db[k].shape}

Loading…
Cancel
Save