Source code for neural_lam.custom_loggers

"""Custom logging utilities (e.g., MLFlow wrappers) used in Neural-LAM."""

# Standard library
import os
from pathlib import Path

# Third-party
import matplotlib.pyplot as plt
import pytorch_lightning as pl
from loguru import logger


[docs] class CustomMLFlowLogger(pl.loggers.MLFlowLogger): """ Custom MLFlow logger that adds the `log_image()` functionality not present in the default implementation from pytorch-lightning as of version `2.0.3` at least. """ def __init__( self, experiment_name: str, tracking_uri: str, run_name: str, save_dir: str, ) -> None: """ Initialize the logger, ensure ``save_dir`` exists, and start the MLflow run. Parameters ---------- experiment_name : str Target MLflow experiment. tracking_uri : str MLflow tracking server URI. run_name : str Human-readable run name stored as ``mlflow.runName``. save_dir : str Directory where ``log_image`` writes temporary figure files. Created eagerly with ``exist_ok=True``. Notes ----- Starts the MLflow run with ``log_system_metrics=True`` and also records ``run_id`` as an MLflow param. """ super().__init__( experiment_name=experiment_name, tracking_uri=tracking_uri ) self._save_dir = save_dir os.makedirs(self._save_dir, exist_ok=True) # mlflow is heavy and only needed with --logger mlflow # Third-party import mlflow mlflow.start_run(run_id=self.run_id, log_system_metrics=True) mlflow.set_tag("mlflow.runName", run_name) mlflow.log_param("run_id", self.run_id) @property def save_dir(self) -> str: """ Returns the directory where the MLFlow artifacts are saved. Used to define the path to save output when using the logger. Returns ------- str Path to the directory where the artifacts are saved. """ return self._save_dir
[docs] def log_image( self, key: str, images: list[plt.Figure], step: int | None = None, ) -> None: """ Log one or more matplotlib figures as images to MLFlow. When ``images`` contains more than one figure, each is logged under a key suffixed with its position in the list (``key_0``, ``key_1``, ...). A single-figure list is logged under the bare key. Parameters ---------- key : str Key to log the image under. If ``step`` is given, ``_{step}`` is appended before any per-figure index suffix. images : list of matplotlib.figure.Figure Figures to log. step : int or None, optional Step to associate with the log entry. ``None`` logs without a step suffix. Raises ------ botocore.exceptions.NoCredentialsError If AWS credentials for the MLflow artifact store are missing; re-raised for the caller to handle. """ # Third-party import mlflow from botocore.exceptions import NoCredentialsError from PIL import Image if step is not None: key = f"{key}_{step}" # Need to save each figure to a temporary file, then log it # mlflow.log_image should do this automatically, but is buggy for i, fig in enumerate(images): img_key = f"{key}_{i}" if len(images) > 1 else key temporary_image = os.path.join(self.save_dir, f"{img_key}.png") try: fig.savefig(temporary_image) with Image.open(temporary_image) as img: mlflow.log_image(img, f"{img_key}.png") except NoCredentialsError: logger.error("Error logging image\nSet AWS credentials") raise finally: if Path(temporary_image).exists(): Path(temporary_image).unlink()