class_docstr: Inference with data-parallel support on multiple GPUs.
  It will build one predict tower on each GPU, and run prediction
  with a large total batch in parallel on all GPUs.
  It will run the remainder (when the total size of input is not a multiple of #GPU)
  sequentially.
code: def _setup_graph(self):
      self._handles = []
      if self._tower_func is None:
          assert (
              self.trainer.tower_func is not None
          ), "You must set tower_func of the trainer to use InferenceRunner!"
          self._tower_func = self.trainer.tower_func
  
      input_callbacks = self._input_source.setup(
          self._tower_func.input_signature
      )
      with tf.variable_scope(tf.get_variable_scope(), reuse=True):
          for idx, dev in enumerate(self._devices):
              vs_name = self.trainer._vs_name_for_predictor(idx)
              with tf.device(dev), PredictTowerContext(
                  self._tower_names[idx], vs_name=vs_name
              ):
                  logger.info(
                      "[InferenceRunner] Building tower '{}' on device {} {}...".format(
                          self._tower_names[idx],
                          dev,
                          "with variable scope '{}'".format(vs_name)
                          if vs_name
                          else "",
                      )
                  )
                  # TODO log for tower creation, here or in tower.py?
                  self._tower_func(
                      *self._input_source.get_input_tensors()
                  )
                  self._handles.append(self._tower_func.towers[-1])
  
      # setup callbacks and hooks
      self._input_callbacks = Callbacks(input_callbacks)
  
      # TODO InputSource might have hooks which break us.
      # e.g. hooks from StagingInput will force the consumption
      # of nr_tower datapoints in every run.
      input_hooks = self._input_callbacks.get_hooks()
      self._hooks.extend(
          [self._build_hook(inf) for inf in self.infs] + input_hooks
      )
      self._hooks_parallel.extend(
          [self._build_hook_parallel(inf) for inf in self.infs]
          + input_hooks
      )
  
      for inf in self.infs:
          inf.setup_graph(self.trainer)
      self._input_callbacks.setup_graph(self.trainer)
  
imports: import itertools
  import sys
  from contextlib import contextmanager
  import tqdm
  from tensorflow.python.training.monitored_session import _HookedSession
  from ..compat import tfv1
  from ..dataflow.base import DataFlow
  from ..input_source import FeedInput, InputSource, QueueInput, StagingInput
  from ..tfutils.tower import PredictTowerContext
  from ..utils import logger
  from ..utils.utils import get_tqdm_kwargs
  from .base import Callback
  from .group import Callbacks
  from .inference import Inferencer
label_desc: Use image node_id 2 for calling the DataParallelInferenceRunner obj's underlying member method code with example usage: obj._setup_graph() without return types