From 172cef2d20d8cc0f29f0ce8c732b3ed9b547e58f Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Thu, 5 Jan 2023 00:20:54 +0100 Subject: [PATCH] CoreML NMS and half fixes (#143) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- README.md | 33 ++++++- tests/check_flops.py | 15 --- tests/data/dataloader/hyp_test.yaml | 29 ------ tests/data/dataloader/yolodetection.py | 85 ---------------- tests/data/dataloader/yolopose.py | 125 ------------------------ tests/data/dataloader/yolosegment.py | 85 ---------------- tests/functional/test_loaders.py | 32 ------ tests/{tests_cli.py => test_cli.py} | 14 ++- tests/{tests.py => test_python.py} | 11 ++- ultralytics/nn/tasks.py | 2 +- ultralytics/yolo/data/utils.py | 2 +- ultralytics/yolo/engine/exporter.py | 31 +++--- ultralytics/yolo/engine/model.py | 4 +- ultralytics/yolo/engine/validator.py | 3 +- ultralytics/yolo/utils/__init__.py | 28 ++++-- ultralytics/yolo/utils/callbacks/hub.py | 8 +- ultralytics/yolo/utils/checks.py | 2 +- ultralytics/yolo/v8/detect/val.py | 2 +- 18 files changed, 96 insertions(+), 415 deletions(-) delete mode 100644 tests/check_flops.py delete mode 100644 tests/data/dataloader/hyp_test.yaml delete mode 100644 tests/data/dataloader/yolodetection.py delete mode 100644 tests/data/dataloader/yolopose.py delete mode 100644 tests/data/dataloader/yolosegment.py delete mode 100644 tests/functional/test_loaders.py rename tests/{tests_cli.py => test_cli.py} (71%) rename tests/{tests.py => test_python.py} (91%) diff --git a/README.md b/README.md index d4993b6..ac3ecaa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Ultralytics CI](https://github.com/ultralytics/ultralytics/actions/workflows/ci.yaml/badge.svg)](https://github.com/ultralytics/ultralytics/actions/workflows/ci.yaml) -### Install +## Install ```bash pip install ultralytics @@ -38,10 +38,39 @@ model = YOLO("yolov8n.yaml") # create a new model from scratch model = YOLO( "yolov8n.pt" ) # load a pretrained model (recommended for best training results) -results = model.train(data="coco128.yaml", epochs=100, imgsz=640, ...) +results = model.train(data="coco128.yaml", epochs=100, imgsz=640) results = model.val() results = model.predict(source="bus.jpg") success = model.export(format="onnx") ``` +## Models + +| Model | size
(pixels) | mAPval
50-95 | Speed
CPU
(ms) | Speed
T4 GPU
(ms) | params
(M) | FLOPs
(B) | +| ------------------------------------------------------------------------------------------------ | --------------------- | -------------------- | ------------------------- | ---------------------------- | ------------------ | ----------------- | +| [YOLOv5n](https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5n.pt) | 640 | 28.0 | - | - | **1.9** | **4.5** | +| [YOLOv6n](url) | 640 | 35.9 | - | - | 4.3 | 11.1 | +| **[YOLOv8n](url)** | 640 | **37.5** | - | - | 3.2 | 8.9 | +| | | | | | | | +| [YOLOv5s](https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5s.pt) | 640 | 37.4 | - | - | 7.2 | 16.5 | +| [YOLOv6s](url) | 640 | 43.5 | - | - | 17.2 | 44.2 | +| **[YOLOv8s](url)** | 640 | **44.7** | - | - | 11.2 | 28.8 | +| | | | | | | | +| [YOLOv5m](https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5m.pt) | 640 | 45.4 | - | - | 21.2 | 49.0 | +| [YOLOv6m](url) | 640 | 49.5 | - | - | 34.3 | 82.2 | +| **[YOLOv8m](url)** | 640 | **50.3** | - | - | 25.9 | 79.3 | +| | | | | | | | +| [YOLOv5l](https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5l.pt) | 640 | 49.0 | - | - | 46.5 | 109.1 | +| [YOLOv6l](url) | 640 | 52.5 | - | - | 58.5 | 144.0 | +| [YOLOv7](url) | 640 | 51.2 | - | - | 36.9 | 104.7 | +| **[YOLOv8l](url)** | 640 | **52.8** | - | - | 43.7 | 165.7 | +| | | | | | | | +| [YOLOv5x](https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5x.pt) | 640 | 50.7 | - | - | 86.7 | 205.7 | +| [YOLOv7-X](url) | 640 | 52.9 | - | - | 71.3 | 189.9 | +| **[YOLOv8x](url)** | 640 | **53.7** | - | - | 68.2 | 258.5 | +| | | | | | | | +| [YOLOv5x6](https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5x6.pt) | 1280 | 55.0 | - | - | 140.7 | 839.2 | +| [YOLOv7-E6E](url) | 1280 | 56.8 | - | - | 151.7 | 843.2 | +| **[YOLOv8x6](https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5x6.pt)**
+TTA | 1280 | -
- | -
- | -
- | 97.4 | 1047.2
- | + If you're looking to modify YOLO for R&D or to build on top of it, refer to [Using Trainer](<>) Guide on our docs. diff --git a/tests/check_flops.py b/tests/check_flops.py deleted file mode 100644 index fd2bbd3..0000000 --- a/tests/check_flops.py +++ /dev/null @@ -1,15 +0,0 @@ -from ultralytics import YOLO -from ultralytics.yolo.utils import ROOT - -if __name__ == "__main__": - for m in list((ROOT / 'yolo/v8/models').rglob('*.yaml')): - try: - YOLO(m.name, verbose=True) - except Exception as e: - print(f'ERROR for {m}: {e}') - - # n vs n-seg: 8.9GFLOPs vs 12.8GFLOPs, 3.16M vs 3.6M. ch[0] // 4 (11.9GFLOPs, 3.39M) - # s vs s-seg: 28.8GFLOPs vs 44.4GFLOPs, 11.1M vs 12.9M. ch[0] // 4 (39.5GFLOPs, 11.7M) - # m vs m-seg: 79.3GFLOPs vs 113.8GFLOPs, 25.9M vs 29.5M. ch[0] // 4 (103.GFLOPs, 27.1M) - # l vs l-seg: 165.7GFLOPs vs 226.3GFLOPs, 43.7M vs 49.6M. ch[0] // 4 (207GFLOPs, 45.7M) - # x vs x-seg: 258.5GFLOPs vs 353.0GFLOPs, 68.3M vs 77.5M. ch[0] // 4 (324GFLOPs, 71.4M) diff --git a/tests/data/dataloader/hyp_test.yaml b/tests/data/dataloader/hyp_test.yaml deleted file mode 100644 index de6ae73..0000000 --- a/tests/data/dataloader/hyp_test.yaml +++ /dev/null @@ -1,29 +0,0 @@ -lr0: 0.001 # initial learning rate (SGD=1E-2, Adam=1E-3) -lrf: 0.01 # final OneCycleLR learning rate (lr0 * lrf) -momentum: 0.937 # SGD momentum/Adam beta1 -weight_decay: 0.0005 # optimizer weight decay 5e-4 -warmup_epochs: 3.0 # warmup epochs (fractions ok) -warmup_momentum: 0.8 # warmup initial momentum -warmup_bias_lr: 0.1 # warmup initial bias lr -box: 0.05 # box loss gain -cls: 0.5 # cls loss gain -cls_pw: 1.0 # cls BCELoss positive_weight -obj: 1.0 # obj loss gain (scale with pixels) -obj_pw: 1.0 # obj BCELoss positive_weight -iou_t: 0.20 # IoU training threshold -anchor_t: 4.0 # anchor-multiple threshold -# anchors: 3 # anchors per output layer (0 to ignore) -fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) -hsv_h: 0.015 # image HSV-Hue augmentation (fraction) -hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) -hsv_v: 0.4 # image HSV-Value augmentation (fraction) -degrees: 0.0 # image rotation (+/- deg) -translate: 0.1 # image translation (+/- fraction) -scale: 0.5 # image scale (+/- gain) -shear: 0.0 # image shear (+/- deg) -perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 -flipud: 0.0 # image flip up-down (probability) -fliplr: 0.5 # image flip left-right (probability) -mosaic: 1.0 # image mosaic (probability) -mixup: 0.0 # image mixup (probability) -copy_paste: 0.5 # segment copy-paste (probability) diff --git a/tests/data/dataloader/yolodetection.py b/tests/data/dataloader/yolodetection.py deleted file mode 100644 index eb03ac3..0000000 --- a/tests/data/dataloader/yolodetection.py +++ /dev/null @@ -1,85 +0,0 @@ -import cv2 -import hydra - -from ultralytics.yolo.data import build_dataloader -from ultralytics.yolo.utils import DEFAULT_CONFIG -from ultralytics.yolo.utils.plotting import plot_images - - -class Colors: - # Ultralytics color palette https://ultralytics.com/ - def __init__(self): - # hex = matplotlib.colors.TABLEAU_COLORS.values() - hexs = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB', - '2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7') - self.palette = [self.hex2rgb(f'#{c}') for c in hexs] - self.n = len(self.palette) - - def __call__(self, i, bgr=False): - c = self.palette[int(i) % self.n] - return (c[2], c[1], c[0]) if bgr else c - - @staticmethod - def hex2rgb(h): # rgb order (PIL) - return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)) - - -colors = Colors() # create instance for 'from utils.plots import colors' - - -def plot_one_box(x, img, color=None, label=None, line_thickness=None): - import random - - # Plots one bounding box on image img - tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness - color = color or [random.randint(0, 255) for _ in range(3)] - c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) - cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA) - if label: - tf = max(tl - 1, 1) # font thickness - t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] - c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 - cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled - cv2.putText( - img, - label, - (c1[0], c1[1] - 2), - 0, - tl / 3, - [225, 255, 255], - thickness=tf, - lineType=cv2.LINE_AA, - ) - - -@hydra.main(version_base=None, config_path=str(DEFAULT_CONFIG.parent), config_name=DEFAULT_CONFIG.name) -def test(cfg): - cfg.task = "detect" - cfg.mode = "train" - dataloader, _ = build_dataloader( - cfg=cfg, - batch_size=4, - img_path="/d/dataset/COCO/coco128-seg/images", - stride=32, - label_path=None, - mode=cfg.mode, - ) - - for d in dataloader: - images = d["img"] - cls = d["cls"].squeeze(-1) - bboxes = d["bboxes"] - paths = d["im_file"] - batch_idx = d["batch_idx"] - result = plot_images(images, batch_idx, cls, bboxes, paths=paths) - - cv2.imshow("p", result) - if cv2.waitKey(0) == ord("q"): - break - - -if __name__ == "__main__": - test() - # test(augment=True, rect=False) - # test(augment=False, rect=True) - # test(augment=False, rect=False) diff --git a/tests/data/dataloader/yolopose.py b/tests/data/dataloader/yolopose.py deleted file mode 100644 index e56b3bb..0000000 --- a/tests/data/dataloader/yolopose.py +++ /dev/null @@ -1,125 +0,0 @@ -import cv2 -import numpy as np -from omegaconf import OmegaConf - -from ultralytics.yolo.data import build_dataloader - - -class Colors: - # Ultralytics color palette https://ultralytics.com/ - def __init__(self): - # hex = matplotlib.colors.TABLEAU_COLORS.values() - hexs = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB', - '2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7') - self.palette = [self.hex2rgb(f'#{c}') for c in hexs] - self.n = len(self.palette) - - def __call__(self, i, bgr=False): - c = self.palette[int(i) % self.n] - return (c[2], c[1], c[0]) if bgr else c - - @staticmethod - def hex2rgb(h): # rgb order (PIL) - return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)) - - -colors = Colors() # create instance for 'from utils.plots import colors' - - -def plot_one_box(x, img, keypoints=None, color=None, label=None, line_thickness=None): - import random - - # Plots one bounding box on image img - tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness - color = color or [random.randint(0, 255) for _ in range(3)] - c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) - cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA) - if label: - tf = max(tl - 1, 1) # font thickness - t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] - c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 - cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled - cv2.putText( - img, - label, - (c1[0], c1[1] - 2), - 0, - tl / 3, - [225, 255, 255], - thickness=tf, - lineType=cv2.LINE_AA, - ) - if keypoints is not None: - plot_keypoint(img, keypoints, color, tl) - - -def plot_keypoint(img, keypoints, color, tl): - num_l = len(keypoints) - # clors = [(255, 0, 0),(0, 255, 0),(0, 0, 255),(255, 255, 0),(0, 255, 255)] - # clors = [[random.randint(0, 255) for _ in range(3)] for _ in range(num_l)] - for i in range(num_l): - point_x = int(keypoints[i][0]) - point_y = int(keypoints[i][1]) - cv2.circle(img, (point_x, point_y), tl + 3, color, -1) - - -with open("ultralytics/tests/data/dataloader/hyp_test.yaml") as f: - hyp = OmegaConf.load(f) - - -def test(augment, rect): - dataloader, _ = build_dataloader( - img_path="/d/dataset/COCO/images/val2017", - imgsz=640, - label_path=None, - cache=False, - hyp=hyp, - augment=augment, - prefix="", - rect=rect, - batch_size=4, - stride=32, - pad=0.5, - use_segments=False, - use_keypoints=True, - ) - - for d in dataloader: - idx = 1 # show which image inside one batch - img = d["img"][idx].numpy() - img = np.ascontiguousarray(img.transpose(1, 2, 0)) - ih, iw = img.shape[:2] - # print(img.shape) - bidx = d["batch_idx"] - cls = d["cls"][bidx == idx].numpy() - bboxes = d["bboxes"][bidx == idx].numpy() - bboxes[:, [0, 2]] *= iw - bboxes[:, [1, 3]] *= ih - keypoints = d["keypoints"][bidx == idx] - keypoints[..., 0] *= iw - keypoints[..., 1] *= ih - # print(keypoints, keypoints.shape) - # print(d["im_file"]) - - for i, b in enumerate(bboxes): - x, y, w, h = b - x1 = x - w / 2 - x2 = x + w / 2 - y1 = y - h / 2 - y2 = y + h / 2 - c = int(cls[i][0]) - # print(x1, y1, x2, y2) - plot_one_box([int(x1), int(y1), int(x2), int(y2)], - img, - keypoints=keypoints[i], - label=f"{c}", - color=colors(c)) - cv2.imshow("p", img) - if cv2.waitKey(0) == ord("q"): - break - - -if __name__ == "__main__": - test(augment=True, rect=False) - test(augment=False, rect=True) - test(augment=False, rect=False) diff --git a/tests/data/dataloader/yolosegment.py b/tests/data/dataloader/yolosegment.py deleted file mode 100644 index 8daf406..0000000 --- a/tests/data/dataloader/yolosegment.py +++ /dev/null @@ -1,85 +0,0 @@ -import cv2 -import hydra - -from ultralytics.yolo.data import build_dataloader -from ultralytics.yolo.utils import DEFAULT_CONFIG -from ultralytics.yolo.utils.plotting import plot_images - - -class Colors: - # Ultralytics color palette https://ultralytics.com/ - def __init__(self): - # hex = matplotlib.colors.TABLEAU_COLORS.values() - hexs = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB', - '2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7') - self.palette = [self.hex2rgb(f'#{c}') for c in hexs] - self.n = len(self.palette) - - def __call__(self, i, bgr=False): - c = self.palette[int(i) % self.n] - return (c[2], c[1], c[0]) if bgr else c - - @staticmethod - def hex2rgb(h): # rgb order (PIL) - return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)) - - -colors = Colors() # create instance for 'from utils.plots import colors' - - -def plot_one_box(x, img, color=None, label=None, line_thickness=None): - import random - - # Plots one bounding box on image img - tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness - color = color or [random.randint(0, 255) for _ in range(3)] - c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) - cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA) - if label: - tf = max(tl - 1, 1) # font thickness - t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] - c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 - cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled - cv2.putText( - img, - label, - (c1[0], c1[1] - 2), - 0, - tl / 3, - [225, 255, 255], - thickness=tf, - lineType=cv2.LINE_AA, - ) - - -@hydra.main(version_base=None, config_path=str(DEFAULT_CONFIG.parent), config_name=DEFAULT_CONFIG.name) -def test(cfg): - cfg.task = "segment" - cfg.mode = "train" - dataloader, _ = build_dataloader( - cfg=cfg, - batch_size=4, - img_path="/d/dataset/COCO/coco128-seg/images", - stride=32, - label_path=None, - mode=cfg.mode, - ) - - for d in dataloader: - images = d["img"] - masks = d["masks"] - cls = d["cls"].squeeze(-1) - bboxes = d["bboxes"] - paths = d["im_file"] - batch_idx = d["batch_idx"] - result = plot_images(images, batch_idx, cls, bboxes, masks, paths=paths) - cv2.imshow("p", result) - if cv2.waitKey(0) == ord("q"): - break - - -if __name__ == "__main__": - test() - # test(augment=True, rect=False) - # test(augment=False, rect=True) - # test(augment=False, rect=False) diff --git a/tests/functional/test_loaders.py b/tests/functional/test_loaders.py deleted file mode 100644 index 84b0d76..0000000 --- a/tests/functional/test_loaders.py +++ /dev/null @@ -1,32 +0,0 @@ -from ultralytics.nn.tasks import DetectionModel -from ultralytics.yolo.utils.checks import check_yaml - - -def test_model_parser(): - cfg = check_yaml("yolov8n.yaml") # check YAML - - # Create model - model = DetectionModel(cfg) - model.info() - ''' - # Options - if opt.line_profile: # profile layer by layer - model(im, profile=True) - - elif opt.profile: # profile forward-backward - results = profile(input=im, ops=[model], n=3) - - elif opt.test: # test all models - for cfg in Path(ROOT / 'models').rglob('yolo*.yaml'): - try: - _ = Model(cfg) - except Exception as e: - print(f'Error in {cfg}: {e}') - - else: # report fused model summary - model.fuse() - ''' - - -if __name__ == "__main__": - test_model_parser() diff --git a/tests/tests_cli.py b/tests/test_cli.py similarity index 71% rename from tests/tests_cli.py rename to tests/test_cli.py index b6b053c..9687ce5 100644 --- a/tests/tests_cli.py +++ b/tests/test_cli.py @@ -1,6 +1,10 @@ import os +from pathlib import Path -from ultralytics.yolo.utils import ROOT +from ultralytics.yolo.utils import ROOT, SETTINGS + +MODEL = Path(SETTINGS['weights_dir']) / 'yolov8n.pt' +CFG = 'yolov8n.yaml' def test_checks(): @@ -9,7 +13,7 @@ def test_checks(): # Train checks --------------------------------------------------------------------------------------------------------- def test_train_detect(): - os.system('yolo mode=train task=detect model=yolov8n.yaml data=coco128.yaml imgsz=32 epochs=1') + os.system(f'yolo mode=train task=detect model={MODEL} data=coco128.yaml imgsz=32 epochs=1') def test_train_segment(): @@ -22,7 +26,7 @@ def test_train_classify(): # Val checks ----------------------------------------------------------------------------------------------------------- def test_val_detect(): - os.system('yolo mode=val task=detect model=yolov8n.pt data=coco128.yaml imgsz=32 epochs=1') + os.system(f'yolo mode=val task=detect model={MODEL} data=coco128.yaml imgsz=32 epochs=1') def test_val_segment(): @@ -35,7 +39,7 @@ def test_val_classify(): # Predict checks ------------------------------------------------------------------------------------------------------- def test_predict_detect(): - os.system(f"yolo mode=predict model=yolov8n.pt source={ROOT / 'assets'}") + os.system(f"yolo mode=predict model={MODEL} source={ROOT / 'assets'}") def test_predict_segment(): @@ -48,7 +52,7 @@ def test_predict_classify(): # Export checks -------------------------------------------------------------------------------------------------------- def test_export_detect_torchscript(): - os.system('yolo mode=export model=yolov8n.pt format=torchscript') + os.system(f'yolo mode=export model={MODEL} format=torchscript') def test_export_segment_torchscript(): diff --git a/tests/tests.py b/tests/test_python.py similarity index 91% rename from tests/tests.py rename to tests/test_python.py index e7a22fe..571a466 100644 --- a/tests/tests.py +++ b/tests/test_python.py @@ -1,9 +1,11 @@ +from pathlib import Path + import torch from ultralytics import YOLO -from ultralytics.yolo.utils import ROOT +from ultralytics.yolo.utils import ROOT, SETTINGS -MODEL = ROOT / 'weights/yolov8n.pt' +MODEL = Path(SETTINGS['weights_dir']) / 'yolov8n.pt' CFG = 'yolov8n.yaml' @@ -95,6 +97,11 @@ def test_export_paddle(): model.export(format='paddle') +def test_all_model_yamls(): + for m in list((ROOT / 'yolo/v8/models').rglob('*.yaml')): + YOLO(m.name) + + # def run_all_tests(): # do not name function test_... # pass # diff --git a/ultralytics/nn/tasks.py b/ultralytics/nn/tasks.py index f1b4fcb..5243129 100644 --- a/ultralytics/nn/tasks.py +++ b/ultralytics/nn/tasks.py @@ -142,7 +142,7 @@ class DetectionModel(BaseModel): # YOLOv5 detection model def __init__(self, cfg='yolov8n.yaml', ch=3, nc=None, verbose=True): # model, input channels, number of classes super().__init__() - self.yaml = cfg if isinstance(cfg, dict) else yaml_load(check_yaml(cfg)) # cfg dict + self.yaml = cfg if isinstance(cfg, dict) else yaml_load(check_yaml(cfg), append_filename=True) # cfg dict # Define model ch = self.yaml['ch'] = self.yaml.get('ch', ch) # input channels diff --git a/ultralytics/yolo/data/utils.py b/ultralytics/yolo/data/utils.py index 25554e9..85f8821 100644 --- a/ultralytics/yolo/data/utils.py +++ b/ultralytics/yolo/data/utils.py @@ -201,7 +201,7 @@ def check_dataset_yaml(data, autodownload=True): extract_dir, autodownload = data.parent, False # Read yaml (optional) if isinstance(data, (str, Path)): - data = yaml_load(data) # dictionary + data = yaml_load(data, append_filename=True) # dictionary # Checks for k in 'train', 'val', 'names': diff --git a/ultralytics/yolo/engine/exporter.py b/ultralytics/yolo/engine/exporter.py index 49a9742..dc457a0 100644 --- a/ultralytics/yolo/engine/exporter.py +++ b/ultralytics/yolo/engine/exporter.py @@ -67,7 +67,7 @@ import torch import ultralytics from ultralytics.nn.modules import Detect, Segment -from ultralytics.nn.tasks import ClassificationModel, DetectionModel, SegmentationModel, attempt_load_weights +from ultralytics.nn.tasks import ClassificationModel, DetectionModel, SegmentationModel from ultralytics.yolo.configs import get_config from ultralytics.yolo.data.dataloaders.stream_loaders import LoadImages from ultralytics.yolo.data.utils import check_dataset @@ -154,7 +154,7 @@ class Exporter: # Load PyTorch model self.device = select_device(self.args.device or 'cpu') if self.args.half: - if self.device.type == 'cpu' or not coreml: + if self.device.type == 'cpu' and not coreml: LOGGER.info('half=True only compatible with GPU or CoreML export, i.e. use device=0 or format=coreml') self.args.half = False assert not self.args.dynamic, '--half not compatible with --dynamic, i.e. use either --half or --dynamic' @@ -769,17 +769,22 @@ class Exporter: def export(cfg): cfg.model = cfg.model or "yolov8n.yaml" cfg.format = cfg.format or "torchscript" - exporter = Exporter(cfg) - - model = None - if isinstance(cfg.model, (str, Path)): - if Path(cfg.model).suffix == '.yaml': - model = DetectionModel(cfg.model) - elif Path(cfg.model).suffix == '.pt': - model = attempt_load_weights(cfg.model, fuse=True) - else: - TypeError(f'Unsupported model type {cfg.model}') - exporter(model=model) + + # exporter = Exporter(cfg) + # + # model = None + # if isinstance(cfg.model, (str, Path)): + # if Path(cfg.model).suffix == '.yaml': + # model = DetectionModel(cfg.model) + # elif Path(cfg.model).suffix == '.pt': + # model = attempt_load_weights(cfg.model, fuse=True) + # else: + # TypeError(f'Unsupported model type {cfg.model}') + # exporter(model=model) + + from ultralytics import YOLO + model = YOLO(cfg.model) + model.export(**cfg) if __name__ == "__main__": diff --git a/ultralytics/yolo/engine/model.py b/ultralytics/yolo/engine/model.py index b4ba821..ddb1187 100644 --- a/ultralytics/yolo/engine/model.py +++ b/ultralytics/yolo/engine/model.py @@ -64,7 +64,7 @@ class YOLO: verbose (bool): display model info on load """ cfg = check_yaml(cfg) # check YAML - cfg_dict = yaml_load(cfg) # model dict + cfg_dict = yaml_load(cfg, append_filename=True) # model dict self.task = guess_task_from_head(cfg_dict["head"][-1][-2]) self.ModelClass, self.TrainerClass, self.ValidatorClass, self.PredictorClass = \ self._guess_ops_from_task(self.task) @@ -183,7 +183,7 @@ class YOLO: overrides.update(kwargs) if kwargs.get("cfg"): LOGGER.info(f"cfg file passed. Overriding default params with {kwargs['cfg']}.") - overrides = yaml_load(check_yaml(kwargs["cfg"])) + overrides = yaml_load(check_yaml(kwargs["cfg"]), append_filename=True) overrides["task"] = self.task overrides["mode"] = "train" if not overrides.get("data"): diff --git a/ultralytics/yolo/engine/validator.py b/ultralytics/yolo/engine/validator.py index ed6fa60..055599d 100644 --- a/ultralytics/yolo/engine/validator.py +++ b/ultralytics/yolo/engine/validator.py @@ -157,7 +157,8 @@ class BaseValidator: self.run_callbacks('on_val_end') if self.training: model.float() - return {**stats, **trainer.label_loss_items(self.loss.cpu() / len(self.dataloader), prefix="val")} + results = {**stats, **trainer.label_loss_items(self.loss.cpu() / len(self.dataloader), prefix="val")} + return {k: round(float(v), 5) for k, v in results.items()} # return results as 5 decimal place floats else: self.logger.info('Speed: %.1fms pre-process, %.1fms inference, %.1fms loss, %.1fms post-process per image' % self.speed) diff --git a/ultralytics/yolo/utils/__init__.py b/ultralytics/yolo/utils/__init__.py index 593311f..4940eaa 100644 --- a/ultralytics/yolo/utils/__init__.py +++ b/ultralytics/yolo/utils/__init__.py @@ -3,6 +3,7 @@ import inspect import logging.config import os import platform +import subprocess import sys import tempfile import threading @@ -171,6 +172,18 @@ def is_dir_writeable(dir_path: str) -> bool: return False +def get_git_root_dir(): + """ + Determines whether the current file is part of a git repository and if so, returns the repository root directory. + If the current file is not part of a git repository, returns None. + """ + try: + output = subprocess.run(["git", "rev-parse", "--git-dir"], capture_output=True, check=True) + return Path(output.stdout.strip().decode('utf-8')).parent # parent/.git + except subprocess.CalledProcessError: + return None + + def get_default_args(func): # Get func() default arguments signature = inspect.signature(func) @@ -311,13 +324,13 @@ def yaml_save(file='data.yaml', data=None): yaml.safe_dump({k: str(v) if isinstance(v, Path) else v for k, v in data.items()}, f, sort_keys=False) -def yaml_load(file='data.yaml', append_filename=True): +def yaml_load(file='data.yaml', append_filename=False): """ Load YAML data from a file. Args: file (str, optional): File name. Default is 'data.yaml'. - append_filename (bool): Add the YAML filename to the YAML dictionary. Default is True. + append_filename (bool): Add the YAML filename to the YAML dictionary. Default is False. Returns: dict: YAML data and file name. @@ -339,14 +352,13 @@ def get_settings(file=USER_CONFIG_DIR / 'settings.yaml'): """ from ultralytics.yolo.utils.torch_utils import torch_distributed_zero_first - git_install = not is_pip_package() + root = get_git_root_dir() or Path('') # not is_pip_package() defaults = { - 'datasets_dir': str(ROOT / 'datasets') if git_install else 'datasets', # default datasets directory. - 'weights_dir': str(ROOT / 'weights') if git_install else 'weights', # default weights directory. - 'runs_dir': str(ROOT / 'runs') if git_install else 'runs', # default runs directory. + 'datasets_dir': str(root / 'datasets'), # default datasets directory. + 'weights_dir': str(root / 'weights'), # default weights directory. + 'runs_dir': str(root / 'runs'), # default runs directory. '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 + 'uuid': uuid.getnode()} # device UUID to align analytics with torch_distributed_zero_first(RANK): if not file.exists(): diff --git a/ultralytics/yolo/utils/callbacks/hub.py b/ultralytics/yolo/utils/callbacks/hub.py index f4cf68f..1fadcbc 100644 --- a/ultralytics/yolo/utils/callbacks/hub.py +++ b/ultralytics/yolo/utils/callbacks/hub.py @@ -18,13 +18,7 @@ def on_pretrain_routine_end(trainer): def on_fit_epoch_end(trainer): session = getattr(trainer, 'hub_session', None) if session: - # Upload metrics after val end - metrics = trainer.metrics - for k, v in metrics.items(): - if isinstance(v, torch.Tensor): - metrics[k] = v.item() - - session.metrics_queue[trainer.epoch] = json.dumps(metrics) # json string + session.metrics_queue[trainer.epoch] = json.dumps(trainer.metrics) # json string if time() - session.t['metrics'] > session.rate_limits['metrics']: session.upload_metrics() session.t['metrics'] = time() # reset timer diff --git a/ultralytics/yolo/utils/checks.py b/ultralytics/yolo/utils/checks.py index e50becb..2d97d56 100644 --- a/ultralytics/yolo/utils/checks.py +++ b/ultralytics/yolo/utils/checks.py @@ -153,7 +153,7 @@ def check_python(minimum: str = '3.7.0') -> bool: @TryExcept() -def check_requirements(requirements=ROOT / 'requirements.txt', exclude=(), install=True, cmds=''): +def check_requirements(requirements=ROOT.parent / 'requirements.txt', exclude=(), install=True, cmds=''): # Check installed dependencies meet YOLOv5 requirements (pass *.txt file or list of packages or single package str) prefix = colorstr('red', 'bold', 'requirements:') check_python() # check python version diff --git a/ultralytics/yolo/v8/detect/val.py b/ultralytics/yolo/v8/detect/val.py index fa69306..27ae5a1 100644 --- a/ultralytics/yolo/v8/detect/val.py +++ b/ultralytics/yolo/v8/detect/val.py @@ -19,7 +19,7 @@ class DetectionValidator(BaseValidator): def __init__(self, dataloader=None, save_dir=None, pbar=None, logger=None, args=None): super().__init__(dataloader, save_dir, pbar, logger, args) - self.data_dict = yaml_load(check_file(self.args.data)) if self.args.data else None + self.data_dict = yaml_load(check_file(self.args.data), append_filename=True) if self.args.data else None self.is_coco = False self.class_map = None self.targets = None