Diff of /benchmarks.py [000000] .. [190ca4]

Switch to unified view

a b/benchmarks.py
1
# YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
2
"""
3
Run YOLOv5 benchmarks on all supported export formats
4
5
Format                      | `export.py --include`         | Model
6
---                         | ---                           | ---
7
PyTorch                     | -                             | yolov5s.pt
8
TorchScript                 | `torchscript`                 | yolov5s.torchscript
9
ONNX                        | `onnx`                        | yolov5s.onnx
10
OpenVINO                    | `openvino`                    | yolov5s_openvino_model/
11
TensorRT                    | `engine`                      | yolov5s.engine
12
CoreML                      | `coreml`                      | yolov5s.mlmodel
13
TensorFlow SavedModel       | `saved_model`                 | yolov5s_saved_model/
14
TensorFlow GraphDef         | `pb`                          | yolov5s.pb
15
TensorFlow Lite             | `tflite`                      | yolov5s.tflite
16
TensorFlow Edge TPU         | `edgetpu`                     | yolov5s_edgetpu.tflite
17
TensorFlow.js               | `tfjs`                        | yolov5s_web_model/
18
19
Requirements:
20
    $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu  # CPU
21
    $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow  # GPU
22
    $ pip install -U nvidia-tensorrt --index-url https://pypi.ngc.nvidia.com  # TensorRT
23
24
Usage:
25
    $ python benchmarks.py --weights yolov5s.pt --img 640
26
"""
27
28
import argparse
29
import platform
30
import sys
31
import time
32
from pathlib import Path
33
34
import pandas as pd
35
36
FILE = Path(__file__).resolve()
37
ROOT = FILE.parents[0]  # YOLOv5 root directory
38
if str(ROOT) not in sys.path:
39
    sys.path.append(str(ROOT))  # add ROOT to PATH
40
# ROOT = ROOT.relative_to(Path.cwd())  # relative
41
42
import export
43
from models.experimental import attempt_load
44
from models.yolo import SegmentationModel
45
from segment.val import run as val_seg
46
from utils import notebook_init
47
from utils.general import LOGGER, check_yaml, file_size, print_args
48
from utils.torch_utils import select_device
49
from val import run as val_det
50
51
52
def run(
53
        weights=ROOT / 'yolov5s.pt',  # weights path
54
        imgsz=640,  # inference size (pixels)
55
        batch_size=1,  # batch size
56
        data=ROOT / 'data/coco128.yaml',  # dataset.yaml path
57
        device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
58
        half=False,  # use FP16 half-precision inference
59
        test=False,  # test exports only
60
        pt_only=False,  # test PyTorch only
61
        hard_fail=False,  # throw error on benchmark failure
62
):
63
    y, t = [], time.time()
64
    device = select_device(device)
65
    model_type = type(attempt_load(weights, fuse=False))  # DetectionModel, SegmentationModel, etc.
66
    for i, (name, f, suffix, cpu, gpu) in export.export_formats().iterrows():  # index, (name, file, suffix, CPU, GPU)
67
        try:
68
            assert i not in (9, 10), 'inference not supported'  # Edge TPU and TF.js are unsupported
69
            assert i != 5 or platform.system() == 'Darwin', 'inference only supported on macOS>=10.13'  # CoreML
70
            if 'cpu' in device.type:
71
                assert cpu, 'inference not supported on CPU'
72
            if 'cuda' in device.type:
73
                assert gpu, 'inference not supported on GPU'
74
75
            # Export
76
            if f == '-':
77
                w = weights  # PyTorch format
78
            else:
79
                w = export.run(weights=weights,
80
                               imgsz=[imgsz],
81
                               include=[f],
82
                               batch_size=batch_size,
83
                               device=device,
84
                               half=half)[-1]  # all others
85
            assert suffix in str(w), 'export failed'
86
87
            # Validate
88
            if model_type == SegmentationModel:
89
                result = val_seg(data, w, batch_size, imgsz, plots=False, device=device, task='speed', half=half)
90
                metric = result[0][7]  # (box(p, r, map50, map), mask(p, r, map50, map), *loss(box, obj, cls))
91
            else:  # DetectionModel:
92
                result = val_det(data, w, batch_size, imgsz, plots=False, device=device, task='speed', half=half)
93
                metric = result[0][3]  # (p, r, map50, map, *loss(box, obj, cls))
94
            speed = result[2][1]  # times (preprocess, inference, postprocess)
95
            y.append([name, round(file_size(w), 1), round(metric, 4), round(speed, 2)])  # MB, mAP, t_inference
96
        except Exception as e:
97
            if hard_fail:
98
                assert type(e) is AssertionError, f'Benchmark --hard-fail for {name}: {e}'
99
            LOGGER.warning(f'WARNING ⚠️ Benchmark failure for {name}: {e}')
100
            y.append([name, None, None, None])  # mAP, t_inference
101
        if pt_only and i == 0:
102
            break  # break after PyTorch
103
104
    # Print results
105
    LOGGER.info('\n')
106
    parse_opt()
107
    notebook_init()  # print system info
108
    c = ['Format', 'Size (MB)', 'mAP50-95', 'Inference time (ms)'] if map else ['Format', 'Export', '', '']
109
    py = pd.DataFrame(y, columns=c)
110
    LOGGER.info(f'\nBenchmarks complete ({time.time() - t:.2f}s)')
111
    LOGGER.info(str(py if map else py.iloc[:, :2]))
112
    if hard_fail and isinstance(hard_fail, str):
113
        metrics = py['mAP50-95'].array  # values to compare to floor
114
        floor = eval(hard_fail)  # minimum metric floor to pass, i.e. = 0.29 mAP for YOLOv5n
115
        assert all(x > floor for x in metrics if pd.notna(x)), f'HARD FAIL: mAP50-95 < floor {floor}'
116
    return py
117
118
119
def test(
120
        weights=ROOT / 'yolov5s.pt',  # weights path
121
        imgsz=640,  # inference size (pixels)
122
        batch_size=1,  # batch size
123
        data=ROOT / 'data/coco128.yaml',  # dataset.yaml path
124
        device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
125
        half=False,  # use FP16 half-precision inference
126
        test=False,  # test exports only
127
        pt_only=False,  # test PyTorch only
128
        hard_fail=False,  # throw error on benchmark failure
129
):
130
    y, t = [], time.time()
131
    device = select_device(device)
132
    for i, (name, f, suffix, gpu) in export.export_formats().iterrows():  # index, (name, file, suffix, gpu-capable)
133
        try:
134
            w = weights if f == '-' else \
135
                export.run(weights=weights, imgsz=[imgsz], include=[f], device=device, half=half)[-1]  # weights
136
            assert suffix in str(w), 'export failed'
137
            y.append([name, True])
138
        except Exception:
139
            y.append([name, False])  # mAP, t_inference
140
141
    # Print results
142
    LOGGER.info('\n')
143
    parse_opt()
144
    notebook_init()  # print system info
145
    py = pd.DataFrame(y, columns=['Format', 'Export'])
146
    LOGGER.info(f'\nExports complete ({time.time() - t:.2f}s)')
147
    LOGGER.info(str(py))
148
    return py
149
150
151
def parse_opt():
152
    parser = argparse.ArgumentParser()
153
    parser.add_argument('--weights', type=str, default=ROOT / 'yolov5s.pt', help='weights path')
154
    parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='inference size (pixels)')
155
    parser.add_argument('--batch-size', type=int, default=1, help='batch size')
156
    parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='dataset.yaml path')
157
    parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
158
    parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference')
159
    parser.add_argument('--test', action='store_true', help='test exports only')
160
    parser.add_argument('--pt-only', action='store_true', help='test PyTorch only')
161
    parser.add_argument('--hard-fail', nargs='?', const=True, default=False, help='Exception on error or < min metric')
162
    opt = parser.parse_args()
163
    opt.data = check_yaml(opt.data)  # check YAML
164
    print_args(vars(opt))
165
    return opt
166
167
168
def main(opt):
169
    test(**vars(opt)) if opt.test else run(**vars(opt))
170
171
172
if __name__ == '__main__':
173
    opt = parse_opt()
174
    main(opt)