diff --git a/benchmarks.py b/benchmarks.py index b590ff63cb01..7b1bf60fe5ab 100644 --- a/benchmarks.py +++ b/benchmarks.py @@ -32,6 +32,10 @@ from pathlib import Path import pandas as pd +from ulralytics.yolo.utils.checks import check_yaml, print_args +from ultraltics.yolo.utils import LOGGER, file_size +from ultralytics.nn.tasks import attempt_load_weights +from ultralytics.yolo.utils.torch_utils import select_device FILE = Path(__file__).resolve() ROOT = FILE.parents[0] # YOLOv5 root directory @@ -40,12 +44,9 @@ # ROOT = ROOT.relative_to(Path.cwd()) # relative import export -from models.experimental import attempt_load from models.yolo import SegmentationModel from segment.val import run as val_seg from utils import notebook_init -from utils.general import LOGGER, check_yaml, file_size, print_args -from utils.torch_utils import select_device from val import run as val_det @@ -62,7 +63,7 @@ def run( ): y, t = [], time.time() device = select_device(device) - model_type = type(attempt_load(weights, fuse=False)) # DetectionModel, SegmentationModel, etc. + model_type = type(attempt_load_weights(weights, fuse=False)) # DetectionModel, SegmentationModel, etc. for i, (name, f, suffix, cpu, gpu) in export.export_formats().iterrows(): # index, (name, file, suffix, CPU, GPU) try: assert i not in (9, 10), 'inference not supported' # Edge TPU and TF.js are unsupported diff --git a/classify/predict.py b/classify/predict.py index 9b64810d4d63..942ee09592b2 100644 --- a/classify/predict.py +++ b/classify/predict.py @@ -36,6 +36,8 @@ import torch import torch.nn.functional as F +from ultralytics.yolo.utils.checks import check_imgsz, check_imshow, check_requirements, print_args +from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode, strip_optimizer FILE = Path(__file__).resolve() ROOT = FILE.parents[1] # YOLOv5 root directory @@ -46,10 +48,8 @@ from models.common import DetectMultiBackend from utils.augmentations import classify_transforms from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams -from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2, - increment_path, print_args, strip_optimizer) +from utils.general import LOGGER, Profile, check_file, colorstr, cv2, increment_path from utils.plots import Annotator -from utils.torch_utils import select_device, smart_inference_mode @smart_inference_mode() @@ -89,7 +89,7 @@ def run( device = select_device(device) model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) stride, names, pt = model.stride, model.names, model.pt - imgsz = check_img_size(imgsz, s=stride) # check image size + imgsz = check_imgsz(imgsz, s=stride) # check image size # Dataloader bs = 1 # batch_size diff --git a/classify/train.py b/classify/train.py index ecbea1d8c0de..72c71e5cda75 100644 --- a/classify/train.py +++ b/classify/train.py @@ -29,6 +29,9 @@ import torchvision from torch.cuda import amp from tqdm import tqdm +from ultralytics.nn.tasks import attempt_load_weights +from ultralytics.yolo.utils.torch_utils import (ModelEMA, de_parallel, model_info, select_device, + torch_distributed_zero_first) FILE = Path(__file__).resolve() ROOT = FILE.parents[1] # YOLOv5 root directory @@ -37,15 +40,13 @@ ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative from classify import val as validate -from models.experimental import attempt_load from models.yolo import ClassificationModel, DetectionModel from utils.dataloaders import create_classification_dataloader from utils.general import (DATASETS_DIR, LOGGER, TQDM_BAR_FORMAT, WorkingDirectory, check_git_info, check_git_status, check_requirements, colorstr, download, increment_path, init_seeds, print_args, yaml_save) from utils.loggers import GenericLogger from utils.plots import imshow_cls -from utils.torch_utils import (ModelEMA, de_parallel, model_info, reshape_classifier_output, select_device, smart_DDP, - smart_optimizer, smartCrossEntropyLoss, torch_distributed_zero_first) +from utils.torch_utils import reshape_classifier_output, smart_DDP, smart_optimizer, smartCrossEntropyLoss LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html RANK = int(os.getenv('RANK', -1)) @@ -108,7 +109,7 @@ def train(opt, device): # Model with torch_distributed_zero_first(LOCAL_RANK), WorkingDirectory(ROOT): if Path(opt.model).is_file() or opt.model.endswith('.pt'): - model = attempt_load(opt.model, device='cpu', fuse=False) + model = attempt_load_weights(opt.model, device='cpu', fuse=False) elif opt.model in torchvision.models.__dict__: # TorchVision models i.e. resnet50, efficientnet_b0 model = torchvision.models.__dict__[opt.model](weights='IMAGENET1K_V1' if pretrained else None) else: @@ -303,7 +304,7 @@ def main(opt): check_requirements(ROOT / 'requirements.txt') # DDP mode - device = select_device(opt.device, batch_size=opt.batch_size) + device = select_device(opt.device, batch=opt.batch_size) if LOCAL_RANK != -1: assert opt.batch_size != -1, 'AutoBatch is coming soon for classification, please pass a valid --batch-size' assert opt.batch_size % WORLD_SIZE == 0, f'--batch-size {opt.batch_size} must be multiple of WORLD_SIZE' diff --git a/classify/val.py b/classify/val.py index 4b92e9f105db..fba638cef0bf 100644 --- a/classify/val.py +++ b/classify/val.py @@ -27,6 +27,9 @@ import torch from tqdm import tqdm +from ultralytics.yolo.utils import colorstr +from ultralytics.yolo.utils.checks import check_imgsz, check_requirements, print_args +from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode FILE = Path(__file__).resolve() ROOT = FILE.parents[1] # YOLOv5 root directory @@ -36,9 +39,7 @@ from models.common import DetectMultiBackend from utils.dataloaders import create_classification_dataloader -from utils.general import (LOGGER, TQDM_BAR_FORMAT, Profile, check_img_size, check_requirements, colorstr, - increment_path, print_args) -from utils.torch_utils import select_device, smart_inference_mode +from utils.general import LOGGER, TQDM_BAR_FORMAT, Profile, increment_path @smart_inference_mode() @@ -67,7 +68,7 @@ def run( half &= device.type != 'cpu' # half precision only supported on CUDA model.half() if half else model.float() else: # called directly - device = select_device(device, batch_size=batch_size) + device = select_device(device, batch=batch_size) # Directories save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run @@ -76,7 +77,7 @@ def run( # Load model model = DetectMultiBackend(weights, device=device, dnn=dnn, fp16=half) stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine - imgsz = check_img_size(imgsz, s=stride) # check image size + imgsz = check_imgsz(imgsz, s=stride) # check image size half = model.fp16 # FP16 supported on limited backends with CUDA if engine: batch_size = model.batch_size diff --git a/detect.py b/detect.py index 216c9dbd5880..3609d1e0f2dc 100644 --- a/detect.py +++ b/detect.py @@ -35,6 +35,9 @@ from pathlib import Path import torch +from ultralyitcs.yolo.utils import colorstr +from ultralytics.yolo.utils.checks import check_imgsz, check_imshow, check_requirements, print_args +from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode FILE = Path(__file__).resolve() ROOT = FILE.parents[0] # YOLOv5 root directory @@ -44,10 +47,9 @@ from models.common import DetectMultiBackend from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams -from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2, - increment_path, non_max_suppression, print_args, scale_boxes, strip_optimizer, xyxy2xywh) +from utils.general import (LOGGER, Profile, check_file, cv2, increment_path, non_max_suppression, scale_boxes, + strip_optimizer, xyxy2xywh) from utils.plots import Annotator, colors, save_one_box -from utils.torch_utils import select_device, smart_inference_mode @smart_inference_mode() @@ -97,7 +99,7 @@ def run( device = select_device(device) model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) stride, names, pt = model.stride, model.names, model.pt - imgsz = check_img_size(imgsz, s=stride) # check image size + imgsz = check_imgsz(imgsz, s=stride) # check image size # Dataloader bs = 1 # batch_size diff --git a/export.py b/export.py index 9399f0bbd221..17b5285e862c 100644 --- a/export.py +++ b/export.py @@ -59,6 +59,10 @@ import pandas as pd import torch from torch.utils.mobile_optimizer import optimize_for_mobile +from ultraltics.yolo.utils import check_requirements, colorstr, file_size +from ultralytics.nn.tasks import attempt_load_weights +from ultralytics.yolo.utils.checks import check_img_size, check_imgsz, check_version, print_args +from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode FILE = Path(__file__).resolve() ROOT = FILE.parents[0] # YOLOv5 root directory @@ -67,12 +71,9 @@ if platform.system() != 'Windows': ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative -from models.experimental import attempt_load from models.yolo import ClassificationModel, Detect, DetectionModel, SegmentationModel from utils.dataloaders import LoadImages -from utils.general import (LOGGER, Profile, check_dataset, check_img_size, check_requirements, check_version, - check_yaml, colorstr, file_size, get_default_args, print_args, url2file, yaml_save) -from utils.torch_utils import select_device, smart_inference_mode +from utils.general import LOGGER, Profile, check_dataset, check_yaml, get_default_args, url2file, yaml_save MACOS = platform.system() == 'Darwin' # macOS environment @@ -733,7 +734,7 @@ def run( if half: assert device.type != 'cpu' or coreml, '--half only compatible with GPU export, i.e. use --device 0' assert not dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic but not both' - model = attempt_load(weights, device=device, inplace=True, fuse=True) # load FP32 model + model = attempt_load_weights(weights, device=device, inplace=True, fuse=True) # load FP32 model # Checks imgsz *= 2 if len(imgsz) == 1 else 1 # expand @@ -742,7 +743,7 @@ def run( # Input gs = int(max(model.stride)) # grid size (max stride) - imgsz = [check_img_size(x, gs) for x in imgsz] # verify img_size are gs-multiples + imgsz = [check_imgsz(x, gs) for x in imgsz] # verify img_size are gs-multiples im = torch.zeros(batch_size, 3, *imgsz).to(device) # image size(1,3,320,192) BCHW iDetection # Update model diff --git a/hubconf.py b/hubconf.py index f0192698fbe3..36573104b003 100644 --- a/hubconf.py +++ b/hubconf.py @@ -30,12 +30,13 @@ def _create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbo """ from pathlib import Path + from ultralytics.nn.tasks import attempt_load_weights + from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode + from models.common import AutoShape, DetectMultiBackend - from models.experimental import attempt_load from models.yolo import ClassificationModel, DetectionModel, SegmentationModel from utils.downloads import attempt_download from utils.general import LOGGER, ROOT, check_requirements, intersect_dicts, logging - from utils.torch_utils import select_device if not verbose: LOGGER.setLevel(logging.WARNING) @@ -57,7 +58,7 @@ def _create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbo else: model = AutoShape(model) # for file/URI/PIL/cv2/np inputs and NMS except Exception: - model = attempt_load(path, device=device, fuse=False) # arbitrary model + model = attempt_load_weights(path, device=device, fuse=False) # arbitrary model else: cfg = list((Path(__file__).parent / 'models').rglob(f'{path.stem}.yaml'))[0] # model.yaml path model = DetectionModel(cfg, channels, classes) # create model diff --git a/models/common.py b/models/common.py index a0ee085d05fc..6d90bf78c748 100644 --- a/models/common.py +++ b/models/common.py @@ -23,12 +23,14 @@ import torch.nn as nn from PIL import Image from torch.cuda import amp +from ultralytics.nn.tasks import attempt_load_weights +from ultralytics.yolo.utils import colorstr +from ultralytics.yolo.utils.checks import check_requirements, check_suffix, check_version from utils import TryExcept from utils.dataloaders import exif_transpose, letterbox -from utils.general import (LOGGER, ROOT, Profile, check_requirements, check_suffix, check_version, colorstr, - increment_path, is_jupyter, make_divisible, non_max_suppression, scale_boxes, xywh2xyxy, - xyxy2xywh, yaml_load) +from utils.general import (LOGGER, ROOT, Profile, increment_path, is_jupyter, make_divisible, non_max_suppression, + scale_boxes, xywh2xyxy, xyxy2xywh, yaml_load) from utils.plots import Annotator, colors, save_one_box from utils.torch_utils import copy_attr, smart_inference_mode @@ -328,7 +330,7 @@ def __init__(self, weights='yolov5s.pt', device=torch.device('cpu'), dnn=False, # TensorFlow Lite: *.tflite # TensorFlow Edge TPU: *_edgetpu.tflite # PaddlePaddle: *_paddle_model - from models.experimental import attempt_download, attempt_load # scoped to avoid circular import + from models.experimental import attempt_download # scoped to avoid circular import super().__init__() w = str(weights[0] if isinstance(weights, list) else weights) @@ -341,7 +343,10 @@ def __init__(self, weights='yolov5s.pt', device=torch.device('cpu'), dnn=False, w = attempt_download(w) # download if not local if pt: # PyTorch - model = attempt_load(weights if isinstance(weights, list) else w, device=device, inplace=True, fuse=fuse) + model = attempt_load_weights(weights if isinstance(weights, list) else w, + device=device, + inplace=True, + fuse=fuse) stride = max(int(model.stride.max()), 32) # model stride names = model.module.names if hasattr(model, 'module') else model.names # get class names model.half() if fp16 else model.float() diff --git a/models/experimental.py b/models/experimental.py index d60d1808da11..f8cca243cd56 100644 --- a/models/experimental.py +++ b/models/experimental.py @@ -68,44 +68,3 @@ def forward(self, x, augment=False, profile=False, visualize=False): # y = torch.stack(y).mean(0) # mean ensemble y = torch.cat(y, 1) # nms ensemble return y, None # inference, train output - - -def attempt_load(weights, device=None, inplace=True, fuse=True): - # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a - from models.yolo import Detect, Model - - model = Ensemble() - for w in weights if isinstance(weights, list) else [weights]: - ckpt = torch.load(attempt_download(w), map_location='cpu') # load - ckpt = (ckpt.get('ema') or ckpt['model']).to(device).float() # FP32 model - - # Model compatibility updates - if not hasattr(ckpt, 'stride'): - ckpt.stride = torch.tensor([32.]) - if hasattr(ckpt, 'names') and isinstance(ckpt.names, (list, tuple)): - ckpt.names = dict(enumerate(ckpt.names)) # convert to dict - - model.append(ckpt.fuse().eval() if fuse and hasattr(ckpt, 'fuse') else ckpt.eval()) # model in eval mode - - # Module compatibility updates - for m in model.modules(): - t = type(m) - if t in (nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU, Detect, Model): - m.inplace = inplace # torch 1.7.0 compatibility - if t is Detect and not isinstance(m.anchor_grid, list): - delattr(m, 'anchor_grid') - setattr(m, 'anchor_grid', [torch.zeros(1)] * m.nl) - elif t is nn.Upsample and not hasattr(m, 'recompute_scale_factor'): - m.recompute_scale_factor = None # torch 1.11.0 compatibility - - # Return model - if len(model) == 1: - return model[-1] - - # Return detection ensemble - print(f'Ensemble created with {weights}\n') - for k in 'names', 'nc', 'yaml': - setattr(model, k, getattr(model[0], k)) - model.stride = model[torch.argmax(torch.tensor([m.stride.max() for m in model])).int()].stride # max stride - assert all(model[0].nc == m.nc for m in model), f'Models have different class counts: {[m.nc for m in model]}' - return model diff --git a/models/tf.py b/models/tf.py index bc0a465d7edd..f39f7c2fd399 100644 --- a/models/tf.py +++ b/models/tf.py @@ -26,10 +26,11 @@ import torch import torch.nn as nn from tensorflow import keras +from ultralytics.nn.tasks import attempt_load_weights from models.common import (C3, SPP, SPPF, Bottleneck, BottleneckCSP, C3x, Concat, Conv, CrossConv, DWConv, DWConvTranspose2d, Focus, autopad) -from models.experimental import MixConv2d, attempt_load +from models.experimental import MixConv2d from models.yolo import Detect, Segment from utils.activations import SiLU from utils.general import LOGGER, make_divisible, print_args @@ -570,7 +571,7 @@ def run( ): # PyTorch model im = torch.zeros((batch_size, 3, *imgsz)) # BCHW image - model = attempt_load(weights, device=torch.device('cpu'), inplace=True, fuse=False) + model = attempt_load_weights(weights, device=torch.device('cpu'), inplace=True, fuse=False) _ = model(im) # inference model.info() diff --git a/models/yolo.py b/models/yolo.py index 18d2542bfb48..c5a3c620fc5a 100644 --- a/models/yolo.py +++ b/models/yolo.py @@ -14,6 +14,9 @@ from copy import deepcopy from pathlib import Path +from ultralytics.yolo.utils.checks import check_version, print_args +from ultralytics.yolo.utils.torch_utils import select_device + FILE = Path(__file__).resolve() ROOT = FILE.parents[1] # YOLOv5 root directory if str(ROOT) not in sys.path: @@ -24,10 +27,9 @@ from models.common import * from models.experimental import * from utils.autoanchor import check_anchor_order -from utils.general import LOGGER, check_version, check_yaml, make_divisible, print_args +from utils.general import LOGGER, check_yaml, make_divisible from utils.plots import feature_visualization -from utils.torch_utils import (fuse_conv_and_bn, initialize_weights, model_info, profile, scale_img, select_device, - time_sync) +from utils.torch_utils import fuse_conv_and_bn, initialize_weights, model_info, profile, scale_img, time_sync try: import thop # for FLOPs computation diff --git a/segment/predict.py b/segment/predict.py index 6a4d5eff3fc1..ec87967f5bb3 100644 --- a/segment/predict.py +++ b/segment/predict.py @@ -35,6 +35,8 @@ from pathlib import Path import torch +from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode, strip_optimizer +from ultrlalytics.yolo.utils.checks import check_imgsz, check_imshow, print_args FILE = Path(__file__).resolve() ROOT = FILE.parents[1] # YOLOv5 root directory @@ -44,12 +46,10 @@ from models.common import DetectMultiBackend from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams -from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2, - increment_path, non_max_suppression, print_args, scale_boxes, scale_segments, - strip_optimizer) +from utils.general import (LOGGER, Profile, check_file, colorstr, cv2, increment_path, non_max_suppression, scale_boxes, + scale_segments) from utils.plots import Annotator, colors, save_one_box from utils.segment.general import masks2segments, process_mask, process_mask_native -from utils.torch_utils import select_device, smart_inference_mode @smart_inference_mode() @@ -100,7 +100,7 @@ def run( device = select_device(device) model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) stride, names, pt = model.stride, model.names, model.pt - imgsz = check_img_size(imgsz, s=stride) # check image size + imgsz = check_imgsz(imgsz, s=stride) # check image size # Dataloader bs = 1 # batch_size diff --git a/segment/train.py b/segment/train.py index 5f1fa4a1e453..27e9bdc7e69d 100644 --- a/segment/train.py +++ b/segment/train.py @@ -33,6 +33,10 @@ import yaml from torch.optim import lr_scheduler from tqdm import tqdm +from ultralytics.nn.tasks import attempt_load_weights +from ultralytics.yolo.utils.autobatch import check_train_batch_size +from ultralytics.yolo.utils.checks import check_img_size, check_imgsz, check_requirements +from ultralytics.yolo.utils.torch_utils import de_parallel, strip_optimizer, torch_distributed_zero_first FILE = Path(__file__).resolve() ROOT = FILE.parents[1] # YOLOv5 root directory @@ -41,24 +45,20 @@ ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative import segment.val as validate # for end-of-epoch mAP -from models.experimental import attempt_load from models.yolo import SegmentationModel from utils.autoanchor import check_anchors -from utils.autobatch import check_train_batch_size from utils.callbacks import Callbacks from utils.downloads import attempt_download, is_url from utils.general import (LOGGER, TQDM_BAR_FORMAT, check_amp, check_dataset, check_file, check_git_info, - check_git_status, check_img_size, check_requirements, check_suffix, check_yaml, colorstr, - get_latest_run, increment_path, init_seeds, intersect_dicts, labels_to_class_weights, - labels_to_image_weights, one_cycle, print_args, print_mutation, strip_optimizer, yaml_save) + check_git_status, check_suffix, check_yaml, get_latest_run, init_seeds, intersect_dicts, + labels_to_class_weights, labels_to_image_weights, one_cycle, print_mutation, yaml_save) from utils.loggers import GenericLogger from utils.plots import plot_evolve, plot_labels from utils.segment.dataloaders import create_dataloader from utils.segment.loss import ComputeLoss from utils.segment.metrics import KEYS, fitness from utils.segment.plots import plot_images_and_masks, plot_results_with_masks -from utils.torch_utils import (EarlyStopping, ModelEMA, de_parallel, select_device, smart_DDP, smart_optimizer, - smart_resume, torch_distributed_zero_first) +from utils.torch_utils import EarlyStopping, ModelEMA, smart_DDP, smart_optimizer, smart_resume LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html RANK = int(os.getenv('RANK', -1)) @@ -134,7 +134,7 @@ def train(hyp, opt, device, callbacks): # hyp is path/to/hyp.yaml or hyp dictio # Image size gs = max(int(model.stride.max()), 32) # grid size (max stride) - imgsz = check_img_size(opt.imgsz, gs, floor=gs * 2) # verify imgsz is gs-multiple + imgsz = check_imgsz(opt.imgsz, gs, floor=gs * 2) # verify imgsz is gs-multiple # Batch size if RANK == -1 and batch_size == -1: # single-GPU only, estimate best batch size @@ -429,7 +429,7 @@ def train(hyp, opt, device, callbacks): # hyp is path/to/hyp.yaml or hyp dictio data_dict, batch_size=batch_size // WORLD_SIZE * 2, imgsz=imgsz, - model=attempt_load(f, device).half(), + model=attempt_load_weights(f, device).half(), iou_thres=0.65 if is_coco else 0.60, # best pycocotools at iou 0.65 single_cls=single_cls, dataloader=val_loader, @@ -540,7 +540,7 @@ def main(opt, callbacks=Callbacks()): opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok)) # DDP mode - device = select_device(opt.device, batch_size=opt.batch_size) + device = select_device(opt.device, batch=opt.batch_size) if LOCAL_RANK != -1: msg = 'is not compatible with YOLOv5 Multi-GPU DDP training' assert not opt.image_weights, f'--image-weights {msg}' diff --git a/segment/val.py b/segment/val.py index dc8081840e37..b280c2cc08bb 100644 --- a/segment/val.py +++ b/segment/val.py @@ -39,20 +39,21 @@ ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative import torch.nn.functional as F +from ultralytics.yolo.utils.torch_utils import de_parallel, select_device, smart_inference_mode +from ultralytis.yolo.utils.checks import check_img_size, check_imgsz, check_requirements, print_args from models.common import DetectMultiBackend from models.yolo import SegmentationModel from utils.callbacks import Callbacks -from utils.general import (LOGGER, NUM_THREADS, TQDM_BAR_FORMAT, Profile, check_dataset, check_img_size, - check_requirements, check_yaml, coco80_to_coco91_class, colorstr, increment_path, - non_max_suppression, print_args, scale_boxes, xywh2xyxy, xyxy2xywh) +from utils.general import (LOGGER, NUM_THREADS, TQDM_BAR_FORMAT, Profile, check_dataset, check_yaml, + coco80_to_coco91_class, colorstr, increment_path, non_max_suppression, scale_boxes, + xywh2xyxy, xyxy2xywh) from utils.metrics import ConfusionMatrix, box_iou from utils.plots import output_to_target, plot_val_study from utils.segment.dataloaders import create_dataloader from utils.segment.general import mask_iou, process_mask, process_mask_native, scale_image from utils.segment.metrics import Metrics, ap_per_class_box_and_mask from utils.segment.plots import plot_images_and_masks -from utils.torch_utils import de_parallel, select_device, smart_inference_mode def save_one_txt(predn, save_conf, shape, file): @@ -173,7 +174,7 @@ def run( model.half() if half else model.float() nm = de_parallel(model).model[-1].nm # number of masks else: # called directly - device = select_device(device, batch_size=batch_size) + device = select_device(device, batch=batch_size) # Directories save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run @@ -182,7 +183,7 @@ def run( # Load model model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine - imgsz = check_img_size(imgsz, s=stride) # check image size + imgsz = check_imgsz(imgsz, s=stride) # check image size half = model.fp16 # FP16 supported on limited backends with CUDA nm = de_parallel(model).model.model[-1].nm if isinstance(model, SegmentationModel) else 32 # number of masks if engine: diff --git a/train.py b/train.py index b5044deb9b5c..2f9b9b29f862 100644 --- a/train.py +++ b/train.py @@ -38,6 +38,12 @@ import yaml from torch.optim import lr_scheduler from tqdm import tqdm +from ultralytics.nn.tasks import attempt_load_weights +from ultralytics.yolo.utils import colorstr +from ultralytics.yolo.utils.autobatch import check_train_batch_size +from ultralytics.yolo.utils.checks import check_imgsz, check_requirements, check_suffix, print_args +from ultralytics.yolo.utils.torch_utils import (EarlyStopping, ModelEMA, de_parallel, select_device, strip_optimizer, + torch_distributed_zero_first) FILE = Path(__file__).resolve() ROOT = FILE.parents[0] # YOLOv5 root directory @@ -46,25 +52,21 @@ ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative import val as validate # for end-of-epoch mAP -from models.experimental import attempt_load from models.yolo import Model from utils.autoanchor import check_anchors -from utils.autobatch import check_train_batch_size from utils.callbacks import Callbacks from utils.dataloaders import create_dataloader from utils.downloads import attempt_download, is_url from utils.general import (LOGGER, TQDM_BAR_FORMAT, check_amp, check_dataset, check_file, check_git_info, - check_git_status, check_img_size, check_requirements, check_suffix, check_yaml, colorstr, - get_latest_run, increment_path, init_seeds, intersect_dicts, labels_to_class_weights, - labels_to_image_weights, methods, one_cycle, print_args, print_mutation, strip_optimizer, + check_git_status, check_yaml, get_latest_run, increment_path, init_seeds, intersect_dicts, + labels_to_class_weights, labels_to_image_weights, methods, one_cycle, print_mutation, yaml_save) from utils.loggers import Loggers from utils.loggers.comet.comet_utils import check_comet_resume from utils.loss import ComputeLoss from utils.metrics import fitness from utils.plots import plot_evolve -from utils.torch_utils import (EarlyStopping, ModelEMA, de_parallel, select_device, smart_DDP, smart_optimizer, - smart_resume, torch_distributed_zero_first) +from utils.torch_utils import smart_DDP, smart_optimizer, smart_resume LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html RANK = int(os.getenv('RANK', -1)) @@ -148,7 +150,7 @@ def train(hyp, opt, device, callbacks): # hyp is path/to/hyp.yaml or hyp dictio # Image size gs = max(int(model.stride.max()), 32) # grid size (max stride) - imgsz = check_img_size(opt.imgsz, gs, floor=gs * 2) # verify imgsz is gs-multiple + imgsz = check_imgsz(opt.imgsz, gs, floor=gs * 2) # verify imgsz is gs-multiple # Batch size if RANK == -1 and batch_size == -1: # single-GPU only, estimate best batch size @@ -420,7 +422,7 @@ def train(hyp, opt, device, callbacks): # hyp is path/to/hyp.yaml or hyp dictio data_dict, batch_size=batch_size // WORLD_SIZE * 2, imgsz=imgsz, - model=attempt_load(f, device).half(), + model=attempt_load_weights(f, device).half(), iou_thres=0.65 if is_coco else 0.60, # best pycocotools at iou 0.65 single_cls=single_cls, dataloader=val_loader, @@ -519,7 +521,7 @@ def main(opt, callbacks=Callbacks()): opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok)) # DDP mode - device = select_device(opt.device, batch_size=opt.batch_size) + device = select_device(opt.device, batch=opt.batch_size) if LOCAL_RANK != -1: msg = 'is not compatible with YOLOv5 Multi-GPU DDP training' assert not opt.image_weights, f'--image-weights {msg}' diff --git a/utils/__init__.py b/utils/__init__.py index 2a7652b52c1b..38765eed27b6 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -7,6 +7,9 @@ import platform import threading +from ultralytics.yolo.utils.checks import check_font, is_colab +from ultralytics.yolo.utils.torch_utils import select_device # imports + def emojis(str=''): # Return platform-dependent emoji-safe version of string diff --git a/utils/augmentations.py b/utils/augmentations.py index 52e2e346e36e..c76a91e0086c 100644 --- a/utils/augmentations.py +++ b/utils/augmentations.py @@ -11,8 +11,10 @@ import torch import torchvision.transforms as T import torchvision.transforms.functional as TF +from ultralytics.yolo.utils import colorstr +from ultralytics.yolo.utils.checks import check_version -from utils.general import LOGGER, check_version, colorstr, resample_segments, segment2box, xywhn2xyxy +from utils.general import LOGGER, resample_segments, segment2box, xywhn2xyxy from utils.metrics import bbox_ioa IMAGENET_MEAN = 0.485, 0.456, 0.406 # RGB mean diff --git a/utils/autobatch.py b/utils/autobatch.py deleted file mode 100644 index aa763b888462..000000000000 --- a/utils/autobatch.py +++ /dev/null @@ -1,72 +0,0 @@ -# YOLOv5 🚀 by Ultralytics, AGPL-3.0 license -""" -Auto-batch utils -""" - -from copy import deepcopy - -import numpy as np -import torch - -from utils.general import LOGGER, colorstr -from utils.torch_utils import profile - - -def check_train_batch_size(model, imgsz=640, amp=True): - # Check YOLOv5 training batch size - with torch.cuda.amp.autocast(amp): - return autobatch(deepcopy(model).train(), imgsz) # compute optimal batch size - - -def autobatch(model, imgsz=640, fraction=0.8, batch_size=16): - # Automatically estimate best YOLOv5 batch size to use `fraction` of available CUDA memory - # Usage: - # import torch - # from utils.autobatch import autobatch - # model = torch.hub.load('ultralytics/yolov5', 'yolov5s', autoshape=False) - # print(autobatch(model)) - - # Check device - prefix = colorstr('AutoBatch: ') - LOGGER.info(f'{prefix}Computing optimal batch size for --imgsz {imgsz}') - device = next(model.parameters()).device # get model device - if device.type == 'cpu': - LOGGER.info(f'{prefix}CUDA not detected, using default CPU batch-size {batch_size}') - return batch_size - if torch.backends.cudnn.benchmark: - LOGGER.info(f'{prefix} ⚠️ Requires torch.backends.cudnn.benchmark=False, using default batch-size {batch_size}') - return batch_size - - # Inspect CUDA memory - gb = 1 << 30 # bytes to GiB (1024 ** 3) - d = str(device).upper() # 'CUDA:0' - properties = torch.cuda.get_device_properties(device) # device properties - t = properties.total_memory / gb # GiB total - r = torch.cuda.memory_reserved(device) / gb # GiB reserved - a = torch.cuda.memory_allocated(device) / gb # GiB allocated - f = t - (r + a) # GiB free - LOGGER.info(f'{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free') - - # Profile batch sizes - batch_sizes = [1, 2, 4, 8, 16] - try: - img = [torch.empty(b, 3, imgsz, imgsz) for b in batch_sizes] - results = profile(img, model, n=3, device=device) - except Exception as e: - LOGGER.warning(f'{prefix}{e}') - - # Fit a solution - y = [x[2] for x in results if x] # memory [2] - p = np.polyfit(batch_sizes[:len(y)], y, deg=1) # first degree polynomial fit - b = int((f * fraction - p[1]) / p[0]) # y intercept (optimal batch size) - if None in results: # some sizes failed - i = results.index(None) # first fail index - if b >= batch_sizes[i]: # y intercept above failure point - b = batch_sizes[max(i - 1, 0)] # select prior safe point - if b < 1 or b > 1024: # b outside of safe range - b = batch_size - LOGGER.warning(f'{prefix}WARNING ⚠️ CUDA anomaly detected, recommend restart environment and retry command.') - - fraction = (np.polyval(p, b) + r + a) / t # actual fraction predicted - LOGGER.info(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%) ✅') - return b diff --git a/utils/dataloaders.py b/utils/dataloaders.py index 26201c3c78fc..24a76526963b 100644 --- a/utils/dataloaders.py +++ b/utils/dataloaders.py @@ -27,12 +27,13 @@ from PIL import ExifTags, Image, ImageOps from torch.utils.data import DataLoader, Dataset, dataloader, distributed from tqdm import tqdm +from ultralytics.yolo.utils.checks import check_requirements from utils.augmentations import (Albumentations, augment_hsv, classify_albumentations, classify_transforms, copy_paste, letterbox, mixup, random_perspective) -from utils.general import (DATASETS_DIR, LOGGER, NUM_THREADS, TQDM_BAR_FORMAT, check_dataset, check_requirements, - check_yaml, clean_str, cv2, is_colab, is_kaggle, segments2boxes, unzip_file, xyn2xy, - xywh2xyxy, xywhn2xyxy, xyxy2xywhn) +from utils.general import (DATASETS_DIR, LOGGER, NUM_THREADS, TQDM_BAR_FORMAT, check_dataset, check_yaml, clean_str, + cv2, is_colab, is_kaggle, segments2boxes, unzip_file, xyn2xy, xywh2xyxy, xywhn2xyxy, + xyxy2xywhn) from utils.torch_utils import torch_distributed_zero_first # Parameters diff --git a/utils/general.py b/utils/general.py index e95b07486619..946bd34b9d67 100644 --- a/utils/general.py +++ b/utils/general.py @@ -35,7 +35,8 @@ import torch import torchvision import yaml -from ultralytics.yolo.utils.checks import check_requirements +from ultralytics.yolo.utils import colorstr +from ultralytics.yolo.utils.checks import check_requirements, check_version from utils import TryExcept, emojis from utils.downloads import curl_download, gsutil_getsize @@ -230,21 +231,6 @@ def methods(instance): return [f for f in dir(instance) if callable(getattr(instance, f)) and not f.startswith('__')] -def print_args(args: Optional[dict] = None, show_file=True, show_func=False): - # Print function arguments (optional args dict) - x = inspect.currentframe().f_back # previous frame - file, _, func, _, _ = inspect.getframeinfo(x) - if args is None: # get args automatically - args, _, _, frm = inspect.getargvalues(x) - args = {k: v for k, v in frm.items() if k in args} - try: - file = Path(file).resolve().relative_to(ROOT).with_suffix('') - except ValueError: - file = Path(file).stem - s = (f'{file}: ' if show_file else '') + (f'{func}: ' if show_func else '') - LOGGER.info(colorstr(s) + ', '.join(f'{k}={v}' for k, v in args.items())) - - def init_seeds(seed=0, deterministic=False): # Initialize random number generator (RNG) seeds https://pytorch.org/docs/stable/notes/randomness.html random.seed(seed) @@ -289,16 +275,9 @@ def file_date(path=__file__): return f'{t.year}-{t.month}-{t.day}' -def file_size(path): - # Return file/dir size (MB) - mb = 1 << 20 # bytes to MiB (1024 ** 2) - path = Path(path) - if path.is_file(): - return path.stat().st_size / mb - elif path.is_dir(): - return sum(f.stat().st_size for f in path.glob('**/*') if f.is_file()) / mb - else: - return 0.0 +def check_yaml(file, suffix=('.yaml', '.yml')): + # Search/download YAML file (if necessary) and return path, checking suffix + return check_file(file, suffix) def check_online(): @@ -376,46 +355,6 @@ def check_python(minimum='3.7.0'): check_version(platform.python_version(), minimum, name='Python ', hard=True) -def check_version(current='0.0.0', minimum='0.0.0', name='version ', pinned=False, hard=False, verbose=False): - # Check version vs. required version - current, minimum = (pkg.parse_version(x) for x in (current, minimum)) - result = (current == minimum) if pinned else (current >= minimum) # bool - s = f'WARNING ⚠️ {name}{minimum} is required by YOLOv5, but {name}{current} is currently installed' # string - if hard: - assert result, emojis(s) # assert min requirements met - if verbose and not result: - LOGGER.warning(s) - return result - - -def check_img_size(imgsz, s=32, floor=0): - # Verify image size is a multiple of stride s in each dimension - if isinstance(imgsz, int): # integer i.e. img_size=640 - new_size = max(make_divisible(imgsz, int(s)), floor) - else: # list i.e. img_size=[640, 480] - imgsz = list(imgsz) # convert to list if tuple - new_size = [max(make_divisible(x, int(s)), floor) for x in imgsz] - if new_size != imgsz: - LOGGER.warning(f'WARNING ⚠️ --img-size {imgsz} must be multiple of max stride {s}, updating to {new_size}') - return new_size - - -def check_imshow(warn=False): - # Check if environment supports image displays - try: - assert not is_jupyter() - assert not is_docker() - cv2.imshow('test', np.zeros((1, 1, 3))) - cv2.waitKey(1) - cv2.destroyAllWindows() - cv2.waitKey(1) - return True - except Exception as e: - if warn: - LOGGER.warning(f'WARNING ⚠️ Environment does not support cv2.imshow() or PIL Image.show()\n{e}') - return False - - def check_suffix(file='yolov5s.pt', suffix=('.pt',), msg=''): # Check file(s) for acceptable suffix if file and suffix: @@ -427,11 +366,6 @@ def check_suffix(file='yolov5s.pt', suffix=('.pt',), msg=''): assert s in suffix, f'{msg}{f} acceptable suffix is {suffix}' -def check_yaml(file, suffix=('.yaml', '.yml')): - # Search/download YAML file (if necessary) and return path, checking suffix - return check_file(file, suffix) - - def check_file(file, suffix=''): # Search/download file (if necessary) and return path check_suffix(file, suffix) # optional @@ -655,32 +589,6 @@ def one_cycle(y1=0.0, y2=1.0, steps=100): return lambda x: ((1 - math.cos(x * math.pi / steps)) / 2) * (y2 - y1) + y1 -def colorstr(*input): - # Colors a string https://en.wikipedia.org/wiki/ANSI_escape_code, i.e. colorstr('blue', 'hello world') - *args, string = input if len(input) > 1 else ('blue', 'bold', input[0]) # color arguments, string - colors = { - 'black': '\033[30m', # basic colors - 'red': '\033[31m', - 'green': '\033[32m', - 'yellow': '\033[33m', - 'blue': '\033[34m', - 'magenta': '\033[35m', - 'cyan': '\033[36m', - 'white': '\033[37m', - 'bright_black': '\033[90m', # bright colors - 'bright_red': '\033[91m', - 'bright_green': '\033[92m', - 'bright_yellow': '\033[93m', - 'bright_blue': '\033[94m', - 'bright_magenta': '\033[95m', - 'bright_cyan': '\033[96m', - 'bright_white': '\033[97m', - 'end': '\033[0m', # misc - 'bold': '\033[1m', - 'underline': '\033[4m'} - return ''.join(colors[x] for x in args) + f'{string}' + colors['end'] - - def labels_to_class_weights(labels, nc=80): # Get class weights (inverse frequency) from training labels if labels[0] is None: # no labels loaded diff --git a/utils/loggers/comet/hpo.py b/utils/loggers/comet/hpo.py index fc49115c1358..1d8e7d28f0ca 100644 --- a/utils/loggers/comet/hpo.py +++ b/utils/loggers/comet/hpo.py @@ -6,6 +6,7 @@ from pathlib import Path import comet_ml +from ultralytics.yolo.utils.torch_utils import select_device logger = logging.getLogger(__name__) @@ -17,7 +18,6 @@ from train import train from utils.callbacks import Callbacks from utils.general import increment_path -from utils.torch_utils import select_device # Project Configuration config = comet_ml.config.get_config() @@ -87,7 +87,7 @@ def run(parameters, opt): opt.batch_size = parameters.get('batch_size') opt.epochs = parameters.get('epochs') - device = select_device(opt.device, batch_size=opt.batch_size) + device = select_device(opt.device, batch=opt.batch_size) train(hyp_dict, opt, device, callbacks=Callbacks()) diff --git a/utils/torch_utils.py b/utils/torch_utils.py index d9e060ab99df..2f82e76cb491 100644 --- a/utils/torch_utils.py +++ b/utils/torch_utils.py @@ -18,8 +18,9 @@ import torch.nn as nn import torch.nn.functional as F from torch.nn.parallel import DistributedDataParallel as DDP - -from utils.general import LOGGER, check_version, colorstr, file_date, git_describe +from ultralytics.yolo.utils import LOGGER, colorstr +from ultralytics.yolo.utils.checks import check_version +from ultralytics.yolo.utils.torch_utils import select_device LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) # https://pytorch.org/docs/stable/elastic/run.html RANK = int(os.getenv('RANK', -1)) @@ -105,42 +106,6 @@ def device_count(): return 0 -def select_device(device='', batch_size=0, newline=True): - # device = None or 'cpu' or 0 or '0' or '0,1,2,3' - s = f'YOLOv5 🚀 {git_describe() or file_date()} Python-{platform.python_version()} torch-{torch.__version__} ' - device = str(device).strip().lower().replace('cuda:', '').replace('none', '') # to string, 'cuda:0' to '0' - cpu = device == 'cpu' - mps = device == 'mps' # Apple Metal Performance Shaders (MPS) - if cpu or mps: - os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # force torch.cuda.is_available() = False - elif device: # non-cpu device requested - os.environ['CUDA_VISIBLE_DEVICES'] = device # set environment variable - must be before assert is_available() - assert torch.cuda.is_available() and torch.cuda.device_count() >= len(device.replace(',', '')), \ - f"Invalid CUDA '--device {device}' requested, use '--device cpu' or pass valid CUDA device(s)" - - if not cpu and not mps and torch.cuda.is_available(): # prefer GPU if available - devices = device.split(',') if device else '0' # range(torch.cuda.device_count()) # i.e. 0,1,6,7 - n = len(devices) # device count - if n > 1 and batch_size > 0: # check batch_size is divisible by device_count - assert batch_size % n == 0, f'batch-size {batch_size} not multiple of GPU count {n}' - space = ' ' * (len(s) + 1) - for i, d in enumerate(devices): - p = torch.cuda.get_device_properties(i) - s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / (1 << 20):.0f}MiB)\n" # bytes to MB - arg = 'cuda:0' - elif mps and getattr(torch, 'has_mps', False) and torch.backends.mps.is_available(): # prefer MPS if available - s += 'MPS\n' - arg = 'mps' - else: # revert to CPU - s += 'CPU\n' - arg = 'cpu' - - if not newline: - s = s.rstrip() - LOGGER.info(s) - return torch.device(arg) - - def time_sync(): # PyTorch-accurate time if torch.cuda.is_available(): diff --git a/val.py b/val.py index 71268651d29b..d53adb7fa4b3 100644 --- a/val.py +++ b/val.py @@ -29,6 +29,8 @@ import numpy as np import torch from tqdm import tqdm +from ultralytics.yolo.utils.checks import check_imgsz, check_requirements, print_args +from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode FILE = Path(__file__).resolve() ROOT = FILE.parents[0] # YOLOv5 root directory @@ -39,12 +41,10 @@ from models.common import DetectMultiBackend from utils.callbacks import Callbacks from utils.dataloaders import create_dataloader -from utils.general import (LOGGER, TQDM_BAR_FORMAT, Profile, check_dataset, check_img_size, check_requirements, - check_yaml, coco80_to_coco91_class, colorstr, increment_path, non_max_suppression, - print_args, scale_boxes, xywh2xyxy, xyxy2xywh) +from utils.general import (LOGGER, TQDM_BAR_FORMAT, Profile, check_dataset, check_yaml, coco80_to_coco91_class, + colorstr, increment_path, non_max_suppression, scale_boxes, xywh2xyxy, xyxy2xywh) from utils.metrics import ConfusionMatrix, ap_per_class, box_iou from utils.plots import output_to_target, plot_images, plot_val_study -from utils.torch_utils import select_device, smart_inference_mode def save_one_txt(predn, save_conf, shape, file): @@ -133,7 +133,7 @@ def run( half &= device.type != 'cpu' # half precision only supported on CUDA model.half() if half else model.float() else: # called directly - device = select_device(device, batch_size=batch_size) + device = select_device(device, batch=batch_size) # Directories save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run @@ -142,7 +142,7 @@ def run( # Load model model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine - imgsz = check_img_size(imgsz, s=stride) # check image size + imgsz = check_imgsz(imgsz, s=stride) # check image size half = model.fp16 # FP16 supported on limited backends with CUDA if engine: batch_size = model.batch_size