docstr: Checks the history of validation scores.
  
  Uses history of validation scores to decide whether training
  should stop.
  
  Saves the model if scores have improved.
code: def check_progress_on_validation(
      self,
      progress_tracker: ProgressTracker,
      validation_output_feature_name: str,
      validation_metric: str,
      save_path: str,
      early_stopping_steps: int,
      skip_save_model: bool,
  ) -> bool:
      """Checks the history of validation scores.
  
      Uses history of validation scores to decide whether training
      should stop.
  
      Saves the model if scores have improved.
      """
      should_break = False
      # record how long its been since an improvement
      improved = get_improved_fn(validation_metric)
      all_validation_metrics = progress_tracker.validation_metrics[
          validation_output_feature_name
      ]
      # The most recent validation_metric metric.
      eval_metric: TrainerMetric = all_validation_metrics[
          validation_metric
      ][-1]
      eval_metric_value = eval_metric[-1]
  
      if improved(
          eval_metric_value, progress_tracker.best_eval_metric_value
      ):
          previous_best_eval_metric_value = (
              progress_tracker.best_eval_metric_value
          )
  
          # Save the value, steps, epoch, and checkpoint number.
          progress_tracker.best_eval_metric_value = eval_metric_value
  
          # Use LGBM fine-grained internal tracking if available, otherwise fall back to coarse-grained tracking
          # every `boosting_rounds_per_checkpoint`.
          if self.model.lgbm_model.best_iteration_ is not None:
              progress_tracker.best_eval_metric_steps = (
                  self.model.lgbm_model.best_iteration_
              )
          else:
              progress_tracker.best_eval_metric_steps = (
                  progress_tracker.steps
              )
          progress_tracker.best_eval_metric_epoch = (
              progress_tracker.epoch
          )
          progress_tracker.best_eval_metric_checkpoint_number = (
              progress_tracker.checkpoint_number
          )
  
          # Save best metrics for all data subsets.
          progress_tracker.best_eval_train_metrics = (
              get_latest_metrics_dict(progress_tracker.train_metrics)
          )
          progress_tracker.best_eval_validation_metrics = (
              get_latest_metrics_dict(
                  progress_tracker.validation_metrics
              )
          )
          progress_tracker.best_eval_test_metrics = (
              get_latest_metrics_dict(progress_tracker.test_metrics)
          )
  
          if self.is_coordinator():
              logger.info(
                  f"Evaluation validation metric: '{validation_output_feature_name}' '{validation_metric}' improved."
              )
              absolute_eval_metric_value_change = round(
                  abs(
                      previous_best_eval_metric_value
                      - progress_tracker.best_eval_metric_value
                  ),
                  3,
              )
              if get_metric_objective(validation_metric) == MINIMIZE:
                  logger.info(
                      f"'{validation_output_feature_name}' '{validation_metric}' decreased by "
                      f"{absolute_eval_metric_value_change}."
                  )
              else:
                  logger.info(
                      f"'{validation_output_feature_name}' '{validation_metric}' increased by "
                      f"{absolute_eval_metric_value_change}."
                  )
              # Save the model.
              if not skip_save_model:
                  logger.info("New best model saved.\n")
                  self.model.save(save_path)
  
      last_improvement_in_steps = (
          progress_tracker.steps
          - progress_tracker.best_eval_metric_steps
      )
      progress_tracker.last_improvement_steps = (
          last_improvement_in_steps
      )
  
      if last_improvement_in_steps != 0 and self.is_coordinator():
          logger.info(
              f"Last improvement of {validation_output_feature_name} validation {validation_metric} happened "
              + f"{last_improvement_in_steps} step(s) ago.\n"
          )
  
      # ========== Early Stop logic ==========
      # If any early stopping condition is satisfied, either lack of improvement for many steps, or via callbacks on
      # any worker, then trigger early stopping.
      early_stop_bool = (
          0 < early_stopping_steps <= last_improvement_in_steps
      )
      if not early_stop_bool:
          for callback in self.callbacks:
              if callback.should_early_stop(
                  self, progress_tracker, self.is_coordinator()
              ):
                  early_stop_bool = True
                  break
  
      should_early_stop = torch.as_tensor(
          [early_stop_bool], dtype=torch.int
      )
      should_early_stop = self.distributed.allreduce(should_early_stop)
      if should_early_stop.item():
          if self.is_coordinator():
              logger.info(
                  f"\nEARLY STOPPING due to lack of validation improvement. It has been {last_improvement_in_steps} "
                  "step(s) since last validation improvement."
              )
          should_break = True
      return should_break
  
imports: import logging
  import os
  import re
  import signal
  import sys
  import threading
  import time
  from typing import Any, Dict, List, Optional, Tuple, Union
  import lightgbm
  import torch
  from torch.utils.tensorboard import SummaryWriter
  from ludwig.constants import BINARY, CATEGORY, MINIMIZE, MODEL_GBM, NUMBER, TEST, TRAINING, VALIDATION
  from ludwig.distributed import init_dist_strategy
  from ludwig.distributed.base import DistributedStrategy, LocalStrategy
  from ludwig.features.feature_utils import LudwigFeatureDict
  from ludwig.globals import is_progressbar_disabled, TRAINING_CHECKPOINTS_DIR_PATH, TRAINING_PROGRESS_TRACKER_FILE_NAME
  from ludwig.models.gbm import GBM
  from ludwig.models.predictor import Predictor
  from ludwig.modules.metric_modules import get_improved_fn, get_initial_validation_value
  from ludwig.modules.metric_registry import get_metric_objective
  from ludwig.progress_bar import LudwigProgressBar
  from ludwig.schema.trainer import BaseTrainerConfig, GBMTrainerConfig
  from ludwig.trainers.base import BaseTrainer
  from ludwig.trainers.registry import register_ray_trainer, register_trainer
  from ludwig.types import ModelConfigDict
  from ludwig.utils import time_utils
  from ludwig.utils.checkpoint_utils import CheckpointManager
  from ludwig.utils.defaults import default_random_seed
  from ludwig.utils.gbm_utils import get_single_output_feature, get_targets, log_loss_objective, logits_to_predictions, multiclass_objective, store_predictions, store_predictions_ray, TrainLogits
  from ludwig.utils.metric_utils import get_metric_names, TrainerMetric
  from ludwig.utils.metrics_printed_table import print_metrics_table
  from ludwig.utils.misc_utils import set_random_seed
  from ludwig.utils.trainer_utils import append_metrics, get_latest_metrics_dict, get_new_progress_tracker, ProgressTracker
label_desc: Use image node_id 11 for calling the LightGBMTrainer obj's underlying member method code with example usage: obj.check_progress_on_validation(progress_tracker, validation_output_feature_name, validation_metric, save_path, early_stopping_steps, skip_save_model) and returns: should_break