code: def plot_logs(
      logs,
      fields=("class_error", "loss_bbox_unscaled", "mAP"),
      ewm_col=0,
      log_name="log.txt",
  ):
      """
      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 each field.
                - ewm_col = optional, which column to use as the exponential weighted smoothing of the plots
                - log_name = optional, name of log file if different than default 'log.txt'.
  
      :: Outputs - matplotlib plots of results in fields, color coded for each log file.
                 - solid lines are training results, dashed lines are test results.
  
      """
      func_name = "plot_utils.py::plot_logs"
  
      # verify logs is a list of Paths (list[Paths]) or single Pathlib object Path,
      # convert single Path to list to avoid 'not iterable' error
  
      if not isinstance(logs, list):
          if isinstance(logs, PurePath):
              logs = [logs]
              print(
                  f"{func_name} info: logs param expects a list argument, converted to list[Path]."
              )
          else:
              raise ValueError(
                  f"{func_name} - invalid argument for logs parameter.\n \
              Expect list[Path] or single Path obj, received {type(logs)}"
              )
  
      # Quality checks - verify valid dir(s), that every item in list is Path object, and that log_name exists in each dir
      for i, dir in enumerate(logs):
          if not isinstance(dir, PurePath):
              raise ValueError(
                  f"{func_name} - non-Path object in logs argument of {type(dir)}: \n{dir}"
              )
          if not dir.exists():
              raise ValueError(
                  f"{func_name} - invalid directory in logs argument:\n{dir}"
              )
          # verify log_name exists
          fn = Path(dir / log_name)
          if not fn.exists():
              print(
                  f"-> missing {log_name}.  Have you gotten to Epoch 1 in training?"
              )
              print(f"--> full path of missing log file: {fn}")
              return
  
      # load log file(s) and plot
      dfs = [pd.read_json(Path(p) / log_name, lines=True) for p in logs]
  
      fig, axs = plt.subplots(ncols=len(fields), figsize=(16, 5))
  
      for df, color in zip(dfs, sns.color_palette(n_colors=len(logs))):
          for j, field in enumerate(fields):
              if field == "mAP":
                  coco_eval = (
                      pd.DataFrame(
                          np.stack(
                              df.test_coco_eval_bbox.dropna().values
                          )[:, 1]
                      )
                      .ewm(com=ewm_col)
                      .mean()
                  )
                  axs[j].plot(coco_eval, c=color)
              else:
                  df.interpolate().ewm(com=ewm_col).mean().plot(
                      y=[f"train_{field}", f"test_{field}"],
                      ax=axs[j],
                      color=[color] * 2,
                      style=["-", "--"],
                  )
      for ax, field in zip(axs, fields):
          ax.legend([Path(p).name for p in logs])
          ax.set_title(field)
  
file_path: detr/util/plot_utils.py
imports: import torch
  import pandas
  import numpy
  import seaborn
  import matplotlib.pyplot
  from pathlib import Path, PurePath
label_desc: Use image node_id 1 for calling a global function with example usage: plot_logs(logs, fields, ewm_col, log_name) without return types