emoji-safe default logging (#8888)
* emoji-safe default logging * implement everywhere
This commit is contained in:
1 parent
73ed7a5e7a
commit
8f28ea4945
6 files changed
+24
-22
No files matched your search
+4
-4
@@ -10,7 +10,7 @@ import torch
|
|||||||
import yaml
|
import yaml
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from utils.general import LOGGER, colorstr, emojis
|
from utils.general import LOGGER, colorstr
|
||||||
|
|
||||||
PREFIX = colorstr('AutoAnchor: ')
|
PREFIX = colorstr('AutoAnchor: ')
|
||||||
|
|
||||||
@@ -45,9 +45,9 @@ def check_anchors(dataset, model, thr=4.0, imgsz=640):
|
|||||||
bpr, aat = metric(anchors.cpu().view(-1, 2))
|
bpr, aat = metric(anchors.cpu().view(-1, 2))
|
||||||
s = f'\n{PREFIX}{aat:.2f} anchors/target, {bpr:.3f} Best Possible Recall (BPR). '
|
s = f'\n{PREFIX}{aat:.2f} anchors/target, {bpr:.3f} Best Possible Recall (BPR). '
|
||||||
if bpr > 0.98: # threshold to recompute
|
if bpr > 0.98: # threshold to recompute
|
||||||
LOGGER.info(emojis(f'{s}Current anchors are a good fit to dataset ✅'))
|
LOGGER.info(f'{s}Current anchors are a good fit to dataset ✅')
|
||||||
else:
|
else:
|
||||||
LOGGER.info(emojis(f'{s}Anchors are a poor fit to dataset ⚠️, attempting to improve...'))
|
LOGGER.info(f'{s}Anchors are a poor fit to dataset ⚠️, attempting to improve...')
|
||||||
na = m.anchors.numel() // 2 # number of anchors
|
na = m.anchors.numel() // 2 # number of anchors
|
||||||
try:
|
try:
|
||||||
anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False)
|
anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False)
|
||||||
@@ -62,7 +62,7 @@ def check_anchors(dataset, model, thr=4.0, imgsz=640):
|
|||||||
s = f'{PREFIX}Done ✅ (optional: update model *.yaml to use these anchors in the future)'
|
s = f'{PREFIX}Done ✅ (optional: update model *.yaml to use these anchors in the future)'
|
||||||
else:
|
else:
|
||||||
s = f'{PREFIX}Done ⚠️ (original anchors better than new anchors, proceeding with original anchors)'
|
s = f'{PREFIX}Done ⚠️ (original anchors better than new anchors, proceeding with original anchors)'
|
||||||
LOGGER.info(emojis(s))
|
LOGGER.info(s)
|
||||||
|
|
||||||
|
|
||||||
def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True):
|
def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True):
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ from copy import deepcopy
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from utils.general import LOGGER, colorstr, emojis
|
from utils.general import LOGGER, colorstr
|
||||||
from utils.torch_utils import profile
|
from utils.torch_utils import profile
|
||||||
|
|
||||||
|
|
||||||
@@ -62,5 +62,5 @@ def autobatch(model, imgsz=640, fraction=0.9, batch_size=16):
|
|||||||
b = batch_sizes[max(i - 1, 0)] # select prior safe point
|
b = batch_sizes[max(i - 1, 0)] # select prior safe point
|
||||||
|
|
||||||
fraction = np.polyval(p, b) / t # actual fraction predicted
|
fraction = np.polyval(p, b) / t # actual fraction predicted
|
||||||
LOGGER.info(emojis(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%) ✅'))
|
LOGGER.info(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%) ✅')
|
||||||
return b
|
return b
|
||||||
+11
-9
@@ -97,6 +97,8 @@ def set_logging(name=None, verbose=VERBOSE):
|
|||||||
|
|
||||||
set_logging() # run before defining LOGGER
|
set_logging() # run before defining LOGGER
|
||||||
LOGGER = logging.getLogger("yolov5") # define globally (used in train.py, val.py, detect.py, etc.)
|
LOGGER = logging.getLogger("yolov5") # define globally (used in train.py, val.py, detect.py, etc.)
|
||||||
|
for fn in LOGGER.info, LOGGER.warning:
|
||||||
|
_fn, fn = fn, lambda x: _fn(emojis(x)) # emoji safe logging
|
||||||
|
|
||||||
|
|
||||||
def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'):
|
def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'):
|
||||||
@@ -334,7 +336,7 @@ def check_git_status(repo='ultralytics/yolov5'):
|
|||||||
s += f"⚠️ YOLOv5 is out of date by {n} commit{'s' * (n > 1)}. Use `{pull}` or `git clone {url}` to update."
|
s += f"⚠️ YOLOv5 is out of date by {n} commit{'s' * (n > 1)}. Use `{pull}` or `git clone {url}` to update."
|
||||||
else:
|
else:
|
||||||
s += f'up to date with {url} ✅'
|
s += f'up to date with {url} ✅'
|
||||||
LOGGER.info(emojis(s)) # emoji-safe
|
LOGGER.info(s)
|
||||||
|
|
||||||
|
|
||||||
def check_python(minimum='3.7.0'):
|
def check_python(minimum='3.7.0'):
|
||||||
@@ -388,7 +390,7 @@ def check_requirements(requirements=ROOT / 'requirements.txt', exclude=(), insta
|
|||||||
source = file.resolve() if 'file' in locals() else requirements
|
source = file.resolve() if 'file' in locals() else requirements
|
||||||
s = f"{prefix} {n} package{'s' * (n > 1)} updated per {source}\n" \
|
s = f"{prefix} {n} package{'s' * (n > 1)} updated per {source}\n" \
|
||||||
f"{prefix} ⚠️ {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"
|
f"{prefix} ⚠️ {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"
|
||||||
LOGGER.info(emojis(s))
|
LOGGER.info(s)
|
||||||
|
|
||||||
|
|
||||||
def check_img_size(imgsz, s=32, floor=0):
|
def check_img_size(imgsz, s=32, floor=0):
|
||||||
@@ -489,9 +491,9 @@ def check_dataset(data, autodownload=True):
|
|||||||
|
|
||||||
# Checks
|
# Checks
|
||||||
for k in 'train', 'val', 'nc':
|
for k in 'train', 'val', 'nc':
|
||||||
assert k in data, emojis(f"data.yaml '{k}:' field missing ❌")
|
assert k in data, f"data.yaml '{k}:' field missing ❌"
|
||||||
if 'names' not in data:
|
if 'names' not in data:
|
||||||
LOGGER.warning(emojis("data.yaml 'names:' field missing ⚠️, assigning default names 'class0', 'class1', etc."))
|
LOGGER.warning("data.yaml 'names:' field missing ⚠️, assigning default names 'class0', 'class1', etc.")
|
||||||
data['names'] = [f'class{i}' for i in range(data['nc'])] # default names
|
data['names'] = [f'class{i}' for i in range(data['nc'])] # default names
|
||||||
|
|
||||||
# Resolve paths
|
# Resolve paths
|
||||||
@@ -507,9 +509,9 @@ def check_dataset(data, autodownload=True):
|
|||||||
if val:
|
if val:
|
||||||
val = [Path(x).resolve() for x in (val if isinstance(val, list) else [val])] # val path
|
val = [Path(x).resolve() for x in (val if isinstance(val, list) else [val])] # val path
|
||||||
if not all(x.exists() for x in val):
|
if not all(x.exists() for x in val):
|
||||||
LOGGER.info(emojis('\nDataset not found ⚠️, missing paths %s' % [str(x) for x in val if not x.exists()]))
|
LOGGER.info('\nDataset not found ⚠️, missing paths %s' % [str(x) for x in val if not x.exists()])
|
||||||
if not s or not autodownload:
|
if not s or not autodownload:
|
||||||
raise Exception(emojis('Dataset not found ❌'))
|
raise Exception('Dataset not found ❌')
|
||||||
t = time.time()
|
t = time.time()
|
||||||
root = path.parent if 'path' in data else '..' # unzip directory i.e. '../'
|
root = path.parent if 'path' in data else '..' # unzip directory i.e. '../'
|
||||||
if s.startswith('http') and s.endswith('.zip'): # URL
|
if s.startswith('http') and s.endswith('.zip'): # URL
|
||||||
@@ -527,7 +529,7 @@ def check_dataset(data, autodownload=True):
|
|||||||
r = exec(s, {'yaml': data}) # return None
|
r = exec(s, {'yaml': data}) # return None
|
||||||
dt = f'({round(time.time() - t, 1)}s)'
|
dt = f'({round(time.time() - t, 1)}s)'
|
||||||
s = f"success ✅ {dt}, saved to {colorstr('bold', root)}" if r in (0, None) else f"failure {dt} ❌"
|
s = f"success ✅ {dt}, saved to {colorstr('bold', root)}" if r in (0, None) else f"failure {dt} ❌"
|
||||||
LOGGER.info(emojis(f"Dataset download {s}"))
|
LOGGER.info(f"Dataset download {s}")
|
||||||
check_font('Arial.ttf' if is_ascii(data['names']) else 'Arial.Unicode.ttf', progress=True) # download fonts
|
check_font('Arial.ttf' if is_ascii(data['names']) else 'Arial.Unicode.ttf', progress=True) # download fonts
|
||||||
return data # dictionary
|
return data # dictionary
|
||||||
|
|
||||||
@@ -552,11 +554,11 @@ def check_amp(model):
|
|||||||
im = f if f.exists() else 'https://ultralytics.com/images/bus.jpg' if check_online() else np.ones((640, 640, 3))
|
im = f if f.exists() else 'https://ultralytics.com/images/bus.jpg' if check_online() else np.ones((640, 640, 3))
|
||||||
try:
|
try:
|
||||||
assert amp_allclose(model, im) or amp_allclose(DetectMultiBackend('yolov5n.pt', device), im)
|
assert amp_allclose(model, im) or amp_allclose(DetectMultiBackend('yolov5n.pt', device), im)
|
||||||
LOGGER.info(emojis(f'{prefix}checks passed ✅'))
|
LOGGER.info(f'{prefix}checks passed ✅')
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
help_url = 'https://github.com/ultralytics/yolov5/issues/7908'
|
help_url = 'https://github.com/ultralytics/yolov5/issues/7908'
|
||||||
LOGGER.warning(emojis(f'{prefix}checks failed ❌, disabling Automatic Mixed Precision. See {help_url}'))
|
LOGGER.warning(f'{prefix}checks failed ❌, disabling Automatic Mixed Precision. See {help_url}')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import pkg_resources as pkg
|
|||||||
import torch
|
import torch
|
||||||
from torch.utils.tensorboard import SummaryWriter
|
from torch.utils.tensorboard import SummaryWriter
|
||||||
|
|
||||||
from utils.general import colorstr, cv2, emojis
|
from utils.general import colorstr, cv2
|
||||||
from utils.loggers.clearml.clearml_utils import ClearmlLogger
|
from utils.loggers.clearml.clearml_utils import ClearmlLogger
|
||||||
from utils.loggers.wandb.wandb_utils import WandbLogger
|
from utils.loggers.wandb.wandb_utils import WandbLogger
|
||||||
from utils.plots import plot_images, plot_results
|
from utils.plots import plot_images, plot_results
|
||||||
@@ -73,11 +73,11 @@ class Loggers():
|
|||||||
if not wandb:
|
if not wandb:
|
||||||
prefix = colorstr('Weights & Biases: ')
|
prefix = colorstr('Weights & Biases: ')
|
||||||
s = f"{prefix}run 'pip install wandb' to automatically track and visualize YOLOv5 🚀 runs in Weights & Biases"
|
s = f"{prefix}run 'pip install wandb' to automatically track and visualize YOLOv5 🚀 runs in Weights & Biases"
|
||||||
self.logger.info(emojis(s))
|
self.logger.info(s)
|
||||||
if not clearml:
|
if not clearml:
|
||||||
prefix = colorstr('ClearML: ')
|
prefix = colorstr('ClearML: ')
|
||||||
s = f"{prefix}run 'pip install clearml' to automatically track, visualize and remotely train YOLOv5 🚀 runs in ClearML"
|
s = f"{prefix}run 'pip install clearml' to automatically track, visualize and remotely train YOLOv5 🚀 runs in ClearML"
|
||||||
self.logger.info(emojis(s))
|
self.logger.info(s)
|
||||||
|
|
||||||
# TensorBoard
|
# TensorBoard
|
||||||
s = self.save_dir
|
s = self.save_dir
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ def select_device(device='', batch_size=0, newline=True):
|
|||||||
|
|
||||||
if not newline:
|
if not newline:
|
||||||
s = s.rstrip()
|
s = s.rstrip()
|
||||||
LOGGER.info(s.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else s) # emoji-safe
|
LOGGER.info(s)
|
||||||
return torch.device(arg)
|
return torch.device(arg)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from models.common import DetectMultiBackend
|
|||||||
from utils.callbacks import Callbacks
|
from utils.callbacks import Callbacks
|
||||||
from utils.dataloaders import create_dataloader
|
from utils.dataloaders import create_dataloader
|
||||||
from utils.general import (LOGGER, check_dataset, check_img_size, check_requirements, check_yaml,
|
from utils.general import (LOGGER, check_dataset, check_img_size, check_requirements, check_yaml,
|
||||||
coco80_to_coco91_class, colorstr, emojis, increment_path, non_max_suppression, print_args,
|
coco80_to_coco91_class, colorstr, increment_path, non_max_suppression, print_args,
|
||||||
scale_coords, xywh2xyxy, xyxy2xywh)
|
scale_coords, xywh2xyxy, xyxy2xywh)
|
||||||
from utils.metrics import ConfusionMatrix, ap_per_class, box_iou
|
from utils.metrics import ConfusionMatrix, ap_per_class, box_iou
|
||||||
from utils.plots import output_to_target, plot_images, plot_val_study
|
from utils.plots import output_to_target, plot_images, plot_val_study
|
||||||
@@ -274,7 +274,7 @@ def run(
|
|||||||
pf = '%20s' + '%11i' * 2 + '%11.3g' * 4 # print format
|
pf = '%20s' + '%11i' * 2 + '%11.3g' * 4 # print format
|
||||||
LOGGER.info(pf % ('all', seen, nt.sum(), mp, mr, map50, map))
|
LOGGER.info(pf % ('all', seen, nt.sum(), mp, mr, map50, map))
|
||||||
if nt.sum() == 0:
|
if nt.sum() == 0:
|
||||||
LOGGER.warning(emojis(f'WARNING: no labels found in {task} set, can not compute metrics without labels ⚠️'))
|
LOGGER.warning(f'WARNING: no labels found in {task} set, can not compute metrics without labels ⚠️')
|
||||||
|
|
||||||
# Print results per class
|
# Print results per class
|
||||||
if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):
|
if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):
|
||||||
@@ -365,7 +365,7 @@ def main(opt):
|
|||||||
|
|
||||||
if opt.task in ('train', 'val', 'test'): # run normally
|
if opt.task in ('train', 'val', 'test'): # run normally
|
||||||
if opt.conf_thres > 0.001: # https://github.com/ultralytics/yolov5/issues/1466
|
if opt.conf_thres > 0.001: # https://github.com/ultralytics/yolov5/issues/1466
|
||||||
LOGGER.info(emojis(f'WARNING: confidence threshold {opt.conf_thres} > 0.001 produces invalid results ⚠️'))
|
LOGGER.info(f'WARNING: confidence threshold {opt.conf_thres} > 0.001 produces invalid results ⚠️')
|
||||||
run(**vars(opt))
|
run(**vars(opt))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in new issue
Block a user