CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hukaixuan19970627

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: hukaixuan19970627/yolov5_obb
Path: blob/master/utils/loggers/wandb/wandb_utils.py
Views: 475
1
"""Utilities and tools for tracking runs with Weights & Biases."""
2
3
import logging
4
import os
5
import sys
6
from contextlib import contextmanager
7
from pathlib import Path
8
from typing import Dict
9
10
import yaml
11
from tqdm import tqdm
12
13
FILE = Path(__file__).resolve()
14
ROOT = FILE.parents[3] # YOLOv5 root directory
15
if str(ROOT) not in sys.path:
16
sys.path.append(str(ROOT)) # add ROOT to PATH
17
18
from utils.datasets import LoadImagesAndLabels, img2label_paths
19
from utils.general import LOGGER, check_dataset, check_file
20
21
try:
22
import wandb
23
24
assert hasattr(wandb, '__version__') # verify package import not local dir
25
except (ImportError, AssertionError):
26
wandb = None
27
28
RANK = int(os.getenv('RANK', -1))
29
WANDB_ARTIFACT_PREFIX = 'wandb-artifact://'
30
31
32
def remove_prefix(from_string, prefix=WANDB_ARTIFACT_PREFIX):
33
return from_string[len(prefix):]
34
35
36
def check_wandb_config_file(data_config_file):
37
wandb_config = '_wandb.'.join(data_config_file.rsplit('.', 1)) # updated data.yaml path
38
if Path(wandb_config).is_file():
39
return wandb_config
40
return data_config_file
41
42
43
def check_wandb_dataset(data_file):
44
is_trainset_wandb_artifact = False
45
is_valset_wandb_artifact = False
46
if check_file(data_file) and data_file.endswith('.yaml'):
47
with open(data_file, errors='ignore') as f:
48
data_dict = yaml.safe_load(f)
49
is_trainset_wandb_artifact = (isinstance(data_dict['train'], str) and
50
data_dict['train'].startswith(WANDB_ARTIFACT_PREFIX))
51
is_valset_wandb_artifact = (isinstance(data_dict['val'], str) and
52
data_dict['val'].startswith(WANDB_ARTIFACT_PREFIX))
53
if is_trainset_wandb_artifact or is_valset_wandb_artifact:
54
return data_dict
55
else:
56
return check_dataset(data_file)
57
58
59
def get_run_info(run_path):
60
run_path = Path(remove_prefix(run_path, WANDB_ARTIFACT_PREFIX))
61
run_id = run_path.stem
62
project = run_path.parent.stem
63
entity = run_path.parent.parent.stem
64
model_artifact_name = 'run_' + run_id + '_model'
65
return entity, project, run_id, model_artifact_name
66
67
68
def check_wandb_resume(opt):
69
process_wandb_config_ddp_mode(opt) if RANK not in [-1, 0] else None
70
if isinstance(opt.resume, str):
71
if opt.resume.startswith(WANDB_ARTIFACT_PREFIX):
72
if RANK not in [-1, 0]: # For resuming DDP runs
73
entity, project, run_id, model_artifact_name = get_run_info(opt.resume)
74
api = wandb.Api()
75
artifact = api.artifact(entity + '/' + project + '/' + model_artifact_name + ':latest')
76
modeldir = artifact.download()
77
opt.weights = str(Path(modeldir) / "last.pt")
78
return True
79
return None
80
81
82
def process_wandb_config_ddp_mode(opt):
83
with open(check_file(opt.data), errors='ignore') as f:
84
data_dict = yaml.safe_load(f) # data dict
85
train_dir, val_dir = None, None
86
if isinstance(data_dict['train'], str) and data_dict['train'].startswith(WANDB_ARTIFACT_PREFIX):
87
api = wandb.Api()
88
train_artifact = api.artifact(remove_prefix(data_dict['train']) + ':' + opt.artifact_alias)
89
train_dir = train_artifact.download()
90
train_path = Path(train_dir) / 'data/images/'
91
data_dict['train'] = str(train_path)
92
93
if isinstance(data_dict['val'], str) and data_dict['val'].startswith(WANDB_ARTIFACT_PREFIX):
94
api = wandb.Api()
95
val_artifact = api.artifact(remove_prefix(data_dict['val']) + ':' + opt.artifact_alias)
96
val_dir = val_artifact.download()
97
val_path = Path(val_dir) / 'data/images/'
98
data_dict['val'] = str(val_path)
99
if train_dir or val_dir:
100
ddp_data_path = str(Path(val_dir) / 'wandb_local_data.yaml')
101
with open(ddp_data_path, 'w') as f:
102
yaml.safe_dump(data_dict, f)
103
opt.data = ddp_data_path
104
105
106
class WandbLogger():
107
"""Log training runs, datasets, models, and predictions to Weights & Biases.
108
109
This logger sends information to W&B at wandb.ai. By default, this information
110
includes hyperparameters, system configuration and metrics, model metrics,
111
and basic data metrics and analyses.
112
113
By providing additional command line arguments to train.py, datasets,
114
models and predictions can also be logged.
115
116
For more on how this logger is used, see the Weights & Biases documentation:
117
https://docs.wandb.com/guides/integrations/yolov5
118
"""
119
120
def __init__(self, opt, run_id=None, job_type='Training'):
121
"""
122
- Initialize WandbLogger instance
123
- Upload dataset if opt.upload_dataset is True
124
- Setup trainig processes if job_type is 'Training'
125
126
arguments:
127
opt (namespace) -- Commandline arguments for this run
128
run_id (str) -- Run ID of W&B run to be resumed
129
job_type (str) -- To set the job_type for this run
130
131
"""
132
# Pre-training routine --
133
self.job_type = job_type
134
self.wandb, self.wandb_run = wandb, None if not wandb else wandb.run
135
self.val_artifact, self.train_artifact = None, None
136
self.train_artifact_path, self.val_artifact_path = None, None
137
self.result_artifact = None
138
self.val_table, self.result_table = None, None
139
self.bbox_media_panel_images = []
140
self.val_table_path_map = None
141
self.max_imgs_to_log = 16
142
self.wandb_artifact_data_dict = None
143
self.data_dict = None
144
# It's more elegant to stick to 1 wandb.init call,
145
# but useful config data is overwritten in the WandbLogger's wandb.init call
146
if isinstance(opt.resume, str): # checks resume from artifact
147
if opt.resume.startswith(WANDB_ARTIFACT_PREFIX):
148
entity, project, run_id, model_artifact_name = get_run_info(opt.resume)
149
model_artifact_name = WANDB_ARTIFACT_PREFIX + model_artifact_name
150
assert wandb, 'install wandb to resume wandb runs'
151
# Resume wandb-artifact:// runs here| workaround for not overwriting wandb.config
152
self.wandb_run = wandb.init(id=run_id,
153
project=project,
154
entity=entity,
155
resume='allow',
156
allow_val_change=True)
157
opt.resume = model_artifact_name
158
elif self.wandb:
159
self.wandb_run = wandb.init(config=opt,
160
resume="allow",
161
project='YOLOv5' if opt.project == 'runs/train' else Path(opt.project).stem,
162
entity=opt.entity,
163
name=opt.name if opt.name != 'exp' else None,
164
job_type=job_type,
165
id=run_id,
166
allow_val_change=True) if not wandb.run else wandb.run
167
if self.wandb_run:
168
if self.job_type == 'Training':
169
if opt.upload_dataset:
170
if not opt.resume:
171
self.wandb_artifact_data_dict = self.check_and_upload_dataset(opt)
172
173
if opt.resume:
174
# resume from artifact
175
if isinstance(opt.resume, str) and opt.resume.startswith(WANDB_ARTIFACT_PREFIX):
176
self.data_dict = dict(self.wandb_run.config.data_dict)
177
else: # local resume
178
self.data_dict = check_wandb_dataset(opt.data)
179
else:
180
self.data_dict = check_wandb_dataset(opt.data)
181
self.wandb_artifact_data_dict = self.wandb_artifact_data_dict or self.data_dict
182
183
# write data_dict to config. useful for resuming from artifacts. Do this only when not resuming.
184
self.wandb_run.config.update({'data_dict': self.wandb_artifact_data_dict},
185
allow_val_change=True)
186
self.setup_training(opt)
187
188
if self.job_type == 'Dataset Creation':
189
self.wandb_run.config.update({"upload_dataset": True})
190
self.data_dict = self.check_and_upload_dataset(opt)
191
192
def check_and_upload_dataset(self, opt):
193
"""
194
Check if the dataset format is compatible and upload it as W&B artifact
195
196
arguments:
197
opt (namespace)-- Commandline arguments for current run
198
199
returns:
200
Updated dataset info dictionary where local dataset paths are replaced by WAND_ARFACT_PREFIX links.
201
"""
202
assert wandb, 'Install wandb to upload dataset'
203
config_path = self.log_dataset_artifact(opt.data,
204
opt.single_cls,
205
'YOLOv5' if opt.project == 'runs/train' else Path(opt.project).stem)
206
with open(config_path, errors='ignore') as f:
207
wandb_data_dict = yaml.safe_load(f)
208
return wandb_data_dict
209
210
def setup_training(self, opt):
211
"""
212
Setup the necessary processes for training YOLO models:
213
- Attempt to download model checkpoint and dataset artifacts if opt.resume stats with WANDB_ARTIFACT_PREFIX
214
- Update data_dict, to contain info of previous run if resumed and the paths of dataset artifact if downloaded
215
- Setup log_dict, initialize bbox_interval
216
217
arguments:
218
opt (namespace) -- commandline arguments for this run
219
220
"""
221
self.log_dict, self.current_epoch = {}, 0
222
self.bbox_interval = opt.bbox_interval
223
if isinstance(opt.resume, str):
224
modeldir, _ = self.download_model_artifact(opt)
225
if modeldir:
226
self.weights = Path(modeldir) / "last.pt"
227
config = self.wandb_run.config
228
opt.weights, opt.save_period, opt.batch_size, opt.bbox_interval, opt.epochs, opt.hyp = str(
229
self.weights), config.save_period, config.batch_size, config.bbox_interval, config.epochs, \
230
config.hyp
231
data_dict = self.data_dict
232
if self.val_artifact is None: # If --upload_dataset is set, use the existing artifact, don't download
233
self.train_artifact_path, self.train_artifact = self.download_dataset_artifact(data_dict.get('train'),
234
opt.artifact_alias)
235
self.val_artifact_path, self.val_artifact = self.download_dataset_artifact(data_dict.get('val'),
236
opt.artifact_alias)
237
238
if self.train_artifact_path is not None:
239
train_path = Path(self.train_artifact_path) / 'data/images/'
240
data_dict['train'] = str(train_path)
241
if self.val_artifact_path is not None:
242
val_path = Path(self.val_artifact_path) / 'data/images/'
243
data_dict['val'] = str(val_path)
244
245
if self.val_artifact is not None:
246
self.result_artifact = wandb.Artifact("run_" + wandb.run.id + "_progress", "evaluation")
247
columns = ["epoch", "id", "ground truth", "prediction"]
248
columns.extend(self.data_dict['names'])
249
self.result_table = wandb.Table(columns)
250
self.val_table = self.val_artifact.get("val")
251
if self.val_table_path_map is None:
252
self.map_val_table_path()
253
if opt.bbox_interval == -1:
254
self.bbox_interval = opt.bbox_interval = (opt.epochs // 10) if opt.epochs > 10 else 1
255
train_from_artifact = self.train_artifact_path is not None and self.val_artifact_path is not None
256
# Update the the data_dict to point to local artifacts dir
257
if train_from_artifact:
258
self.data_dict = data_dict
259
260
def download_dataset_artifact(self, path, alias):
261
"""
262
download the model checkpoint artifact if the path starts with WANDB_ARTIFACT_PREFIX
263
264
arguments:
265
path -- path of the dataset to be used for training
266
alias (str)-- alias of the artifact to be download/used for training
267
268
returns:
269
(str, wandb.Artifact) -- path of the downladed dataset and it's corresponding artifact object if dataset
270
is found otherwise returns (None, None)
271
"""
272
if isinstance(path, str) and path.startswith(WANDB_ARTIFACT_PREFIX):
273
artifact_path = Path(remove_prefix(path, WANDB_ARTIFACT_PREFIX) + ":" + alias)
274
dataset_artifact = wandb.use_artifact(artifact_path.as_posix().replace("\\", "/"))
275
assert dataset_artifact is not None, "'Error: W&B dataset artifact doesn\'t exist'"
276
datadir = dataset_artifact.download()
277
return datadir, dataset_artifact
278
return None, None
279
280
def download_model_artifact(self, opt):
281
"""
282
download the model checkpoint artifact if the resume path starts with WANDB_ARTIFACT_PREFIX
283
284
arguments:
285
opt (namespace) -- Commandline arguments for this run
286
"""
287
if opt.resume.startswith(WANDB_ARTIFACT_PREFIX):
288
model_artifact = wandb.use_artifact(remove_prefix(opt.resume, WANDB_ARTIFACT_PREFIX) + ":latest")
289
assert model_artifact is not None, 'Error: W&B model artifact doesn\'t exist'
290
modeldir = model_artifact.download()
291
epochs_trained = model_artifact.metadata.get('epochs_trained')
292
total_epochs = model_artifact.metadata.get('total_epochs')
293
is_finished = total_epochs is None
294
assert not is_finished, 'training is finished, can only resume incomplete runs.'
295
return modeldir, model_artifact
296
return None, None
297
298
def log_model(self, path, opt, epoch, fitness_score, best_model=False):
299
"""
300
Log the model checkpoint as W&B artifact
301
302
arguments:
303
path (Path) -- Path of directory containing the checkpoints
304
opt (namespace) -- Command line arguments for this run
305
epoch (int) -- Current epoch number
306
fitness_score (float) -- fitness score for current epoch
307
best_model (boolean) -- Boolean representing if the current checkpoint is the best yet.
308
"""
309
model_artifact = wandb.Artifact('run_' + wandb.run.id + '_model', type='model', metadata={
310
'original_url': str(path),
311
'epochs_trained': epoch + 1,
312
'save period': opt.save_period,
313
'project': opt.project,
314
'total_epochs': opt.epochs,
315
'fitness_score': fitness_score
316
})
317
model_artifact.add_file(str(path / 'last.pt'), name='last.pt')
318
wandb.log_artifact(model_artifact,
319
aliases=['latest', 'last', 'epoch ' + str(self.current_epoch), 'best' if best_model else ''])
320
LOGGER.info(f"Saving model artifact on epoch {epoch + 1}")
321
322
def log_dataset_artifact(self, data_file, single_cls, project, overwrite_config=False):
323
"""
324
Log the dataset as W&B artifact and return the new data file with W&B links
325
326
arguments:
327
data_file (str) -- the .yaml file with information about the dataset like - path, classes etc.
328
single_class (boolean) -- train multi-class data as single-class
329
project (str) -- project name. Used to construct the artifact path
330
overwrite_config (boolean) -- overwrites the data.yaml file if set to true otherwise creates a new
331
file with _wandb postfix. Eg -> data_wandb.yaml
332
333
returns:
334
the new .yaml file with artifact links. it can be used to start training directly from artifacts
335
"""
336
upload_dataset = self.wandb_run.config.upload_dataset
337
log_val_only = isinstance(upload_dataset, str) and upload_dataset == 'val'
338
self.data_dict = check_dataset(data_file) # parse and check
339
data = dict(self.data_dict)
340
nc, names = (1, ['item']) if single_cls else (int(data['nc']), data['names'])
341
names = {k: v for k, v in enumerate(names)} # to index dictionary
342
343
# log train set
344
if not log_val_only:
345
self.train_artifact = self.create_dataset_table(LoadImagesAndLabels(
346
data['train'], rect=True, batch_size=1), names, name='train') if data.get('train') else None
347
if data.get('train'):
348
data['train'] = WANDB_ARTIFACT_PREFIX + str(Path(project) / 'train')
349
350
self.val_artifact = self.create_dataset_table(LoadImagesAndLabels(
351
data['val'], rect=True, batch_size=1), names, name='val') if data.get('val') else None
352
if data.get('val'):
353
data['val'] = WANDB_ARTIFACT_PREFIX + str(Path(project) / 'val')
354
355
path = Path(data_file)
356
# create a _wandb.yaml file with artifacts links if both train and test set are logged
357
if not log_val_only:
358
path = (path.stem if overwrite_config else path.stem + '_wandb') + '.yaml' # updated data.yaml path
359
path = Path('data') / path
360
data.pop('download', None)
361
data.pop('path', None)
362
with open(path, 'w') as f:
363
yaml.safe_dump(data, f)
364
LOGGER.info(f"Created dataset config file {path}")
365
366
if self.job_type == 'Training': # builds correct artifact pipeline graph
367
if not log_val_only:
368
self.wandb_run.log_artifact(
369
self.train_artifact) # calling use_artifact downloads the dataset. NOT NEEDED!
370
self.wandb_run.use_artifact(self.val_artifact)
371
self.val_artifact.wait()
372
self.val_table = self.val_artifact.get('val')
373
self.map_val_table_path()
374
else:
375
self.wandb_run.log_artifact(self.train_artifact)
376
self.wandb_run.log_artifact(self.val_artifact)
377
return path
378
379
def map_val_table_path(self):
380
"""
381
Map the validation dataset Table like name of file -> it's id in the W&B Table.
382
Useful for - referencing artifacts for evaluation.
383
"""
384
self.val_table_path_map = {}
385
LOGGER.info("Mapping dataset")
386
for i, data in enumerate(tqdm(self.val_table.data)):
387
self.val_table_path_map[data[3]] = data[0]
388
389
def create_dataset_table(self, dataset: LoadImagesAndLabels, class_to_id: Dict[int, str], name: str = 'dataset'):
390
"""
391
Create and return W&B artifact containing W&B Table of the dataset.
392
393
arguments:
394
dataset -- instance of LoadImagesAndLabels class used to iterate over the data to build Table
395
class_to_id -- hash map that maps class ids to labels
396
name -- name of the artifact
397
398
returns:
399
dataset artifact to be logged or used
400
"""
401
# TODO: Explore multiprocessing to slpit this loop parallely| This is essential for speeding up the the logging
402
artifact = wandb.Artifact(name=name, type="dataset")
403
img_files = tqdm([dataset.path]) if isinstance(dataset.path, str) and Path(dataset.path).is_dir() else None
404
img_files = tqdm(dataset.img_files) if not img_files else img_files
405
for img_file in img_files:
406
if Path(img_file).is_dir():
407
artifact.add_dir(img_file, name='data/images')
408
labels_path = 'labels'.join(dataset.path.rsplit('images', 1))
409
artifact.add_dir(labels_path, name='data/labels')
410
else:
411
artifact.add_file(img_file, name='data/images/' + Path(img_file).name)
412
label_file = Path(img2label_paths([img_file])[0])
413
artifact.add_file(str(label_file),
414
name='data/labels/' + label_file.name) if label_file.exists() else None
415
table = wandb.Table(columns=["id", "train_image", "Classes", "name"])
416
class_set = wandb.Classes([{'id': id, 'name': name} for id, name in class_to_id.items()])
417
for si, (img, labels, paths, shapes) in enumerate(tqdm(dataset)):
418
box_data, img_classes = [], {}
419
for cls, *xywh in labels[:, 1:].tolist():
420
cls = int(cls)
421
box_data.append({"position": {"middle": [xywh[0], xywh[1]], "width": xywh[2], "height": xywh[3]},
422
"class_id": cls,
423
"box_caption": "%s" % (class_to_id[cls])})
424
img_classes[cls] = class_to_id[cls]
425
boxes = {"ground_truth": {"box_data": box_data, "class_labels": class_to_id}} # inference-space
426
table.add_data(si, wandb.Image(paths, classes=class_set, boxes=boxes), list(img_classes.values()),
427
Path(paths).name)
428
artifact.add(table, name)
429
return artifact
430
431
def log_training_progress(self, predn, path, names):
432
"""
433
Build evaluation Table. Uses reference from validation dataset table.
434
435
arguments:
436
predn (list): list of predictions in the native space in the format - [xmin, ymin, xmax, ymax, confidence, class]
437
path (str): local path of the current evaluation image
438
names (dict(int, str)): hash map that maps class ids to labels
439
"""
440
class_set = wandb.Classes([{'id': id, 'name': name} for id, name in names.items()])
441
box_data = []
442
avg_conf_per_class = [0] * len(self.data_dict['names'])
443
pred_class_count = {}
444
for *xyxy, conf, cls in predn.tolist():
445
if conf >= 0.25:
446
cls = int(cls)
447
box_data.append(
448
{"position": {"minX": xyxy[0], "minY": xyxy[1], "maxX": xyxy[2], "maxY": xyxy[3]},
449
"class_id": cls,
450
"box_caption": f"{names[cls]} {conf:.3f}",
451
"scores": {"class_score": conf},
452
"domain": "pixel"})
453
avg_conf_per_class[cls] += conf
454
455
if cls in pred_class_count:
456
pred_class_count[cls] += 1
457
else:
458
pred_class_count[cls] = 1
459
460
for pred_class in pred_class_count.keys():
461
avg_conf_per_class[pred_class] = avg_conf_per_class[pred_class] / pred_class_count[pred_class]
462
463
boxes = {"predictions": {"box_data": box_data, "class_labels": names}} # inference-space
464
id = self.val_table_path_map[Path(path).name]
465
self.result_table.add_data(self.current_epoch,
466
id,
467
self.val_table.data[id][1],
468
wandb.Image(self.val_table.data[id][1], boxes=boxes, classes=class_set),
469
*avg_conf_per_class
470
)
471
472
def val_one_image(self, pred, predn, path, names, im):
473
"""
474
Log validation data for one image. updates the result Table if validation dataset is uploaded and log bbox media panel
475
476
arguments:
477
pred (list): list of scaled predictions in the format - [xmin, ymin, xmax, ymax, confidence, class]
478
predn (list): list of predictions in the native space - [xmin, ymin, xmax, ymax, confidence, class]
479
path (str): local path of the current evaluation image
480
"""
481
if self.val_table and self.result_table: # Log Table if Val dataset is uploaded as artifact
482
self.log_training_progress(predn, path, names)
483
484
if len(self.bbox_media_panel_images) < self.max_imgs_to_log and self.current_epoch > 0:
485
if self.current_epoch % self.bbox_interval == 0:
486
box_data = [{"position": {"minX": xyxy[0], "minY": xyxy[1], "maxX": xyxy[2], "maxY": xyxy[3]},
487
"class_id": int(cls),
488
"box_caption": f"{names[cls]} {conf:.3f}",
489
"scores": {"class_score": conf},
490
"domain": "pixel"} for *xyxy, conf, cls in pred.tolist()]
491
boxes = {"predictions": {"box_data": box_data, "class_labels": names}} # inference-space
492
self.bbox_media_panel_images.append(wandb.Image(im, boxes=boxes, caption=path.name))
493
494
def log(self, log_dict):
495
"""
496
save the metrics to the logging dictionary
497
498
arguments:
499
log_dict (Dict) -- metrics/media to be logged in current step
500
"""
501
if self.wandb_run:
502
for key, value in log_dict.items():
503
self.log_dict[key] = value
504
505
def end_epoch(self, best_result=False):
506
"""
507
commit the log_dict, model artifacts and Tables to W&B and flush the log_dict.
508
509
arguments:
510
best_result (boolean): Boolean representing if the result of this evaluation is best or not
511
"""
512
if self.wandb_run:
513
with all_logging_disabled():
514
if self.bbox_media_panel_images:
515
self.log_dict["BoundingBoxDebugger"] = self.bbox_media_panel_images
516
try:
517
wandb.log(self.log_dict)
518
except BaseException as e:
519
LOGGER.info(
520
f"An error occurred in wandb logger. The training will proceed without interruption. More info\n{e}")
521
self.wandb_run.finish()
522
self.wandb_run = None
523
524
self.log_dict = {}
525
self.bbox_media_panel_images = []
526
if self.result_artifact:
527
self.result_artifact.add(self.result_table, 'result')
528
wandb.log_artifact(self.result_artifact, aliases=['latest', 'last', 'epoch ' + str(self.current_epoch),
529
('best' if best_result else '')])
530
531
wandb.log({"evaluation": self.result_table})
532
columns = ["epoch", "id", "ground truth", "prediction"]
533
columns.extend(self.data_dict['names'])
534
self.result_table = wandb.Table(columns)
535
self.result_artifact = wandb.Artifact("run_" + wandb.run.id + "_progress", "evaluation")
536
537
def finish_run(self):
538
"""
539
Log metrics if any and finish the current W&B run
540
"""
541
if self.wandb_run:
542
if self.log_dict:
543
with all_logging_disabled():
544
wandb.log(self.log_dict)
545
wandb.run.finish()
546
547
548
@contextmanager
549
def all_logging_disabled(highest_level=logging.CRITICAL):
550
""" source - https://gist.github.com/simon-weber/7853144
551
A context manager that will prevent any logging messages triggered during the body from being processed.
552
:param highest_level: the maximum logging level in use.
553
This would only need to be changed if a custom level greater than CRITICAL is defined.
554
"""
555
previous_level = logging.root.manager.disable
556
logging.disable(highest_level)
557
try:
558
yield
559
finally:
560
logging.disable(previous_level)
561
562