bplusplus 1.1.0__py3-none-any.whl → 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bplusplus might be problematic. Click here for more details.

Files changed (97) hide show
  1. bplusplus/__init__.py +4 -2
  2. bplusplus/collect.py +72 -3
  3. bplusplus/hierarchical/test.py +670 -0
  4. bplusplus/hierarchical/train.py +676 -0
  5. bplusplus/prepare.py +236 -71
  6. bplusplus/resnet/test.py +473 -0
  7. bplusplus/resnet/train.py +329 -0
  8. bplusplus-1.2.1.dist-info/METADATA +252 -0
  9. bplusplus-1.2.1.dist-info/RECORD +12 -0
  10. bplusplus/yolov5detect/__init__.py +0 -1
  11. bplusplus/yolov5detect/detect.py +0 -444
  12. bplusplus/yolov5detect/export.py +0 -1530
  13. bplusplus/yolov5detect/insect.yaml +0 -8
  14. bplusplus/yolov5detect/models/__init__.py +0 -0
  15. bplusplus/yolov5detect/models/common.py +0 -1109
  16. bplusplus/yolov5detect/models/experimental.py +0 -130
  17. bplusplus/yolov5detect/models/hub/anchors.yaml +0 -56
  18. bplusplus/yolov5detect/models/hub/yolov3-spp.yaml +0 -52
  19. bplusplus/yolov5detect/models/hub/yolov3-tiny.yaml +0 -42
  20. bplusplus/yolov5detect/models/hub/yolov3.yaml +0 -52
  21. bplusplus/yolov5detect/models/hub/yolov5-bifpn.yaml +0 -49
  22. bplusplus/yolov5detect/models/hub/yolov5-fpn.yaml +0 -43
  23. bplusplus/yolov5detect/models/hub/yolov5-p2.yaml +0 -55
  24. bplusplus/yolov5detect/models/hub/yolov5-p34.yaml +0 -42
  25. bplusplus/yolov5detect/models/hub/yolov5-p6.yaml +0 -57
  26. bplusplus/yolov5detect/models/hub/yolov5-p7.yaml +0 -68
  27. bplusplus/yolov5detect/models/hub/yolov5-panet.yaml +0 -49
  28. bplusplus/yolov5detect/models/hub/yolov5l6.yaml +0 -61
  29. bplusplus/yolov5detect/models/hub/yolov5m6.yaml +0 -61
  30. bplusplus/yolov5detect/models/hub/yolov5n6.yaml +0 -61
  31. bplusplus/yolov5detect/models/hub/yolov5s-LeakyReLU.yaml +0 -50
  32. bplusplus/yolov5detect/models/hub/yolov5s-ghost.yaml +0 -49
  33. bplusplus/yolov5detect/models/hub/yolov5s-transformer.yaml +0 -49
  34. bplusplus/yolov5detect/models/hub/yolov5s6.yaml +0 -61
  35. bplusplus/yolov5detect/models/hub/yolov5x6.yaml +0 -61
  36. bplusplus/yolov5detect/models/segment/yolov5l-seg.yaml +0 -49
  37. bplusplus/yolov5detect/models/segment/yolov5m-seg.yaml +0 -49
  38. bplusplus/yolov5detect/models/segment/yolov5n-seg.yaml +0 -49
  39. bplusplus/yolov5detect/models/segment/yolov5s-seg.yaml +0 -49
  40. bplusplus/yolov5detect/models/segment/yolov5x-seg.yaml +0 -49
  41. bplusplus/yolov5detect/models/tf.py +0 -797
  42. bplusplus/yolov5detect/models/yolo.py +0 -495
  43. bplusplus/yolov5detect/models/yolov5l.yaml +0 -49
  44. bplusplus/yolov5detect/models/yolov5m.yaml +0 -49
  45. bplusplus/yolov5detect/models/yolov5n.yaml +0 -49
  46. bplusplus/yolov5detect/models/yolov5s.yaml +0 -49
  47. bplusplus/yolov5detect/models/yolov5x.yaml +0 -49
  48. bplusplus/yolov5detect/utils/__init__.py +0 -97
  49. bplusplus/yolov5detect/utils/activations.py +0 -134
  50. bplusplus/yolov5detect/utils/augmentations.py +0 -448
  51. bplusplus/yolov5detect/utils/autoanchor.py +0 -175
  52. bplusplus/yolov5detect/utils/autobatch.py +0 -70
  53. bplusplus/yolov5detect/utils/aws/__init__.py +0 -0
  54. bplusplus/yolov5detect/utils/aws/mime.sh +0 -26
  55. bplusplus/yolov5detect/utils/aws/resume.py +0 -41
  56. bplusplus/yolov5detect/utils/aws/userdata.sh +0 -27
  57. bplusplus/yolov5detect/utils/callbacks.py +0 -72
  58. bplusplus/yolov5detect/utils/dataloaders.py +0 -1385
  59. bplusplus/yolov5detect/utils/docker/Dockerfile +0 -73
  60. bplusplus/yolov5detect/utils/docker/Dockerfile-arm64 +0 -40
  61. bplusplus/yolov5detect/utils/docker/Dockerfile-cpu +0 -42
  62. bplusplus/yolov5detect/utils/downloads.py +0 -136
  63. bplusplus/yolov5detect/utils/flask_rest_api/README.md +0 -70
  64. bplusplus/yolov5detect/utils/flask_rest_api/example_request.py +0 -17
  65. bplusplus/yolov5detect/utils/flask_rest_api/restapi.py +0 -49
  66. bplusplus/yolov5detect/utils/general.py +0 -1294
  67. bplusplus/yolov5detect/utils/google_app_engine/Dockerfile +0 -25
  68. bplusplus/yolov5detect/utils/google_app_engine/additional_requirements.txt +0 -6
  69. bplusplus/yolov5detect/utils/google_app_engine/app.yaml +0 -16
  70. bplusplus/yolov5detect/utils/loggers/__init__.py +0 -476
  71. bplusplus/yolov5detect/utils/loggers/clearml/README.md +0 -222
  72. bplusplus/yolov5detect/utils/loggers/clearml/__init__.py +0 -0
  73. bplusplus/yolov5detect/utils/loggers/clearml/clearml_utils.py +0 -230
  74. bplusplus/yolov5detect/utils/loggers/clearml/hpo.py +0 -90
  75. bplusplus/yolov5detect/utils/loggers/comet/README.md +0 -250
  76. bplusplus/yolov5detect/utils/loggers/comet/__init__.py +0 -551
  77. bplusplus/yolov5detect/utils/loggers/comet/comet_utils.py +0 -151
  78. bplusplus/yolov5detect/utils/loggers/comet/hpo.py +0 -126
  79. bplusplus/yolov5detect/utils/loggers/comet/optimizer_config.json +0 -135
  80. bplusplus/yolov5detect/utils/loggers/wandb/__init__.py +0 -0
  81. bplusplus/yolov5detect/utils/loggers/wandb/wandb_utils.py +0 -210
  82. bplusplus/yolov5detect/utils/loss.py +0 -259
  83. bplusplus/yolov5detect/utils/metrics.py +0 -381
  84. bplusplus/yolov5detect/utils/plots.py +0 -517
  85. bplusplus/yolov5detect/utils/segment/__init__.py +0 -0
  86. bplusplus/yolov5detect/utils/segment/augmentations.py +0 -100
  87. bplusplus/yolov5detect/utils/segment/dataloaders.py +0 -366
  88. bplusplus/yolov5detect/utils/segment/general.py +0 -160
  89. bplusplus/yolov5detect/utils/segment/loss.py +0 -198
  90. bplusplus/yolov5detect/utils/segment/metrics.py +0 -225
  91. bplusplus/yolov5detect/utils/segment/plots.py +0 -152
  92. bplusplus/yolov5detect/utils/torch_utils.py +0 -482
  93. bplusplus/yolov5detect/utils/triton.py +0 -90
  94. bplusplus-1.1.0.dist-info/METADATA +0 -179
  95. bplusplus-1.1.0.dist-info/RECORD +0 -92
  96. {bplusplus-1.1.0.dist-info → bplusplus-1.2.1.dist-info}/LICENSE +0 -0
  97. {bplusplus-1.1.0.dist-info → bplusplus-1.2.1.dist-info}/WHEEL +0 -0
@@ -1,482 +0,0 @@
1
- # Ultralytics YOLOv5 🚀, AGPL-3.0 license
2
- """PyTorch utils."""
3
-
4
- import math
5
- import os
6
- import platform
7
- import subprocess
8
- import time
9
- import warnings
10
- from contextlib import contextmanager
11
- from copy import deepcopy
12
- from pathlib import Path
13
-
14
- import torch
15
- import torch.distributed as dist
16
- import torch.nn as nn
17
- import torch.nn.functional as F
18
- from torch.nn.parallel import DistributedDataParallel as DDP
19
-
20
- from utils.general import LOGGER, check_version, colorstr, file_date, git_describe
21
-
22
- LOCAL_RANK = int(os.getenv("LOCAL_RANK", -1)) # https://pytorch.org/docs/stable/elastic/run.html
23
- RANK = int(os.getenv("RANK", -1))
24
- WORLD_SIZE = int(os.getenv("WORLD_SIZE", 1))
25
-
26
- try:
27
- import thop # for FLOPs computation
28
- except ImportError:
29
- thop = None
30
-
31
- # Suppress PyTorch warnings
32
- warnings.filterwarnings("ignore", message="User provided device_type of 'cuda', but CUDA is not available. Disabling")
33
- warnings.filterwarnings("ignore", category=UserWarning)
34
-
35
-
36
- def smart_inference_mode(torch_1_9=check_version(torch.__version__, "1.9.0")):
37
- """Applies torch.inference_mode() if torch>=1.9.0, else torch.no_grad() as a decorator for functions."""
38
-
39
- def decorate(fn):
40
- """Applies torch.inference_mode() if torch>=1.9.0, else torch.no_grad() to the decorated function."""
41
- return (torch.inference_mode if torch_1_9 else torch.no_grad)()(fn)
42
-
43
- return decorate
44
-
45
-
46
- def smartCrossEntropyLoss(label_smoothing=0.0):
47
- """Returns a CrossEntropyLoss with optional label smoothing for torch>=1.10.0; warns if smoothing on lower
48
- versions.
49
- """
50
- if check_version(torch.__version__, "1.10.0"):
51
- return nn.CrossEntropyLoss(label_smoothing=label_smoothing)
52
- if label_smoothing > 0:
53
- LOGGER.warning(f"WARNING ⚠️ label smoothing {label_smoothing} requires torch>=1.10.0")
54
- return nn.CrossEntropyLoss()
55
-
56
-
57
- def smart_DDP(model):
58
- """Initializes DistributedDataParallel (DDP) for model training, respecting torch version constraints."""
59
- assert not check_version(torch.__version__, "1.12.0", pinned=True), (
60
- "torch==1.12.0 torchvision==0.13.0 DDP training is not supported due to a known issue. "
61
- "Please upgrade or downgrade torch to use DDP. See https://github.com/ultralytics/yolov5/issues/8395"
62
- )
63
- if check_version(torch.__version__, "1.11.0"):
64
- return DDP(model, device_ids=[LOCAL_RANK], output_device=LOCAL_RANK, static_graph=True)
65
- else:
66
- return DDP(model, device_ids=[LOCAL_RANK], output_device=LOCAL_RANK)
67
-
68
-
69
- def reshape_classifier_output(model, n=1000):
70
- """Reshapes last layer of model to match class count 'n', supporting Classify, Linear, Sequential types."""
71
- from models.common import Classify
72
-
73
- name, m = list((model.model if hasattr(model, "model") else model).named_children())[-1] # last module
74
- if isinstance(m, Classify): # YOLOv5 Classify() head
75
- if m.linear.out_features != n:
76
- m.linear = nn.Linear(m.linear.in_features, n)
77
- elif isinstance(m, nn.Linear): # ResNet, EfficientNet
78
- if m.out_features != n:
79
- setattr(model, name, nn.Linear(m.in_features, n))
80
- elif isinstance(m, nn.Sequential):
81
- types = [type(x) for x in m]
82
- if nn.Linear in types:
83
- i = len(types) - 1 - types[::-1].index(nn.Linear) # last nn.Linear index
84
- if m[i].out_features != n:
85
- m[i] = nn.Linear(m[i].in_features, n)
86
- elif nn.Conv2d in types:
87
- i = len(types) - 1 - types[::-1].index(nn.Conv2d) # last nn.Conv2d index
88
- if m[i].out_channels != n:
89
- m[i] = nn.Conv2d(m[i].in_channels, n, m[i].kernel_size, m[i].stride, bias=m[i].bias is not None)
90
-
91
-
92
- @contextmanager
93
- def torch_distributed_zero_first(local_rank: int):
94
- """Context manager ensuring ordered operations in distributed training by making all processes wait for the leading
95
- process.
96
- """
97
- if local_rank not in [-1, 0]:
98
- dist.barrier(device_ids=[local_rank])
99
- yield
100
- if local_rank == 0:
101
- dist.barrier(device_ids=[0])
102
-
103
-
104
- def device_count():
105
- """Returns the number of available CUDA devices; works on Linux and Windows by invoking `nvidia-smi`."""
106
- assert platform.system() in ("Linux", "Windows"), "device_count() only supported on Linux or Windows"
107
- try:
108
- cmd = "nvidia-smi -L | wc -l" if platform.system() == "Linux" else 'nvidia-smi -L | find /c /v ""' # Windows
109
- return int(subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().split()[-1])
110
- except Exception:
111
- return 0
112
-
113
-
114
- def select_device(device="", batch_size=0, newline=True):
115
- """Selects computing device (CPU, CUDA GPU, MPS) for YOLOv5 model deployment, logging device info."""
116
- s = f"YOLOv5 🚀 {git_describe() or file_date()} Python-{platform.python_version()} torch-{torch.__version__} "
117
- device = str(device).strip().lower().replace("cuda:", "").replace("none", "") # to string, 'cuda:0' to '0'
118
- cpu = device == "cpu"
119
- mps = device == "mps" # Apple Metal Performance Shaders (MPS)
120
- if cpu or mps:
121
- os.environ["CUDA_VISIBLE_DEVICES"] = "-1" # force torch.cuda.is_available() = False
122
- elif device: # non-cpu device requested
123
- os.environ["CUDA_VISIBLE_DEVICES"] = device # set environment variable - must be before assert is_available()
124
- assert torch.cuda.is_available() and torch.cuda.device_count() >= len(
125
- device.replace(",", "")
126
- ), f"Invalid CUDA '--device {device}' requested, use '--device cpu' or pass valid CUDA device(s)"
127
-
128
- if not cpu and not mps and torch.cuda.is_available(): # prefer GPU if available
129
- devices = device.split(",") if device else "0" # range(torch.cuda.device_count()) # i.e. 0,1,6,7
130
- n = len(devices) # device count
131
- if n > 1 and batch_size > 0: # check batch_size is divisible by device_count
132
- assert batch_size % n == 0, f"batch-size {batch_size} not multiple of GPU count {n}"
133
- space = " " * (len(s) + 1)
134
- for i, d in enumerate(devices):
135
- p = torch.cuda.get_device_properties(i)
136
- s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / (1 << 20):.0f}MiB)\n" # bytes to MB
137
- arg = "cuda:0"
138
- elif mps and getattr(torch, "has_mps", False) and torch.backends.mps.is_available(): # prefer MPS if available
139
- s += "MPS\n"
140
- arg = "mps"
141
- else: # revert to CPU
142
- s += "CPU\n"
143
- arg = "cpu"
144
-
145
- if not newline:
146
- s = s.rstrip()
147
- LOGGER.info(s)
148
- return torch.device(arg)
149
-
150
-
151
- def time_sync():
152
- """Synchronizes PyTorch for accurate timing, leveraging CUDA if available, and returns the current time."""
153
- if torch.cuda.is_available():
154
- torch.cuda.synchronize()
155
- return time.time()
156
-
157
-
158
- def profile(input, ops, n=10, device=None):
159
- """YOLOv5 speed/memory/FLOPs profiler
160
- Usage:
161
- input = torch.randn(16, 3, 640, 640)
162
- m1 = lambda x: x * torch.sigmoid(x)
163
- m2 = nn.SiLU()
164
- profile(input, [m1, m2], n=100) # profile over 100 iterations.
165
- """
166
- results = []
167
- if not isinstance(device, torch.device):
168
- device = select_device(device)
169
- print(
170
- f"{'Params':>12s}{'GFLOPs':>12s}{'GPU_mem (GB)':>14s}{'forward (ms)':>14s}{'backward (ms)':>14s}"
171
- f"{'input':>24s}{'output':>24s}"
172
- )
173
-
174
- for x in input if isinstance(input, list) else [input]:
175
- x = x.to(device)
176
- x.requires_grad = True
177
- for m in ops if isinstance(ops, list) else [ops]:
178
- m = m.to(device) if hasattr(m, "to") else m # device
179
- m = m.half() if hasattr(m, "half") and isinstance(x, torch.Tensor) and x.dtype is torch.float16 else m
180
- tf, tb, t = 0, 0, [0, 0, 0] # dt forward, backward
181
- try:
182
- flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1e9 * 2 # GFLOPs
183
- except Exception:
184
- flops = 0
185
-
186
- try:
187
- for _ in range(n):
188
- t[0] = time_sync()
189
- y = m(x)
190
- t[1] = time_sync()
191
- try:
192
- _ = (sum(yi.sum() for yi in y) if isinstance(y, list) else y).sum().backward()
193
- t[2] = time_sync()
194
- except Exception: # no backward method
195
- # print(e) # for debug
196
- t[2] = float("nan")
197
- tf += (t[1] - t[0]) * 1000 / n # ms per op forward
198
- tb += (t[2] - t[1]) * 1000 / n # ms per op backward
199
- mem = torch.cuda.memory_reserved() / 1e9 if torch.cuda.is_available() else 0 # (GB)
200
- s_in, s_out = (tuple(x.shape) if isinstance(x, torch.Tensor) else "list" for x in (x, y)) # shapes
201
- p = sum(x.numel() for x in m.parameters()) if isinstance(m, nn.Module) else 0 # parameters
202
- print(f"{p:12}{flops:12.4g}{mem:>14.3f}{tf:14.4g}{tb:14.4g}{str(s_in):>24s}{str(s_out):>24s}")
203
- results.append([p, flops, mem, tf, tb, s_in, s_out])
204
- except Exception as e:
205
- print(e)
206
- results.append(None)
207
- torch.cuda.empty_cache()
208
- return results
209
-
210
-
211
- def is_parallel(model):
212
- """Checks if the model is using Data Parallelism (DP) or Distributed Data Parallelism (DDP)."""
213
- return type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel)
214
-
215
-
216
- def de_parallel(model):
217
- """Returns a single-GPU model by removing Data Parallelism (DP) or Distributed Data Parallelism (DDP) if applied."""
218
- return model.module if is_parallel(model) else model
219
-
220
-
221
- def initialize_weights(model):
222
- """Initializes weights of Conv2d, BatchNorm2d, and activations (Hardswish, LeakyReLU, ReLU, ReLU6, SiLU) in the
223
- model.
224
- """
225
- for m in model.modules():
226
- t = type(m)
227
- if t is nn.Conv2d:
228
- pass # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
229
- elif t is nn.BatchNorm2d:
230
- m.eps = 1e-3
231
- m.momentum = 0.03
232
- elif t in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]:
233
- m.inplace = True
234
-
235
-
236
- def find_modules(model, mclass=nn.Conv2d):
237
- """Finds and returns list of layer indices in `model.module_list` matching the specified `mclass`."""
238
- return [i for i, m in enumerate(model.module_list) if isinstance(m, mclass)]
239
-
240
-
241
- def sparsity(model):
242
- """Calculates and returns the global sparsity of a model as the ratio of zero-valued parameters to total
243
- parameters.
244
- """
245
- a, b = 0, 0
246
- for p in model.parameters():
247
- a += p.numel()
248
- b += (p == 0).sum()
249
- return b / a
250
-
251
-
252
- def prune(model, amount=0.3):
253
- """Prunes Conv2d layers in a model to a specified sparsity using L1 unstructured pruning."""
254
- import torch.nn.utils.prune as prune
255
-
256
- for name, m in model.named_modules():
257
- if isinstance(m, nn.Conv2d):
258
- prune.l1_unstructured(m, name="weight", amount=amount) # prune
259
- prune.remove(m, "weight") # make permanent
260
- LOGGER.info(f"Model pruned to {sparsity(model):.3g} global sparsity")
261
-
262
-
263
- def fuse_conv_and_bn(conv, bn):
264
- """
265
- Fuses Conv2d and BatchNorm2d layers into a single Conv2d layer.
266
-
267
- See https://tehnokv.com/posts/fusing-batchnorm-and-conv/.
268
- """
269
- fusedconv = (
270
- nn.Conv2d(
271
- conv.in_channels,
272
- conv.out_channels,
273
- kernel_size=conv.kernel_size,
274
- stride=conv.stride,
275
- padding=conv.padding,
276
- dilation=conv.dilation,
277
- groups=conv.groups,
278
- bias=True,
279
- )
280
- .requires_grad_(False)
281
- .to(conv.weight.device)
282
- )
283
-
284
- # Prepare filters
285
- w_conv = conv.weight.clone().view(conv.out_channels, -1)
286
- w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var)))
287
- fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.shape))
288
-
289
- # Prepare spatial bias
290
- b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias
291
- b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps))
292
- fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn)
293
-
294
- return fusedconv
295
-
296
-
297
- def model_info(model, verbose=False, imgsz=640):
298
- """
299
- Prints model summary including layers, parameters, gradients, and FLOPs; imgsz may be int or list.
300
-
301
- Example: img_size=640 or img_size=[640, 320]
302
- """
303
- n_p = sum(x.numel() for x in model.parameters()) # number parameters
304
- n_g = sum(x.numel() for x in model.parameters() if x.requires_grad) # number gradients
305
- if verbose:
306
- print(f"{'layer':>5} {'name':>40} {'gradient':>9} {'parameters':>12} {'shape':>20} {'mu':>10} {'sigma':>10}")
307
- for i, (name, p) in enumerate(model.named_parameters()):
308
- name = name.replace("module_list.", "")
309
- print(
310
- "%5g %40s %9s %12g %20s %10.3g %10.3g"
311
- % (i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std())
312
- )
313
-
314
- try: # FLOPs
315
- p = next(model.parameters())
316
- stride = max(int(model.stride.max()), 32) if hasattr(model, "stride") else 32 # max stride
317
- im = torch.empty((1, p.shape[1], stride, stride), device=p.device) # input image in BCHW format
318
- flops = thop.profile(deepcopy(model), inputs=(im,), verbose=False)[0] / 1e9 * 2 # stride GFLOPs
319
- imgsz = imgsz if isinstance(imgsz, list) else [imgsz, imgsz] # expand if int/float
320
- fs = f", {flops * imgsz[0] / stride * imgsz[1] / stride:.1f} GFLOPs" # 640x640 GFLOPs
321
- except Exception:
322
- fs = ""
323
-
324
- name = Path(model.yaml_file).stem.replace("yolov5", "YOLOv5") if hasattr(model, "yaml_file") else "Model"
325
- LOGGER.info(f"{name} summary: {len(list(model.modules()))} layers, {n_p} parameters, {n_g} gradients{fs}")
326
-
327
-
328
- def scale_img(img, ratio=1.0, same_shape=False, gs=32): # img(16,3,256,416)
329
- """Scales an image tensor `img` of shape (bs,3,y,x) by `ratio`, optionally maintaining the original shape, padded to
330
- multiples of `gs`.
331
- """
332
- if ratio == 1.0:
333
- return img
334
- h, w = img.shape[2:]
335
- s = (int(h * ratio), int(w * ratio)) # new size
336
- img = F.interpolate(img, size=s, mode="bilinear", align_corners=False) # resize
337
- if not same_shape: # pad/crop img
338
- h, w = (math.ceil(x * ratio / gs) * gs for x in (h, w))
339
- return F.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447) # value = imagenet mean
340
-
341
-
342
- def copy_attr(a, b, include=(), exclude=()):
343
- """Copies attributes from object b to a, optionally filtering with include and exclude lists."""
344
- for k, v in b.__dict__.items():
345
- if (len(include) and k not in include) or k.startswith("_") or k in exclude:
346
- continue
347
- else:
348
- setattr(a, k, v)
349
-
350
-
351
- def smart_optimizer(model, name="Adam", lr=0.001, momentum=0.9, decay=1e-5):
352
- """
353
- Initializes YOLOv5 smart optimizer with 3 parameter groups for different decay configurations.
354
-
355
- Groups are 0) weights with decay, 1) weights no decay, 2) biases no decay.
356
- """
357
- g = [], [], [] # optimizer parameter groups
358
- bn = tuple(v for k, v in nn.__dict__.items() if "Norm" in k) # normalization layers, i.e. BatchNorm2d()
359
- for v in model.modules():
360
- for p_name, p in v.named_parameters(recurse=0):
361
- if p_name == "bias": # bias (no decay)
362
- g[2].append(p)
363
- elif p_name == "weight" and isinstance(v, bn): # weight (no decay)
364
- g[1].append(p)
365
- else:
366
- g[0].append(p) # weight (with decay)
367
-
368
- if name == "Adam":
369
- optimizer = torch.optim.Adam(g[2], lr=lr, betas=(momentum, 0.999)) # adjust beta1 to momentum
370
- elif name == "AdamW":
371
- optimizer = torch.optim.AdamW(g[2], lr=lr, betas=(momentum, 0.999), weight_decay=0.0)
372
- elif name == "RMSProp":
373
- optimizer = torch.optim.RMSprop(g[2], lr=lr, momentum=momentum)
374
- elif name == "SGD":
375
- optimizer = torch.optim.SGD(g[2], lr=lr, momentum=momentum, nesterov=True)
376
- else:
377
- raise NotImplementedError(f"Optimizer {name} not implemented.")
378
-
379
- optimizer.add_param_group({"params": g[0], "weight_decay": decay}) # add g0 with weight_decay
380
- optimizer.add_param_group({"params": g[1], "weight_decay": 0.0}) # add g1 (BatchNorm2d weights)
381
- LOGGER.info(
382
- f"{colorstr('optimizer:')} {type(optimizer).__name__}(lr={lr}) with parameter groups "
383
- f'{len(g[1])} weight(decay=0.0), {len(g[0])} weight(decay={decay}), {len(g[2])} bias'
384
- )
385
- return optimizer
386
-
387
-
388
- def smart_hub_load(repo="ultralytics/yolov5", model="yolov5s", **kwargs):
389
- """YOLOv5 torch.hub.load() wrapper with smart error handling, adjusting torch arguments for compatibility."""
390
- if check_version(torch.__version__, "1.9.1"):
391
- kwargs["skip_validation"] = True # validation causes GitHub API rate limit errors
392
- if check_version(torch.__version__, "1.12.0"):
393
- kwargs["trust_repo"] = True # argument required starting in torch 0.12
394
- try:
395
- return torch.hub.load(repo, model, **kwargs)
396
- except Exception:
397
- return torch.hub.load(repo, model, force_reload=True, **kwargs)
398
-
399
-
400
- def smart_resume(ckpt, optimizer, ema=None, weights="yolov5s.pt", epochs=300, resume=True):
401
- """Resumes training from a checkpoint, updating optimizer, ema, and epochs, with optional resume verification."""
402
- best_fitness = 0.0
403
- start_epoch = ckpt["epoch"] + 1
404
- if ckpt["optimizer"] is not None:
405
- optimizer.load_state_dict(ckpt["optimizer"]) # optimizer
406
- best_fitness = ckpt["best_fitness"]
407
- if ema and ckpt.get("ema"):
408
- ema.ema.load_state_dict(ckpt["ema"].float().state_dict()) # EMA
409
- ema.updates = ckpt["updates"]
410
- if resume:
411
- assert start_epoch > 0, (
412
- f"{weights} training to {epochs} epochs is finished, nothing to resume.\n"
413
- f"Start a new training without --resume, i.e. 'python train.py --weights {weights}'"
414
- )
415
- LOGGER.info(f"Resuming training from {weights} from epoch {start_epoch} to {epochs} total epochs")
416
- if epochs < start_epoch:
417
- LOGGER.info(f"{weights} has been trained for {ckpt['epoch']} epochs. Fine-tuning for {epochs} more epochs.")
418
- epochs += ckpt["epoch"] # finetune additional epochs
419
- return best_fitness, start_epoch, epochs
420
-
421
-
422
- class EarlyStopping:
423
- """Implements early stopping to halt training when no improvement is observed for a specified number of epochs."""
424
-
425
- def __init__(self, patience=30):
426
- """Initializes simple early stopping mechanism for YOLOv5, with adjustable patience for non-improving epochs."""
427
- self.best_fitness = 0.0 # i.e. mAP
428
- self.best_epoch = 0
429
- self.patience = patience or float("inf") # epochs to wait after fitness stops improving to stop
430
- self.possible_stop = False # possible stop may occur next epoch
431
-
432
- def __call__(self, epoch, fitness):
433
- """Evaluates if training should stop based on fitness improvement and patience, returning a boolean."""
434
- if fitness >= self.best_fitness: # >= 0 to allow for early zero-fitness stage of training
435
- self.best_epoch = epoch
436
- self.best_fitness = fitness
437
- delta = epoch - self.best_epoch # epochs without improvement
438
- self.possible_stop = delta >= (self.patience - 1) # possible stop may occur next epoch
439
- stop = delta >= self.patience # stop training if patience exceeded
440
- if stop:
441
- LOGGER.info(
442
- f"Stopping training early as no improvement observed in last {self.patience} epochs. "
443
- f"Best results observed at epoch {self.best_epoch}, best model saved as best.pt.\n"
444
- f"To update EarlyStopping(patience={self.patience}) pass a new patience value, "
445
- f"i.e. `python train.py --patience 300` or use `--patience 0` to disable EarlyStopping."
446
- )
447
- return stop
448
-
449
-
450
- class ModelEMA:
451
- """Updated Exponential Moving Average (EMA) from https://github.com/rwightman/pytorch-image-models
452
- Keeps a moving average of everything in the model state_dict (parameters and buffers)
453
- For EMA details see https://www.tensorflow.org/api_docs/python/tf/train/ExponentialMovingAverage.
454
- """
455
-
456
- def __init__(self, model, decay=0.9999, tau=2000, updates=0):
457
- """Initializes EMA with model parameters, decay rate, tau for decay adjustment, and update count; sets model to
458
- evaluation mode.
459
- """
460
- self.ema = deepcopy(de_parallel(model)).eval() # FP32 EMA
461
- self.updates = updates # number of EMA updates
462
- self.decay = lambda x: decay * (1 - math.exp(-x / tau)) # decay exponential ramp (to help early epochs)
463
- for p in self.ema.parameters():
464
- p.requires_grad_(False)
465
-
466
- def update(self, model):
467
- """Updates the Exponential Moving Average (EMA) parameters based on the current model's parameters."""
468
- self.updates += 1
469
- d = self.decay(self.updates)
470
-
471
- msd = de_parallel(model).state_dict() # model state_dict
472
- for k, v in self.ema.state_dict().items():
473
- if v.dtype.is_floating_point: # true for FP16 and FP32
474
- v *= d
475
- v += (1 - d) * msd[k].detach()
476
- # assert v.dtype == msd[k].dtype == torch.float32, f'{k}: EMA {v.dtype} and model {msd[k].dtype} must be FP32'
477
-
478
- def update_attr(self, model, include=(), exclude=("process_group", "reducer")):
479
- """Updates EMA attributes by copying specified attributes from model to EMA, excluding certain attributes by
480
- default.
481
- """
482
- copy_attr(self.ema, model, include, exclude)
@@ -1,90 +0,0 @@
1
- # Ultralytics YOLOv5 🚀, AGPL-3.0 license
2
- """Utils to interact with the Triton Inference Server."""
3
-
4
- import typing
5
- from urllib.parse import urlparse
6
-
7
- import torch
8
-
9
-
10
- class TritonRemoteModel:
11
- """
12
- A wrapper over a model served by the Triton Inference Server.
13
-
14
- It can be configured to communicate over GRPC or HTTP. It accepts Torch Tensors as input and returns them as
15
- outputs.
16
- """
17
-
18
- def __init__(self, url: str):
19
- """
20
- Keyword Arguments:
21
- url: Fully qualified address of the Triton server - for e.g. grpc://localhost:8000.
22
- """
23
- parsed_url = urlparse(url)
24
- if parsed_url.scheme == "grpc":
25
- from tritonclient.grpc import InferenceServerClient, InferInput
26
-
27
- self.client = InferenceServerClient(parsed_url.netloc) # Triton GRPC client
28
- model_repository = self.client.get_model_repository_index()
29
- self.model_name = model_repository.models[0].name
30
- self.metadata = self.client.get_model_metadata(self.model_name, as_json=True)
31
-
32
- def create_input_placeholders() -> typing.List[InferInput]:
33
- return [
34
- InferInput(i["name"], [int(s) for s in i["shape"]], i["datatype"]) for i in self.metadata["inputs"]
35
- ]
36
-
37
- else:
38
- from tritonclient.http import InferenceServerClient, InferInput
39
-
40
- self.client = InferenceServerClient(parsed_url.netloc) # Triton HTTP client
41
- model_repository = self.client.get_model_repository_index()
42
- self.model_name = model_repository[0]["name"]
43
- self.metadata = self.client.get_model_metadata(self.model_name)
44
-
45
- def create_input_placeholders() -> typing.List[InferInput]:
46
- return [
47
- InferInput(i["name"], [int(s) for s in i["shape"]], i["datatype"]) for i in self.metadata["inputs"]
48
- ]
49
-
50
- self._create_input_placeholders_fn = create_input_placeholders
51
-
52
- @property
53
- def runtime(self):
54
- """Returns the model runtime."""
55
- return self.metadata.get("backend", self.metadata.get("platform"))
56
-
57
- def __call__(self, *args, **kwargs) -> typing.Union[torch.Tensor, typing.Tuple[torch.Tensor, ...]]:
58
- """
59
- Invokes the model.
60
-
61
- Parameters can be provided via args or kwargs. args, if provided, are assumed to match the order of inputs of
62
- the model. kwargs are matched with the model input names.
63
- """
64
- inputs = self._create_inputs(*args, **kwargs)
65
- response = self.client.infer(model_name=self.model_name, inputs=inputs)
66
- result = []
67
- for output in self.metadata["outputs"]:
68
- tensor = torch.as_tensor(response.as_numpy(output["name"]))
69
- result.append(tensor)
70
- return result[0] if len(result) == 1 else result
71
-
72
- def _create_inputs(self, *args, **kwargs):
73
- """Creates input tensors from args or kwargs, not both; raises error if none or both are provided."""
74
- args_len, kwargs_len = len(args), len(kwargs)
75
- if not args_len and not kwargs_len:
76
- raise RuntimeError("No inputs provided.")
77
- if args_len and kwargs_len:
78
- raise RuntimeError("Cannot specify args and kwargs at the same time")
79
-
80
- placeholders = self._create_input_placeholders_fn()
81
- if args_len:
82
- if args_len != len(placeholders):
83
- raise RuntimeError(f"Expected {len(placeholders)} inputs, got {args_len}.")
84
- for input, value in zip(placeholders, args):
85
- input.set_data_from_numpy(value.cpu().numpy())
86
- else:
87
- for input in placeholders:
88
- value = kwargs[input.name]
89
- input.set_data_from_numpy(value.cpu().numpy())
90
- return placeholders