class_docstr: Torch Estimator implementation for Image Classification.
  
  Parameters
  ----------
  config : dict
      Config in nested dict.
  logger : logging.Logger
      Optional logger for this estimator, can be `None` when default setting is used.
  reporter : callable
      The reporter for metric checkpointing.
  net : torch.nn.Module
      The custom network. If defined, the model name in config will be ignored so your
      custom network will be used for training rather than pulling it from model zoo.
code: def _train_loop(self, train_loader, val_loader, time_limit=math.inf):
      start_tic = time.time()
      # setup loss function
      if self.mixup_active:
          # smoothing is handled with mixup target transform
          train_loss_fn = SoftTargetCrossEntropy()
      elif self._augmentation_cfg.smoothing:
          train_loss_fn = LabelSmoothingCrossEntropy(
              smoothing=self._augmentation_cfg.smoothing
          )
      else:
          train_loss_fn = nn.CrossEntropyLoss()
      validate_loss_fn = nn.CrossEntropyLoss()
      train_loss_fn = train_loss_fn.to(self.ctx[0])
      validate_loss_fn = validate_loss_fn.to(self.ctx[0])
      eval_metric = self._misc_cfg.eval_metric
      if self._problem_type == REGRESSION:
          train_loss_fn = nn.MSELoss()
          validate_loss_fn = nn.MSELoss()
          eval_metric = "rmse"
      early_stopper = EarlyStopperOnPlateau(
          patience=self._train_cfg.early_stop_patience,
          min_delta=self._train_cfg.early_stop_min_delta,
          baseline_value=self._train_cfg.early_stop_baseline,
          max_value=self._train_cfg.early_stop_max_value,
      )
  
      self._logger.info(
          "Start training from [Epoch %d]",
          max(self._train_cfg.start_epoch, self.epoch),
      )
  
      self._time_elapsed += time.time() - start_tic
      for self.epoch in range(
          max(self.start_epoch, self.epoch), self.epochs
      ):
          epoch = self.epoch
          if self._best_acc >= 1.0:
              self._logger.info(
                  "[Epoch {}] Early stopping as acc is reaching 1.0".format(
                      epoch
                  )
              )
              break
          (
              should_stop,
              stop_message,
          ) = early_stopper.get_early_stop_advice()
          if should_stop:
              self._logger.info(
                  "[Epoch {}] ".format(epoch) + stop_message
              )
              break
          train_metrics = self.train_one_epoch(
              epoch,
              self.net,
              train_loader,
              self._optimizer,
              train_loss_fn,
              lr_scheduler=self._lr_scheduler,
              output_dir=self._logdir,
              amp_autocast=self._amp_autocast,
              loss_scaler=self._loss_scaler,
              model_ema=self._model_ema,
              mixup_fn=self._mixup_fn,
              time_limit=time_limit,
          )
          # reaching time limit, exit early
          if train_metrics["time_limit"]:
              self._logger.warning(
                  f"`time_limit={time_limit}` reached, exit early..."
              )
              return {
                  "train_acc": train_metrics["train_acc"],
                  "valid_acc": self._best_acc,
                  "time": self._time_elapsed,
                  "checkpoint": self._cp_name,
              }
          post_tic = time.time()
  
          eval_metrics = self.validate(
              self.net,
              val_loader,
              validate_loss_fn,
              amp_autocast=self._amp_autocast,
          )
  
          if (
              self._model_ema is not None
              and not self._model_ema_cfg.model_ema_force_cpu
          ):
              ema_eval_metrics = self.validate(
                  self._model_ema.module,
                  val_loader,
                  validate_loss_fn,
                  amp_autocast=self._amp_autocast,
              )
              eval_metrics = ema_eval_metrics
  
          if self._problem_type == REGRESSION:
              val_acc = eval_metrics["rmse"]
              if self._reporter:
                  self._reporter(epoch=epoch, acc_reward=-val_acc)
              early_stopper.update(-val_acc)
  
              if -val_acc > self._best_acc:
                  self._cp_name = os.path.join(
                      self._logdir, _BEST_CHECKPOINT_FILE
                  )
                  self._logger.info(
                      "[Epoch %d] Current best rmse: %f vs previous %f, saved to %s",
                      self.epoch,
                      val_acc,
                      -self._best_acc,
                      self._cp_name,
                  )
                  self.save(self._cp_name)
                  self._best_acc = -val_acc
          else:
              val_acc = eval_metrics["top1"]
              if self._reporter:
                  self._reporter(epoch=epoch, acc_reward=val_acc)
              early_stopper.update(val_acc)
  
              if val_acc > self._best_acc:
                  self._cp_name = os.path.join(
                      self._logdir, _BEST_CHECKPOINT_FILE
                  )
                  self._logger.info(
                      "[Epoch %d] Current best top-1: %f vs previous %f, saved to %s",
                      self.epoch,
                      val_acc,
                      self._best_acc,
                      self._cp_name,
                  )
                  self.save(self._cp_name)
                  self._best_acc = val_acc
  
          if self._lr_scheduler is not None:
              # step LR for next epoch
              self._lr_scheduler.step(
                  epoch + 1, eval_metrics[eval_metric]
              )
  
          self._time_elapsed += time.time() - post_tic
  
      if "accuracy" in train_metrics:
          return {
              "train_acc": train_metrics["accuracy"],
              "valid_acc": self._best_acc,
              "time": self._time_elapsed,
              "checkpoint": self._cp_name,
          }
      # rmse
      else:
          if self._problem_type == REGRESSION:
              return {
                  "train_score": train_metrics["rmse"],
                  "valid_score": -self._best_acc,
                  "time": self._time_elapsed,
                  "checkpoint": self._cp_name,
              }
          # mixup
          else:
              return {
                  "train_score": train_metrics["rmse"],
                  "valid_acc": self._best_acc,
                  "time": self._time_elapsed,
                  "checkpoint": self._cp_name,
              }
  
imports: import math
  import copy
  import os
  import logging
  import time
  import warnings
  import pickle
  from contextlib import suppress
  from PIL import Image
  import pandas
  import numpy
  import torch
  import torch.nn
  import torchvision.utils
  from torch.optim.optimizer import Optimizer
  from timm.data import create_loader, Mixup, FastCollateMixup, AugMixDataset
  from timm.models import create_model, safe_model_name, convert_splitbn_model, model_parameters
  from timm.utils import random_seed, dispatch_clip_grad, accuracy, unwrap_model, get_state_dict
  from timm.optim import create_optimizer_v2
  from timm.utils import ApexScaler, NativeScaler, ModelEmaV2, AverageMeter
  from timm.loss import LabelSmoothingCrossEntropy, SoftTargetCrossEntropy
  from .default import TorchImageClassificationCfg
  from .utils import resolve_data_config, update_cfg, optimizer_kwargs, create_scheduler, rmse, create_optimizer_v2a
  from ..constants import BINARY, MULTICLASS, REGRESSION
  from ..utils import EarlyStopperOnPlateau
  from ..utils import _suggest_load_context
  from ..conf import _BEST_CHECKPOINT_FILE
  from ..base_estimator import BaseEstimator, set_default
label_desc: Use image node_id 4 for calling the TorchImageClassificationEstimator obj's underlying member method code with example usage: obj._train_loop(train_loader, val_loader, time_limit) and returns: dict, dict, dict, dict