glenn-jocher commited on
Commit
dd03b20
·
unverified ·
1 Parent(s): 1d1c056

colorstr() updates (#1909)

Browse files

* W&B ImportError message fix

* colorstr() updates

* colorstr() updates

* colorstr() default to 'blue', 'bold'

* train: magenta

* train: blue

Files changed (6) hide show
  1. test.py +3 -2
  2. train.py +11 -10
  3. utils/autoanchor.py +2 -2
  4. utils/datasets.py +27 -27
  5. utils/general.py +2 -3
  6. utils/plots.py +1 -1
test.py CHANGED
@@ -12,7 +12,7 @@ from tqdm import tqdm
12
  from models.experimental import attempt_load
13
  from utils.datasets import create_dataloader
14
  from utils.general import coco80_to_coco91_class, check_dataset, check_file, check_img_size, check_requirements, \
15
- box_iou, non_max_suppression, scale_coords, xyxy2xywh, xywh2xyxy, set_logging, increment_path
16
  from utils.loss import compute_loss
17
  from utils.metrics import ap_per_class, ConfusionMatrix
18
  from utils.plots import plot_images, output_to_target, plot_study_txt
@@ -86,7 +86,8 @@ def test(data,
86
  img = torch.zeros((1, 3, imgsz, imgsz), device=device) # init img
87
  _ = model(img.half() if half else img) if device.type != 'cpu' else None # run once
88
  path = data['test'] if opt.task == 'test' else data['val'] # path to val/test images
89
- dataloader = create_dataloader(path, imgsz, batch_size, model.stride.max(), opt, pad=0.5, rect=True)[0]
 
90
 
91
  seen = 0
92
  confusion_matrix = ConfusionMatrix(nc=nc)
 
12
  from models.experimental import attempt_load
13
  from utils.datasets import create_dataloader
14
  from utils.general import coco80_to_coco91_class, check_dataset, check_file, check_img_size, check_requirements, \
15
+ box_iou, non_max_suppression, scale_coords, xyxy2xywh, xywh2xyxy, set_logging, increment_path, colorstr
16
  from utils.loss import compute_loss
17
  from utils.metrics import ap_per_class, ConfusionMatrix
18
  from utils.plots import plot_images, output_to_target, plot_study_txt
 
86
  img = torch.zeros((1, 3, imgsz, imgsz), device=device) # init img
87
  _ = model(img.half() if half else img) if device.type != 'cpu' else None # run once
88
  path = data['test'] if opt.task == 'test' else data['val'] # path to val/test images
89
+ dataloader = create_dataloader(path, imgsz, batch_size, model.stride.max(), opt, pad=0.5, rect=True,
90
+ prefix=colorstr('test: ' if opt.task == 'test' else 'val: '))[0]
91
 
92
  seen = 0
93
  confusion_matrix = ConfusionMatrix(nc=nc)
train.py CHANGED
@@ -36,15 +36,9 @@ from utils.torch_utils import ModelEMA, select_device, intersect_dicts, torch_di
36
 
37
  logger = logging.getLogger(__name__)
38
 
39
- try:
40
- import wandb
41
- except ImportError:
42
- wandb = None
43
- logger.info("Install Weights & Biases for experiment logging via 'pip install wandb' (recommended)")
44
-
45
 
46
  def train(hyp, opt, device, tb_writer=None, wandb=None):
47
- logger.info(colorstr('blue', 'bold', 'Hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
48
  save_dir, epochs, batch_size, total_batch_size, weights, rank = \
49
  Path(opt.save_dir), opt.epochs, opt.batch_size, opt.total_batch_size, opt.weights, opt.global_rank
50
 
@@ -189,7 +183,7 @@ def train(hyp, opt, device, tb_writer=None, wandb=None):
189
  dataloader, dataset = create_dataloader(train_path, imgsz, batch_size, gs, opt,
190
  hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, rank=rank,
191
  world_size=opt.world_size, workers=opt.workers,
192
- image_weights=opt.image_weights, quad=opt.quad)
193
  mlc = np.concatenate(dataset.labels, 0)[:, 0].max() # max label class
194
  nb = len(dataloader) # number of batches
195
  assert mlc < nc, 'Label class %g exceeds nc=%g in %s. Possible class labels are 0-%g' % (mlc, nc, opt.data, nc - 1)
@@ -198,8 +192,9 @@ def train(hyp, opt, device, tb_writer=None, wandb=None):
198
  if rank in [-1, 0]:
199
  ema.updates = start_epoch * nb // accumulate # set EMA updates
200
  testloader = create_dataloader(test_path, imgsz_test, total_batch_size, gs, opt, # testloader
201
- hyp=hyp, cache=opt.cache_images and not opt.notest, rect=True,
202
- rank=-1, world_size=opt.world_size, workers=opt.workers, pad=0.5)[0]
 
203
 
204
  if not opt.resume:
205
  labels = np.concatenate(dataset.labels, 0)
@@ -514,6 +509,12 @@ if __name__ == '__main__':
514
 
515
  # Train
516
  logger.info(opt)
 
 
 
 
 
 
517
  if not opt.evolve:
518
  tb_writer = None # init loggers
519
  if opt.global_rank in [-1, 0]:
 
36
 
37
  logger = logging.getLogger(__name__)
38
 
 
 
 
 
 
 
39
 
40
  def train(hyp, opt, device, tb_writer=None, wandb=None):
41
+ logger.info(colorstr('Hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
42
  save_dir, epochs, batch_size, total_batch_size, weights, rank = \
43
  Path(opt.save_dir), opt.epochs, opt.batch_size, opt.total_batch_size, opt.weights, opt.global_rank
44
 
 
183
  dataloader, dataset = create_dataloader(train_path, imgsz, batch_size, gs, opt,
184
  hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, rank=rank,
185
  world_size=opt.world_size, workers=opt.workers,
186
+ image_weights=opt.image_weights, quad=opt.quad, prefix=colorstr('train: '))
187
  mlc = np.concatenate(dataset.labels, 0)[:, 0].max() # max label class
188
  nb = len(dataloader) # number of batches
189
  assert mlc < nc, 'Label class %g exceeds nc=%g in %s. Possible class labels are 0-%g' % (mlc, nc, opt.data, nc - 1)
 
192
  if rank in [-1, 0]:
193
  ema.updates = start_epoch * nb // accumulate # set EMA updates
194
  testloader = create_dataloader(test_path, imgsz_test, total_batch_size, gs, opt, # testloader
195
+ hyp=hyp, cache=opt.cache_images and not opt.notest, rect=True, rank=-1,
196
+ world_size=opt.world_size, workers=opt.workers,
197
+ pad=0.5, prefix=colorstr('val: '))[0]
198
 
199
  if not opt.resume:
200
  labels = np.concatenate(dataset.labels, 0)
 
509
 
510
  # Train
511
  logger.info(opt)
512
+ try:
513
+ import wandb
514
+ except ImportError:
515
+ wandb = None
516
+ prefix = colorstr('wandb: ')
517
+ logger.info(f"{prefix}Install Weights & Biases for YOLOv5 logging with 'pip install wandb' (recommended)")
518
  if not opt.evolve:
519
  tb_writer = None # init loggers
520
  if opt.global_rank in [-1, 0]:
utils/autoanchor.py CHANGED
@@ -22,7 +22,7 @@ def check_anchor_order(m):
22
 
23
  def check_anchors(dataset, model, thr=4.0, imgsz=640):
24
  # Check anchor fit to data, recompute if necessary
25
- prefix = colorstr('blue', 'bold', 'autoanchor') + ': '
26
  print(f'\n{prefix}Analyzing anchors... ', end='')
27
  m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect()
28
  shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True)
@@ -73,7 +73,7 @@ def kmean_anchors(path='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=10
73
  from utils.autoanchor import *; _ = kmean_anchors()
74
  """
75
  thr = 1. / thr
76
- prefix = colorstr('blue', 'bold', 'autoanchor') + ': '
77
 
78
  def metric(k, wh): # compute metrics
79
  r = wh[:, None] / k[None]
 
22
 
23
  def check_anchors(dataset, model, thr=4.0, imgsz=640):
24
  # Check anchor fit to data, recompute if necessary
25
+ prefix = colorstr('autoanchor: ')
26
  print(f'\n{prefix}Analyzing anchors... ', end='')
27
  m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect()
28
  shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True)
 
73
  from utils.autoanchor import *; _ = kmean_anchors()
74
  """
75
  thr = 1. / thr
76
+ prefix = colorstr('autoanchor: ')
77
 
78
  def metric(k, wh): # compute metrics
79
  r = wh[:, None] / k[None]
utils/datasets.py CHANGED
@@ -56,7 +56,7 @@ def exif_size(img):
56
 
57
 
58
  def create_dataloader(path, imgsz, batch_size, stride, opt, hyp=None, augment=False, cache=False, pad=0.0, rect=False,
59
- rank=-1, world_size=1, workers=8, image_weights=False, quad=False):
60
  # Make sure only the first process in DDP process the dataset first, and the following others can use the cache
61
  with torch_distributed_zero_first(rank):
62
  dataset = LoadImagesAndLabels(path, imgsz, batch_size,
@@ -67,8 +67,8 @@ def create_dataloader(path, imgsz, batch_size, stride, opt, hyp=None, augment=Fa
67
  single_cls=opt.single_cls,
68
  stride=int(stride),
69
  pad=pad,
70
- rank=rank,
71
- image_weights=image_weights)
72
 
73
  batch_size = min(batch_size, len(dataset))
74
  nw = min([os.cpu_count() // world_size, batch_size if batch_size > 1 else 0, workers]) # number of workers
@@ -129,7 +129,7 @@ class LoadImages: # for inference
129
  elif os.path.isfile(p):
130
  files = [p] # files
131
  else:
132
- raise Exception('ERROR: %s does not exist' % p)
133
 
134
  images = [x for x in files if x.split('.')[-1].lower() in img_formats]
135
  videos = [x for x in files if x.split('.')[-1].lower() in vid_formats]
@@ -144,8 +144,8 @@ class LoadImages: # for inference
144
  self.new_video(videos[0]) # new video
145
  else:
146
  self.cap = None
147
- assert self.nf > 0, 'No images or videos found in %s. Supported formats are:\nimages: %s\nvideos: %s' % \
148
- (p, img_formats, vid_formats)
149
 
150
  def __iter__(self):
151
  self.count = 0
@@ -171,14 +171,14 @@ class LoadImages: # for inference
171
  ret_val, img0 = self.cap.read()
172
 
173
  self.frame += 1
174
- print('video %g/%g (%g/%g) %s: ' % (self.count + 1, self.nf, self.frame, self.nframes, path), end='')
175
 
176
  else:
177
  # Read image
178
  self.count += 1
179
  img0 = cv2.imread(path) # BGR
180
  assert img0 is not None, 'Image Not Found ' + path
181
- print('image %g/%g %s: ' % (self.count, self.nf, path), end='')
182
 
183
  # Padded resize
184
  img = letterbox(img0, new_shape=self.img_size)[0]
@@ -238,9 +238,9 @@ class LoadWebcam: # for inference
238
  break
239
 
240
  # Print
241
- assert ret_val, 'Camera Error %s' % self.pipe
242
  img_path = 'webcam.jpg'
243
- print('webcam %g: ' % self.count, end='')
244
 
245
  # Padded resize
246
  img = letterbox(img0, new_shape=self.img_size)[0]
@@ -271,15 +271,15 @@ class LoadStreams: # multiple IP or RTSP cameras
271
  self.sources = [clean_str(x) for x in sources] # clean source names for later
272
  for i, s in enumerate(sources):
273
  # Start the thread to read frames from the video stream
274
- print('%g/%g: %s... ' % (i + 1, n, s), end='')
275
  cap = cv2.VideoCapture(eval(s) if s.isnumeric() else s)
276
- assert cap.isOpened(), 'Failed to open %s' % s
277
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
278
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
279
  fps = cap.get(cv2.CAP_PROP_FPS) % 100
280
  _, self.imgs[i] = cap.read() # guarantee first frame
281
  thread = Thread(target=self.update, args=([i, cap]), daemon=True)
282
- print(' success (%gx%g at %.2f FPS).' % (w, h, fps))
283
  thread.start()
284
  print('') # newline
285
 
@@ -336,7 +336,7 @@ def img2label_paths(img_paths):
336
 
337
  class LoadImagesAndLabels(Dataset): # for training/testing
338
  def __init__(self, path, img_size=640, batch_size=16, augment=False, hyp=None, rect=False, image_weights=False,
339
- cache_images=False, single_cls=False, stride=32, pad=0.0, rank=-1):
340
  self.img_size = img_size
341
  self.augment = augment
342
  self.hyp = hyp
@@ -358,11 +358,11 @@ class LoadImagesAndLabels(Dataset): # for training/testing
358
  parent = str(p.parent) + os.sep
359
  f += [x.replace('./', parent) if x.startswith('./') else x for x in t] # local to global path
360
  else:
361
- raise Exception('%s does not exist' % p)
362
  self.img_files = sorted([x.replace('/', os.sep) for x in f if x.split('.')[-1].lower() in img_formats])
363
- assert self.img_files, 'No images found'
364
  except Exception as e:
365
- raise Exception('Error loading data from %s: %s\nSee %s' % (path, e, help_url))
366
 
367
  # Check cache
368
  self.label_files = img2label_paths(self.img_files) # labels
@@ -370,15 +370,15 @@ class LoadImagesAndLabels(Dataset): # for training/testing
370
  if cache_path.is_file():
371
  cache = torch.load(cache_path) # load
372
  if cache['hash'] != get_hash(self.label_files + self.img_files) or 'results' not in cache: # changed
373
- cache = self.cache_labels(cache_path) # re-cache
374
  else:
375
- cache = self.cache_labels(cache_path) # cache
376
 
377
  # Display cache
378
  [nf, nm, ne, nc, n] = cache.pop('results') # found, missing, empty, corrupted, total
379
  desc = f"Scanning '{cache_path}' for images and labels... {nf} found, {nm} missing, {ne} empty, {nc} corrupted"
380
- tqdm(None, desc=desc, total=n, initial=n)
381
- assert nf > 0 or not augment, f'No labels found in {cache_path}. Can not train without labels. See {help_url}'
382
 
383
  # Read cache
384
  cache.pop('hash') # remove hash
@@ -432,9 +432,9 @@ class LoadImagesAndLabels(Dataset): # for training/testing
432
  for i, x in pbar:
433
  self.imgs[i], self.img_hw0[i], self.img_hw[i] = x # img, hw_original, hw_resized = load_image(self, i)
434
  gb += self.imgs[i].nbytes
435
- pbar.desc = 'Caching images (%.1fGB)' % (gb / 1E9)
436
 
437
- def cache_labels(self, path=Path('./labels.cache')):
438
  # Cache dataset labels, check images and read shapes
439
  x = {} # dict
440
  nm, nf, ne, nc = 0, 0, 0, 0 # number missing, found, empty, duplicate
@@ -466,18 +466,18 @@ class LoadImagesAndLabels(Dataset): # for training/testing
466
  x[im_file] = [l, shape]
467
  except Exception as e:
468
  nc += 1
469
- print('WARNING: Ignoring corrupted image and/or label %s: %s' % (im_file, e))
470
 
471
- pbar.desc = f"Scanning '{path.parent / path.stem}' for images and labels... " \
472
  f"{nf} found, {nm} missing, {ne} empty, {nc} corrupted"
473
 
474
  if nf == 0:
475
- print(f'WARNING: No labels found in {path}. See {help_url}')
476
 
477
  x['hash'] = get_hash(self.label_files + self.img_files)
478
  x['results'] = [nf, nm, ne, nc, i + 1]
479
  torch.save(x, path) # save for next time
480
- logging.info(f"New cache created: {path}")
481
  return x
482
 
483
  def __len__(self):
 
56
 
57
 
58
  def create_dataloader(path, imgsz, batch_size, stride, opt, hyp=None, augment=False, cache=False, pad=0.0, rect=False,
59
+ rank=-1, world_size=1, workers=8, image_weights=False, quad=False, prefix=''):
60
  # Make sure only the first process in DDP process the dataset first, and the following others can use the cache
61
  with torch_distributed_zero_first(rank):
62
  dataset = LoadImagesAndLabels(path, imgsz, batch_size,
 
67
  single_cls=opt.single_cls,
68
  stride=int(stride),
69
  pad=pad,
70
+ image_weights=image_weights,
71
+ prefix=prefix)
72
 
73
  batch_size = min(batch_size, len(dataset))
74
  nw = min([os.cpu_count() // world_size, batch_size if batch_size > 1 else 0, workers]) # number of workers
 
129
  elif os.path.isfile(p):
130
  files = [p] # files
131
  else:
132
+ raise Exception(f'ERROR: {p} does not exist')
133
 
134
  images = [x for x in files if x.split('.')[-1].lower() in img_formats]
135
  videos = [x for x in files if x.split('.')[-1].lower() in vid_formats]
 
144
  self.new_video(videos[0]) # new video
145
  else:
146
  self.cap = None
147
+ assert self.nf > 0, f'No images or videos found in {p}. ' \
148
+ f'Supported formats are:\nimages: {img_formats}\nvideos: {vid_formats}'
149
 
150
  def __iter__(self):
151
  self.count = 0
 
171
  ret_val, img0 = self.cap.read()
172
 
173
  self.frame += 1
174
+ print(f'video {self.count + 1}/{self.nf} ({self.frame}/{self.nframes}) {path}: ', end='')
175
 
176
  else:
177
  # Read image
178
  self.count += 1
179
  img0 = cv2.imread(path) # BGR
180
  assert img0 is not None, 'Image Not Found ' + path
181
+ print(f'image {self.count}/{self.nf} {path}: ', end='')
182
 
183
  # Padded resize
184
  img = letterbox(img0, new_shape=self.img_size)[0]
 
238
  break
239
 
240
  # Print
241
+ assert ret_val, f'Camera Error {self.pipe}'
242
  img_path = 'webcam.jpg'
243
+ print(f'webcam {self.count}: ', end='')
244
 
245
  # Padded resize
246
  img = letterbox(img0, new_shape=self.img_size)[0]
 
271
  self.sources = [clean_str(x) for x in sources] # clean source names for later
272
  for i, s in enumerate(sources):
273
  # Start the thread to read frames from the video stream
274
+ print(f'{i + 1}/{n}: {s}... ', end='')
275
  cap = cv2.VideoCapture(eval(s) if s.isnumeric() else s)
276
+ assert cap.isOpened(), f'Failed to open {s}'
277
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
278
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
279
  fps = cap.get(cv2.CAP_PROP_FPS) % 100
280
  _, self.imgs[i] = cap.read() # guarantee first frame
281
  thread = Thread(target=self.update, args=([i, cap]), daemon=True)
282
+ print(f' success ({w}x{h} at {fps:.2f} FPS).')
283
  thread.start()
284
  print('') # newline
285
 
 
336
 
337
  class LoadImagesAndLabels(Dataset): # for training/testing
338
  def __init__(self, path, img_size=640, batch_size=16, augment=False, hyp=None, rect=False, image_weights=False,
339
+ cache_images=False, single_cls=False, stride=32, pad=0.0, prefix=''):
340
  self.img_size = img_size
341
  self.augment = augment
342
  self.hyp = hyp
 
358
  parent = str(p.parent) + os.sep
359
  f += [x.replace('./', parent) if x.startswith('./') else x for x in t] # local to global path
360
  else:
361
+ raise Exception(f'{prefix}{p} does not exist')
362
  self.img_files = sorted([x.replace('/', os.sep) for x in f if x.split('.')[-1].lower() in img_formats])
363
+ assert self.img_files, f'{prefix}No images found'
364
  except Exception as e:
365
+ raise Exception(f'{prefix}Error loading data from {path}: {e}\nSee {help_url}')
366
 
367
  # Check cache
368
  self.label_files = img2label_paths(self.img_files) # labels
 
370
  if cache_path.is_file():
371
  cache = torch.load(cache_path) # load
372
  if cache['hash'] != get_hash(self.label_files + self.img_files) or 'results' not in cache: # changed
373
+ cache = self.cache_labels(cache_path, prefix) # re-cache
374
  else:
375
+ cache = self.cache_labels(cache_path, prefix) # cache
376
 
377
  # Display cache
378
  [nf, nm, ne, nc, n] = cache.pop('results') # found, missing, empty, corrupted, total
379
  desc = f"Scanning '{cache_path}' for images and labels... {nf} found, {nm} missing, {ne} empty, {nc} corrupted"
380
+ tqdm(None, desc=prefix + desc, total=n, initial=n)
381
+ assert nf > 0 or not augment, f'{prefix}No labels in {cache_path}. Can not train without labels. See {help_url}'
382
 
383
  # Read cache
384
  cache.pop('hash') # remove hash
 
432
  for i, x in pbar:
433
  self.imgs[i], self.img_hw0[i], self.img_hw[i] = x # img, hw_original, hw_resized = load_image(self, i)
434
  gb += self.imgs[i].nbytes
435
+ pbar.desc = f'{prefix}Caching images ({gb / 1E9:.1f}GB)'
436
 
437
+ def cache_labels(self, path=Path('./labels.cache'), prefix=''):
438
  # Cache dataset labels, check images and read shapes
439
  x = {} # dict
440
  nm, nf, ne, nc = 0, 0, 0, 0 # number missing, found, empty, duplicate
 
466
  x[im_file] = [l, shape]
467
  except Exception as e:
468
  nc += 1
469
+ print(f'{prefix}WARNING: Ignoring corrupted image and/or label {im_file}: {e}')
470
 
471
+ pbar.desc = f"{prefix}Scanning '{path.parent / path.stem}' for images and labels... " \
472
  f"{nf} found, {nm} missing, {ne} empty, {nc} corrupted"
473
 
474
  if nf == 0:
475
+ print(f'{prefix}WARNING: No labels found in {path}. See {help_url}')
476
 
477
  x['hash'] = get_hash(self.label_files + self.img_files)
478
  x['results'] = [nf, nm, ne, nc, i + 1]
479
  torch.save(x, path) # save for next time
480
+ logging.info(f'{prefix}New cache created: {path}')
481
  return x
482
 
483
  def __len__(self):
utils/general.py CHANGED
@@ -118,7 +118,7 @@ def one_cycle(y1=0.0, y2=1.0, steps=100):
118
 
119
  def colorstr(*input):
120
  # Colors a string https://en.wikipedia.org/wiki/ANSI_escape_code, i.e. colorstr('blue', 'hello world')
121
- *prefix, string = input # color arguments, string
122
  colors = {'black': '\033[30m', # basic colors
123
  'red': '\033[31m',
124
  'green': '\033[32m',
@@ -138,8 +138,7 @@ def colorstr(*input):
138
  'end': '\033[0m', # misc
139
  'bold': '\033[1m',
140
  'underline': '\033[4m'}
141
-
142
- return ''.join(colors[x] for x in prefix) + f'{string}' + colors['end']
143
 
144
 
145
  def labels_to_class_weights(labels, nc=80):
 
118
 
119
  def colorstr(*input):
120
  # Colors a string https://en.wikipedia.org/wiki/ANSI_escape_code, i.e. colorstr('blue', 'hello world')
121
+ *args, string = input if len(input) > 1 else ('blue', 'bold', input[0]) # color arguments, string
122
  colors = {'black': '\033[30m', # basic colors
123
  'red': '\033[31m',
124
  'green': '\033[32m',
 
138
  'end': '\033[0m', # misc
139
  'bold': '\033[1m',
140
  'underline': '\033[4m'}
141
+ return ''.join(colors[x] for x in args) + f'{string}' + colors['end']
 
142
 
143
 
144
  def labels_to_class_weights(labels, nc=80):
utils/plots.py CHANGED
@@ -245,9 +245,9 @@ def plot_study_txt(path='study/', x=None): # from utils.plots import *; plot_st
245
  'k.-', linewidth=2, markersize=8, alpha=.25, label='EfficientDet')
246
 
247
  ax2.grid()
 
248
  ax2.set_xlim(0, 30)
249
  ax2.set_ylim(29, 51)
250
- ax2.set_yticks(np.arange(30, 55, 5))
251
  ax2.set_xlabel('GPU Speed (ms/img)')
252
  ax2.set_ylabel('COCO AP val')
253
  ax2.legend(loc='lower right')
 
245
  'k.-', linewidth=2, markersize=8, alpha=.25, label='EfficientDet')
246
 
247
  ax2.grid()
248
+ ax2.set_yticks(np.arange(30, 60, 5))
249
  ax2.set_xlim(0, 30)
250
  ax2.set_ylim(29, 51)
 
251
  ax2.set_xlabel('GPU Speed (ms/img)')
252
  ax2.set_ylabel('COCO AP val')
253
  ax2.legend(loc='lower right')