class_docstr: Logs epoch-level metrics to a CSV file
  
  :param file: (string) path to csv file
  :param separator: (string) delimiter for file
  :param append: (bool) whether to append result to existing file or make new file
class_docstr_tok: ["Logs","epoch-level","metrics","to","a","CSV","file",":","param","file",":","(","string",")","path","to","csv","file",":","param","separator",":","(","string",")","delimiter","for","file",":","param","append",":","(","bool",")","whether","to","append","result","to","existing","file","or","make","new","file"]
code: def on_epoch_end(self, epoch, logs=None):
      logs = logs or {}
      RK = {"num_batches", "num_epoch"}
  
      def handle_value(k):
          is_zero_dim_tensor = (
              isinstance(k, torch.Tensor) and k.dim() == 0
          )
          if isinstance(k, Iterable) and not is_zero_dim_tensor:
              return '"[%s]"' % (", ".join(map(str, k)))
          else:
              return k
  
      if not self.writer:
          self.keys = sorted(logs.keys())
  
          class CustomDialect(csv.excel):
              delimiter = self.sep
  
          self.writer = csv.DictWriter(
              self.csv_file,
              fieldnames=["epoch"]
              + [k for k in self.keys if k not in RK],
              dialect=CustomDialect,
          )
          if self.append_header:
              self.writer.writeheader()
  
      row_dict = OrderedDict({"epoch": epoch})
      row_dict.update(
          (key, handle_value(logs[key]))
          for key in self.keys
          if key not in RK
      )
      self.writer.writerow(row_dict)
      self.csv_file.flush()
  
file_path: pywick/pywick/callbacks/CSVLogger.py
imports: import csv
  import os
  from collections.abc import Iterable
  from collections import OrderedDict
  import torch
  from .None import Callback
label_desc: Use image node_id 3 for calling the CSVLogger obj's underlying member method code with example usage: obj.on_epoch_end(epoch, logs) and returns: unknown, k