code: def retry_with_halved_batch_size(run_config: ExplanationRunConfig):
      """Function wrapper that retries an fn with a halved batch size.
  
      We want to maintain as large of a batch size as possible to maximize throughput. However, calculating explanations
      requires significantly more memory, and the original batch sized used during training may be too large and cause a
      CUDA OOM error, for example, if using GPUs.
  
      Will raise an error if a non-OOM error is raised, or if the batch size is reduced below 1 and the fn still fails.
      """
  
      def retry_with_halved_batch_size_fn(fn):
          def retry_with_halved_batch_size_wrapper(*args, **kwargs):
              latest_error = None
              while run_config.batch_size >= MINIMUM_BATCH_SIZE:
                  try:
                      return fn(*args, **kwargs)
                  except RuntimeError as e:
                      latest_error = e
                      # PyTorch only generates Runtime errors for CUDA OOM.
                      gc.collect()
                      if "CUDA out of memory" in str(e) or isinstance(
                          e, torch.cuda.OutOfMemoryError
                      ):
                          logger.exception(
                              f"OOM at batch_size={run_config.batch_size}, halving and trying again"
                          )
                          run_config.batch_size //= 2
                      else:
                          # Not a CUDA error
                          raise
  
              raise RuntimeError(
                  f"Ran into latest error {latest_error} during explanation. "
                  "If a CUDA out of memory error, then the batch size could not be reduced any further."
              )
  
          return retry_with_halved_batch_size_wrapper
  
      return retry_with_halved_batch_size_fn
  
imports: import copy
  import gc
  import logging
  from collections import defaultdict
  from dataclasses import dataclass
  from typing import Dict, List, Optional, Tuple
  import numpy
  import numpy.typing
  import pandas
  import torch
  from captum.attr import LayerIntegratedGradients, TokenReferenceBase
  from captum.attr._utils.input_layer_wrapper import InputIdentity
  from torch.autograd import Variable
  from tqdm import tqdm
  from ludwig.api import LudwigModel
  from ludwig.api_annotations import PublicAPI
  from ludwig.constants import BINARY, CATEGORY, DATE, IMAGE, INPUT_FEATURES, MINIMUM_BATCH_SIZE, NAME, NUMBER, PREPROCESSING, SEQUENCE, SET, TEXT, UNKNOWN_SYMBOL
  from ludwig.data.preprocessing import preprocess_for_prediction
  from ludwig.explain.explainer import Explainer
  from ludwig.explain.explanation import ExplanationsResult
  from ludwig.explain.util import get_pred_col, replace_layer_with_copy
  from ludwig.features.feature_utils import LudwigFeatureDict
  from ludwig.models.ecd import ECD
  from ludwig.utils.torch_utils import DEVICE
label_desc: Use image node_id 1 for calling a global function with example usage: retry_with_halved_batch_size(run_config) and returns: retry_with_halved_batch_size_fn, retry_with_halved_batch_size_wrapper, fn