# Copyright (C) 2021-2025, Mindee. # This program is licensed under the Apache License 2.0. # See LICENSE or go to for full license details. import math import matplotlib.pyplot as plt import numpy as np def plot_samples(images, targets): # Unnormalize image num_samples = min(len(images), 12) num_cols = min(len(images), 4) num_rows = int(math.ceil(num_samples / num_cols)) _, axes = plt.subplots(num_rows, num_cols, figsize=(20, 5)) for idx in range(num_samples): img = (255 * images[idx].numpy()).round().clip(0, 255).astype(np.uint8) if img.shape[0] == 3 and img.shape[2] != 3: img = img.transpose(1, 2, 0) row_idx = idx // num_cols col_idx = idx % num_cols ax = axes[row_idx] if num_rows > 1 else axes ax = ax[col_idx] if num_cols > 1 else ax ax.imshow(img) ax.set_title(targets[idx]) # Disable axis for ax in axes.ravel(): ax.axis("off") plt.show() def plot_recorder(lr_recorder, loss_recorder, beta: float = 0.95, **kwargs) -> None: """Display the results of the LR grid search. Adapted from https://github.com/frgfm/Holocron/blob/master/holocron/trainer/core.py. Args: lr_recorder: list of LR values loss_recorder: list of loss values beta (float, optional): smoothing factor **kwargs: keyword arguments from `matplotlib.pyplot.show`. """ if len(lr_recorder) != len(loss_recorder) or len(lr_recorder) == 0: raise AssertionError("Both `lr_recorder` and `loss_recorder` should have the same length") # Exp moving average of loss smoothed_losses = [] avg_loss = 0.0 for idx, loss in enumerate(loss_recorder): avg_loss = beta * avg_loss + (1 - beta) * loss smoothed_losses.append(avg_loss / (1 - beta ** (idx + 1))) # Properly rescale Y-axis data_slice = slice( min(len(loss_recorder) // 10, 10), -min(len(loss_recorder) // 20, 5) if len(loss_recorder) >= 20 else len(loss_recorder), ) vals = np.array(smoothed_losses[data_slice]) min_idx = vals.argmin() max_val = vals.max() if min_idx is None else vals[: min_idx + 1].max() # type: ignore[misc] delta = max_val - vals[min_idx] plt.plot(lr_recorder[data_slice], smoothed_losses[data_slice]) plt.xscale("log") plt.xlabel("Learning Rate") plt.ylabel("Training loss") plt.ylim(vals[min_idx] - 0.1 * delta, max_val + 0.2 * delta) plt.grid(True, linestyle="--", axis="x") plt.show(**kwargs) class EarlyStopper: def __init__(self, patience: int = 5, min_delta: float = 0.01): self.patience = patience self.min_delta = min_delta self.counter = 0 self.min_validation_loss = float("inf") def early_stop(self, validation_loss: float) -> bool: if validation_loss < self.min_validation_loss: self.min_validation_loss = validation_loss self.counter = 0 elif validation_loss > (self.min_validation_loss + self.min_delta): self.counter += 1 if self.counter >= self.patience: return True return False