Function to plot specific fields from training log(s). Plots both training and test results. :: Inputs - logs = list containing Path objects, each pointing to individual dir with a log file - fields = which results to plot from each log file - plots both training and test for
(logs, fields=('class_error', 'loss_bbox_unscaled', 'mAP'), ewm_col=0, log_name='log.txt')
| 11 | |
| 12 | |
| 13 | def plot_logs(logs, fields=('class_error', 'loss_bbox_unscaled', 'mAP'), ewm_col=0, log_name='log.txt'): |
| 14 | ''' |
| 15 | Function to plot specific fields from training log(s). Plots both training and test results. |
| 16 | |
| 17 | :: Inputs - logs = list containing Path objects, each pointing to individual dir with a log file |
| 18 | - fields = which results to plot from each log file - plots both training and test for each field. |
| 19 | - ewm_col = optional, which column to use as the exponential weighted smoothing of the plots |
| 20 | - log_name = optional, name of log file if different than default 'log.txt'. |
| 21 | |
| 22 | :: Outputs - matplotlib plots of results in fields, color coded for each log file. |
| 23 | - solid lines are training results, dashed lines are test results. |
| 24 | |
| 25 | ''' |
| 26 | func_name = "plot_utils.py::plot_logs" |
| 27 | |
| 28 | # verify logs is a list of Paths (list[Paths]) or single Pathlib object Path, |
| 29 | # convert single Path to list to avoid 'not iterable' error |
| 30 | |
| 31 | if not isinstance(logs, list): |
| 32 | if isinstance(logs, PurePath): |
| 33 | logs = [logs] |
| 34 | print(f"{func_name} info: logs param expects a list argument, converted to list[Path].") |
| 35 | else: |
| 36 | raise ValueError(f"{func_name} - invalid argument for logs parameter.\n \ |
| 37 | Expect list[Path] or single Path obj, received {type(logs)}") |
| 38 | |
| 39 | # Quality checks - verify valid dir(s), that every item in list is Path object, and that log_name exists in each dir |
| 40 | for i, dir in enumerate(logs): |
| 41 | if not isinstance(dir, PurePath): |
| 42 | raise ValueError(f"{func_name} - non-Path object in logs argument of {type(dir)}: \n{dir}") |
| 43 | if not dir.exists(): |
| 44 | raise ValueError(f"{func_name} - invalid directory in logs argument:\n{dir}") |
| 45 | # verify log_name exists |
| 46 | fn = Path(dir / log_name) |
| 47 | if not fn.exists(): |
| 48 | print(f"-> missing {log_name}. Have you gotten to Epoch 1 in training?") |
| 49 | print(f"--> full path of missing log file: {fn}") |
| 50 | return |
| 51 | |
| 52 | # load log file(s) and plot |
| 53 | dfs = [pd.read_json(Path(p) / log_name, lines=True) for p in logs] |
| 54 | |
| 55 | fig, axs = plt.subplots(ncols=len(fields), figsize=(16, 5)) |
| 56 | |
| 57 | for df, color in zip(dfs, sns.color_palette(n_colors=len(logs))): |
| 58 | for j, field in enumerate(fields): |
| 59 | if field == 'mAP': |
| 60 | coco_eval = pd.DataFrame( |
| 61 | np.stack(df.test_coco_eval_bbox.dropna().values)[:, 1] |
| 62 | ).ewm(com=ewm_col).mean() |
| 63 | axs[j].plot(coco_eval, c=color) |
| 64 | else: |
| 65 | df.interpolate().ewm(com=ewm_col).mean().plot( |
| 66 | y=[f'train_{field}', f'test_{field}'], |
| 67 | ax=axs[j], |
| 68 | color=[color] * 2, |
| 69 | style=['-', '--'] |
| 70 | ) |