glenn-jocher pre-commit-ci[bot] commited on
Commit
80cfaf4
·
unverified ·
1 Parent(s): 09d1703

AutoAnchor and AutoBatch `LOGGER` (#5635)

Browse files

* AutoBatch, AutoAnchor `LOGGER`

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update autoanchor.py

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

Files changed (3) hide show
  1. utils/autoanchor.py +26 -24
  2. utils/autobatch.py +7 -7
  3. utils/plots.py +3 -3
utils/autoanchor.py CHANGED
@@ -10,7 +10,9 @@ import torch
10
  import yaml
11
  from tqdm import tqdm
12
 
13
- from utils.general import colorstr
 
 
14
 
15
 
16
  def check_anchor_order(m):
@@ -19,14 +21,12 @@ def check_anchor_order(m):
19
  da = a[-1] - a[0] # delta a
20
  ds = m.stride[-1] - m.stride[0] # delta s
21
  if da.sign() != ds.sign(): # same order
22
- print('Reversing anchor order')
23
  m.anchors[:] = m.anchors.flip(0)
24
 
25
 
26
  def check_anchors(dataset, model, thr=4.0, imgsz=640):
27
  # Check anchor fit to data, recompute if necessary
28
- prefix = colorstr('autoanchor: ')
29
- print(f'\n{prefix}Analyzing anchors... ', end='')
30
  m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect()
31
  shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True)
32
  scale = np.random.uniform(0.9, 1.1, size=(shapes.shape[0], 1)) # augment scale
@@ -42,23 +42,24 @@ def check_anchors(dataset, model, thr=4.0, imgsz=640):
42
 
43
  anchors = m.anchors.clone() * m.stride.to(m.anchors.device).view(-1, 1, 1) # current anchors
44
  bpr, aat = metric(anchors.cpu().view(-1, 2))
45
- print(f'anchors/target = {aat:.2f}, Best Possible Recall (BPR) = {bpr:.4f}', end='')
46
- if bpr < 0.98: # threshold to recompute
47
- print('. Attempting to improve anchors, please wait...')
 
 
48
  na = m.anchors.numel() // 2 # number of anchors
49
  try:
50
  anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False)
51
  except Exception as e:
52
- print(f'{prefix}ERROR: {e}')
53
  new_bpr = metric(anchors)[0]
54
  if new_bpr > bpr: # replace anchors
55
  anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors)
56
  m.anchors[:] = anchors.clone().view_as(m.anchors) / m.stride.to(m.anchors.device).view(-1, 1, 1) # loss
57
  check_anchor_order(m)
58
- print(f'{prefix}New anchors saved to model. Update model *.yaml to use these anchors in the future.')
59
  else:
60
- print(f'{prefix}Original anchors better than new anchors. Proceeding with original anchors.')
61
- print('') # newline
62
 
63
 
64
  def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True):
@@ -81,7 +82,6 @@ def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen
81
  from scipy.cluster.vq import kmeans
82
 
83
  thr = 1 / thr
84
- prefix = colorstr('autoanchor: ')
85
 
86
  def metric(k, wh): # compute metrics
87
  r = wh[:, None] / k[None]
@@ -93,15 +93,17 @@ def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen
93
  _, best = metric(torch.tensor(k, dtype=torch.float32), wh)
94
  return (best * (best > thr).float()).mean() # fitness
95
 
96
- def print_results(k):
97
  k = k[np.argsort(k.prod(1))] # sort small to large
98
  x, best = metric(k, wh0)
99
  bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr
100
- print(f'{prefix}thr={thr:.2f}: {bpr:.4f} best possible recall, {aat:.2f} anchors past thr')
101
- print(f'{prefix}n={n}, img_size={img_size}, metric_all={x.mean():.3f}/{best.mean():.3f}-mean/best, '
102
- f'past_thr={x[x > thr].mean():.3f}-mean: ', end='')
103
  for i, x in enumerate(k):
104
- print('%i,%i' % (round(x[0]), round(x[1])), end=', ' if i < len(k) - 1 else '\n') # use in *.cfg
 
 
105
  return k
106
 
107
  if isinstance(dataset, str): # *.yaml file
@@ -117,19 +119,19 @@ def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen
117
  # Filter
118
  i = (wh0 < 3.0).any(1).sum()
119
  if i:
120
- print(f'{prefix}WARNING: Extremely small objects found. {i} of {len(wh0)} labels are < 3 pixels in size.')
121
  wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels
122
  # wh = wh * (np.random.rand(wh.shape[0], 1) * 0.9 + 0.1) # multiply by random scale 0-1
123
 
124
  # Kmeans calculation
125
- print(f'{prefix}Running kmeans for {n} anchors on {len(wh)} points...')
126
  s = wh.std(0) # sigmas for whitening
127
  k, dist = kmeans(wh / s, n, iter=30) # points, mean distance
128
- assert len(k) == n, f'{prefix}ERROR: scipy.cluster.vq.kmeans requested {n} points but returned only {len(k)}'
129
  k *= s
130
  wh = torch.tensor(wh, dtype=torch.float32) # filtered
131
  wh0 = torch.tensor(wh0, dtype=torch.float32) # unfiltered
132
- k = print_results(k)
133
 
134
  # Plot
135
  # k, d = [None] * 20, [None] * 20
@@ -146,7 +148,7 @@ def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen
146
  # Evolve
147
  npr = np.random
148
  f, sh, mp, s = anchor_fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma
149
- pbar = tqdm(range(gen), desc=f'{prefix}Evolving anchors with Genetic Algorithm:') # progress bar
150
  for _ in pbar:
151
  v = np.ones(sh)
152
  while (v == 1).all(): # mutate until a change occurs (prevent duplicates)
@@ -155,8 +157,8 @@ def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen
155
  fg = anchor_fitness(kg)
156
  if fg > f:
157
  f, k = fg, kg.copy()
158
- pbar.desc = f'{prefix}Evolving anchors with Genetic Algorithm: fitness = {f:.4f}'
159
  if verbose:
160
- print_results(k)
161
 
162
  return print_results(k)
 
10
  import yaml
11
  from tqdm import tqdm
12
 
13
+ from utils.general import LOGGER, colorstr, emojis
14
+
15
+ PREFIX = colorstr('AutoAnchor: ')
16
 
17
 
18
  def check_anchor_order(m):
 
21
  da = a[-1] - a[0] # delta a
22
  ds = m.stride[-1] - m.stride[0] # delta s
23
  if da.sign() != ds.sign(): # same order
24
+ LOGGER.info(f'{PREFIX}Reversing anchor order')
25
  m.anchors[:] = m.anchors.flip(0)
26
 
27
 
28
  def check_anchors(dataset, model, thr=4.0, imgsz=640):
29
  # Check anchor fit to data, recompute if necessary
 
 
30
  m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect()
31
  shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True)
32
  scale = np.random.uniform(0.9, 1.1, size=(shapes.shape[0], 1)) # augment scale
 
42
 
43
  anchors = m.anchors.clone() * m.stride.to(m.anchors.device).view(-1, 1, 1) # current anchors
44
  bpr, aat = metric(anchors.cpu().view(-1, 2))
45
+ s = f'\n{PREFIX}{aat:.2f} anchors/target, {bpr:.3f} Best Possible Recall (BPR). '
46
+ if bpr > 0.98: # threshold to recompute
47
+ LOGGER.info(emojis(f'{s}Current anchors are a good fit to dataset ✅'))
48
+ else:
49
+ LOGGER.info(emojis(f'{s}Anchors are a poor fit to dataset ⚠️, attempting to improve...'))
50
  na = m.anchors.numel() // 2 # number of anchors
51
  try:
52
  anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False)
53
  except Exception as e:
54
+ LOGGER.info(f'{PREFIX}ERROR: {e}')
55
  new_bpr = metric(anchors)[0]
56
  if new_bpr > bpr: # replace anchors
57
  anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors)
58
  m.anchors[:] = anchors.clone().view_as(m.anchors) / m.stride.to(m.anchors.device).view(-1, 1, 1) # loss
59
  check_anchor_order(m)
60
+ LOGGER.info(f'{PREFIX}New anchors saved to model. Update model *.yaml to use these anchors in the future.')
61
  else:
62
+ LOGGER.info(f'{PREFIX}Original anchors better than new anchors. Proceeding with original anchors.')
 
63
 
64
 
65
  def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True):
 
82
  from scipy.cluster.vq import kmeans
83
 
84
  thr = 1 / thr
 
85
 
86
  def metric(k, wh): # compute metrics
87
  r = wh[:, None] / k[None]
 
93
  _, best = metric(torch.tensor(k, dtype=torch.float32), wh)
94
  return (best * (best > thr).float()).mean() # fitness
95
 
96
+ def print_results(k, verbose=True):
97
  k = k[np.argsort(k.prod(1))] # sort small to large
98
  x, best = metric(k, wh0)
99
  bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr
100
+ s = f'{PREFIX}thr={thr:.2f}: {bpr:.4f} best possible recall, {aat:.2f} anchors past thr\n' \
101
+ f'{PREFIX}n={n}, img_size={img_size}, metric_all={x.mean():.3f}/{best.mean():.3f}-mean/best, ' \
102
+ f'past_thr={x[x > thr].mean():.3f}-mean: '
103
  for i, x in enumerate(k):
104
+ s += '%i,%i, ' % (round(x[0]), round(x[1]))
105
+ if verbose:
106
+ LOGGER.info(s[:-2])
107
  return k
108
 
109
  if isinstance(dataset, str): # *.yaml file
 
119
  # Filter
120
  i = (wh0 < 3.0).any(1).sum()
121
  if i:
122
+ LOGGER.info(f'{PREFIX}WARNING: Extremely small objects found. {i} of {len(wh0)} labels are < 3 pixels in size.')
123
  wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels
124
  # wh = wh * (np.random.rand(wh.shape[0], 1) * 0.9 + 0.1) # multiply by random scale 0-1
125
 
126
  # Kmeans calculation
127
+ LOGGER.info(f'{PREFIX}Running kmeans for {n} anchors on {len(wh)} points...')
128
  s = wh.std(0) # sigmas for whitening
129
  k, dist = kmeans(wh / s, n, iter=30) # points, mean distance
130
+ assert len(k) == n, f'{PREFIX}ERROR: scipy.cluster.vq.kmeans requested {n} points but returned only {len(k)}'
131
  k *= s
132
  wh = torch.tensor(wh, dtype=torch.float32) # filtered
133
  wh0 = torch.tensor(wh0, dtype=torch.float32) # unfiltered
134
+ k = print_results(k, verbose=False)
135
 
136
  # Plot
137
  # k, d = [None] * 20, [None] * 20
 
148
  # Evolve
149
  npr = np.random
150
  f, sh, mp, s = anchor_fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma
151
+ pbar = tqdm(range(gen), desc=f'{PREFIX}Evolving anchors with Genetic Algorithm:') # progress bar
152
  for _ in pbar:
153
  v = np.ones(sh)
154
  while (v == 1).all(): # mutate until a change occurs (prevent duplicates)
 
157
  fg = anchor_fitness(kg)
158
  if fg > f:
159
  f, k = fg, kg.copy()
160
+ pbar.desc = f'{PREFIX}Evolving anchors with Genetic Algorithm: fitness = {f:.4f}'
161
  if verbose:
162
+ print_results(k, verbose)
163
 
164
  return print_results(k)
utils/autobatch.py CHANGED
@@ -9,7 +9,7 @@ import numpy as np
9
  import torch
10
  from torch.cuda import amp
11
 
12
- from utils.general import colorstr
13
  from utils.torch_utils import profile
14
 
15
 
@@ -27,11 +27,11 @@ def autobatch(model, imgsz=640, fraction=0.9, batch_size=16):
27
  # model = torch.hub.load('ultralytics/yolov5', 'yolov5s', autoshape=False)
28
  # print(autobatch(model))
29
 
30
- prefix = colorstr('autobatch: ')
31
- print(f'{prefix}Computing optimal batch size for --imgsz {imgsz}')
32
  device = next(model.parameters()).device # get model device
33
  if device.type == 'cpu':
34
- print(f'{prefix}CUDA not detected, using default CPU batch-size {batch_size}')
35
  return batch_size
36
 
37
  d = str(device).upper() # 'CUDA:0'
@@ -40,18 +40,18 @@ def autobatch(model, imgsz=640, fraction=0.9, batch_size=16):
40
  r = torch.cuda.memory_reserved(device) / 1024 ** 3 # (GiB)
41
  a = torch.cuda.memory_allocated(device) / 1024 ** 3 # (GiB)
42
  f = t - (r + a) # free inside reserved
43
- print(f'{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free')
44
 
45
  batch_sizes = [1, 2, 4, 8, 16]
46
  try:
47
  img = [torch.zeros(b, 3, imgsz, imgsz) for b in batch_sizes]
48
  y = profile(img, model, n=3, device=device)
49
  except Exception as e:
50
- print(f'{prefix}{e}')
51
 
52
  y = [x[2] for x in y if x] # memory [2]
53
  batch_sizes = batch_sizes[:len(y)]
54
  p = np.polyfit(batch_sizes, y, deg=1) # first degree polynomial fit
55
  b = int((f * fraction - p[1]) / p[0]) # y intercept (optimal batch size)
56
- print(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%)')
57
  return b
 
9
  import torch
10
  from torch.cuda import amp
11
 
12
+ from utils.general import LOGGER, colorstr
13
  from utils.torch_utils import profile
14
 
15
 
 
27
  # model = torch.hub.load('ultralytics/yolov5', 'yolov5s', autoshape=False)
28
  # print(autobatch(model))
29
 
30
+ prefix = colorstr('AutoBatch: ')
31
+ LOGGER.info(f'{prefix}Computing optimal batch size for --imgsz {imgsz}')
32
  device = next(model.parameters()).device # get model device
33
  if device.type == 'cpu':
34
+ LOGGER.info(f'{prefix}CUDA not detected, using default CPU batch-size {batch_size}')
35
  return batch_size
36
 
37
  d = str(device).upper() # 'CUDA:0'
 
40
  r = torch.cuda.memory_reserved(device) / 1024 ** 3 # (GiB)
41
  a = torch.cuda.memory_allocated(device) / 1024 ** 3 # (GiB)
42
  f = t - (r + a) # free inside reserved
43
+ LOGGER.info(f'{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free')
44
 
45
  batch_sizes = [1, 2, 4, 8, 16]
46
  try:
47
  img = [torch.zeros(b, 3, imgsz, imgsz) for b in batch_sizes]
48
  y = profile(img, model, n=3, device=device)
49
  except Exception as e:
50
+ LOGGER.warning(f'{prefix}{e}')
51
 
52
  y = [x[2] for x in y if x] # memory [2]
53
  batch_sizes = batch_sizes[:len(y)]
54
  p = np.polyfit(batch_sizes, y, deg=1) # first degree polynomial fit
55
  b = int((f * fraction - p[1]) / p[0]) # y intercept (optimal batch size)
56
+ LOGGER.info(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%)')
57
  return b
utils/plots.py CHANGED
@@ -17,8 +17,8 @@ import seaborn as sn
17
  import torch
18
  from PIL import Image, ImageDraw, ImageFont
19
 
20
- from utils.general import (Timeout, check_requirements, clip_coords, increment_path, is_ascii, is_chinese, try_except,
21
- user_config_dir, xywh2xyxy, xyxy2xywh)
22
  from utils.metrics import fitness
23
 
24
  # Settings
@@ -328,7 +328,7 @@ def plot_val_study(file='', dir='', x=None): # from utils.plots import *; plot_
328
  @Timeout(30) # known issue https://github.com/ultralytics/yolov5/issues/5611
329
  def plot_labels(labels, names=(), save_dir=Path('')):
330
  # plot dataset labels
331
- print('Plotting labels... ')
332
  c, b = labels[:, 0], labels[:, 1:].transpose() # classes, boxes
333
  nc = int(c.max() + 1) # number of classes
334
  x = pd.DataFrame(b.transpose(), columns=['x', 'y', 'width', 'height'])
 
17
  import torch
18
  from PIL import Image, ImageDraw, ImageFont
19
 
20
+ from utils.general import (LOGGER, Timeout, check_requirements, clip_coords, increment_path, is_ascii, is_chinese,
21
+ try_except, user_config_dir, xywh2xyxy, xyxy2xywh)
22
  from utils.metrics import fitness
23
 
24
  # Settings
 
328
  @Timeout(30) # known issue https://github.com/ultralytics/yolov5/issues/5611
329
  def plot_labels(labels, names=(), save_dir=Path('')):
330
  # plot dataset labels
331
+ LOGGER.info(f"Plotting labels to {save_dir / 'labels.jpg'}... ")
332
  c, b = labels[:, 0], labels[:, 1:].transpose() # classes, boxes
333
  nc = int(c.max() + 1) # number of classes
334
  x = pd.DataFrame(b.transpose(), columns=['x', 'y', 'width', 'height'])