docs: def resnet_main(seed, flags, model_function, input_function, shape=None):
    """Shared main loop for ResNet Models.
  
    Args:
      flags: FLAGS object that contains the params for running. See
        ResnetArgParser for created flags.
      model_function: the function that instantiates the Model and builds the
        ops for train/eval. This will be passed directly into the estimator.
      input_function: the function that processes the dataset and returns a
        dataset that the estimator can train on. This will be wrapped with
        all the relevant flags for running and passed to estimator.
      shape: list of ints representing the shape of the images used for training.
        This is only used if flags.export_dir is passed.
    """
  
    mlperf_log.resnet_print(key=mlperf_log.RUN_START)
  
    # Using the Winograd non-fused algorithms provides a small performance boost.
    os.environ['TF_ENABLE_WINOGRAD_NONFUSED'] = '1'
  
    # Create session config based on values of inter_op_parallelism_threads and
    # intra_op_parallelism_threads. Note that we default to having
    # allow_soft_placement = True, which is required for multi-GPU and not
    # harmful for other modes.
    session_config = tf.ConfigProto(
        inter_op_parallelism_threads=flags.inter_op_parallelism_threads,
        intra_op_parallelism_threads=flags.intra_op_parallelism_threads,
        allow_soft_placement=True)
  
    if flags.num_gpus == 0:
      distribution = tf.contrib.distribute.OneDeviceStrategy('device:CPU:0')
    elif flags.num_gpus == 1:
      distribution = tf.contrib.distribute.OneDeviceStrategy('device:GPU:0')
    else:
      distribution = tf.contrib.distribute.MirroredStrategy(
          num_gpus=flags.num_gpus
      )
  
    mlperf_log.resnet_print(key=mlperf_log.RUN_SET_RANDOM_SEED, value=seed)
    run_config = tf.estimator.RunConfig(train_distribute=distribution,
                                        session_config=session_config,
                                        tf_random_seed=seed)
  
    mlperf_log.resnet_print(key=mlperf_log.INPUT_BATCH_SIZE,
                            value=flags.batch_size)
    classifier = tf.estimator.Estimator(
        model_fn=model_function, model_dir=flags.model_dir, config=run_config,
        params={
            'resnet_size': flags.resnet_size,
            'data_format': flags.data_format,
            'batch_size': flags.batch_size,
            'version': flags.version,
            'loss_scale': flags.loss_scale,
            'dtype': flags.dtype,
            'label_smoothing': flags.label_smoothing,
            'enable_lars': flags.enable_lars,
            'weight_decay': flags.weight_decay,
            'fine_tune': flags.fine_tune
        })
  
    if flags.benchmark_log_dir is not None:
      benchmark_logger = logger.BenchmarkLogger(flags.benchmark_log_dir)
      benchmark_logger.log_run_info('resnet')
    else:
      benchmark_logger = None
  
    mlperf_log.resnet_print(key=mlperf_log.TRAIN_LOOP)
  
    # The reference performs the first evaluation on the fourth epoch. (offset
    # eval by 3 epochs)
    mlperf_log.resnet_print(key=mlperf_log.EVAL_EPOCH_OFFSET, value=3)
    success = False
    for i in range(flags.train_epochs // flags.epochs_between_evals):
      # Data for epochs_between_evals (i.e. 4 epochs between evals) worth of
      # epochs is concatenated and run as a single block inside a session. For
      # this reason we declare all of the epochs that will be run at the start.
      # Submitters may report in a way which is reasonable for their control flow.
      for j in range(flags.epochs_between_evals):
        mlperf_log.resnet_print(key=mlperf_log.TRAIN_EPOCH,
                                value=i * flags.epochs_between_evals + j)
      train_hooks = hooks_helper.get_train_hooks(
          flags.hooks,
          batch_size=flags.batch_size,
          benchmark_log_dir=flags.benchmark_log_dir)
  
      _log_cache = []
      def formatter(x):
        """Abuse side effects to get tensors out of the model_fn."""
        if _log_cache:
          _log_cache.pop()
        _log_cache.append(x.copy())
        return str(x)
  
      compliance_hook = tf.train.LoggingTensorHook(
        tensors={_NUM_EXAMPLES_NAME: _NUM_EXAMPLES_NAME},
        every_n_iter=int(1e10),
        at_end=True,
        formatter=formatter)
  
      print('Starting a training cycle.')
  
      def input_fn_train():
        return input_function(
            is_training=True,
            data_dir=flags.data_dir,
            batch_size=per_device_batch_size(flags.batch_size, flags.num_gpus),
            num_epochs=flags.epochs_between_evals,
            num_gpus=flags.num_gpus,
            dtype=flags.dtype
        )
  
      classifier.train(input_fn=input_fn_train, hooks=train_hooks + [compliance_hook],
                       max_steps=flags.max_train_steps)
  
      train_examples = int(_log_cache.pop()[_NUM_EXAMPLES_NAME])
      mlperf_log.resnet_print(key=mlperf_log.INPUT_SIZE, value=train_examples)
  
      print('Starting to evaluate.')
      # Evaluate the model and print results
      def input_fn_eval():
        return input_function(
            is_training=False,
            data_dir=flags.data_dir,
            batch_size=per_device_batch_size(flags.batch_size, flags.num_gpus),
            num_epochs=1,
            dtype=flags.dtype
        )
  
  
      mlperf_log.resnet_print(key=mlperf_log.EVAL_START)
      # flags.max_train_steps is generally associated with testing and profiling.
      # As a result it is frequently called with synthetic data, which will
      # iterate forever. Passing steps=flags.max_train_steps allows the eval
      # (which is generally unimportant in those circumstances) to terminate.
      # Note that eval will run for max_train_steps each loop, regardless of the
      # global_step count.
      eval_results = classifier.evaluate(input_fn=input_fn_eval,
                                         steps=flags.max_train_steps)
      mlperf_log.resnet_print(key=mlperf_log.EVAL_STOP)
      mlperf_log.resnet_print(key=mlperf_log.EVAL_SIZE, value=int(eval_results[_NUM_EXAMPLES_NAME]))
      mlperf_log.resnet_print(key=mlperf_log.EVAL_ACCURACY, value=float(eval_results['accuracy']))
      mlperf_log.resnet_print(key=mlperf_log.EVAL_TARGET, value=flags.stop_threshold)
      print(eval_results)
  
      if benchmark_logger:
        benchmark_logger.log_estimator_evaluation_result(eval_results)
  
      if model_helpers.past_stop_threshold(
          flags.stop_threshold, eval_results['accuracy']):
        success = True
        break
  
    mlperf_log.resnet_print(key=mlperf_log.RUN_STOP, value={"success": success})
    mlperf_log.resnet_print(key=mlperf_log.RUN_FINAL)