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.
docstr: Args:
      input (DataFlow or QueueInput)
      gpus (int or list[int]): #gpus, or list of GPU id
      tower_name (str): the name scope of the tower to build.
          If multiple InferenceRunner are used, each needs a different tower_name.
      tower_func (tfutils.TowerFunc or None): the tower function to be used to build the graph.
          The tower function will be called under a `training=False` TowerContext.
          The default is `trainer.tower_func`,
          but you can change it to a different tower function
          if you need to inference with several different models.
docstr_tok: ["Args",":","input","(","DataFlow","or","QueueInput",")","gpus","(","int","or","list","[","int","]",")",":","#","gpus",",","or","list","of","GPU","id","tower_name","(","str",")",":","the","name","scope","of","the","tower","to","build",".","If","multiple","InferenceRunner","are","used",",","each","needs","a","different","tower_name",".","tower_func","(","tfutils.TowerFunc","or","None",")",":","the","tower","function","to","be","used","to","build","the","graph",".","The","tower","function","will","be","called","under","a","`","training=False","`","TowerContext",".","The","default","is","`","trainer.tower_func","`",",","but","you","can","change","it","to","a","different","tower","function","if","you","need","to","inference","with","several","different","models","."]
code: def __init__(
      self,
      input,
      infs,
      gpus,
      tower_name="InferenceTower",
      tower_func=None,
  ):
      """
      Args:
          input (DataFlow or QueueInput)
          gpus (int or list[int]): #gpus, or list of GPU id
          tower_name (str): the name scope of the tower to build.
              If multiple InferenceRunner are used, each needs a different tower_name.
          tower_func (tfutils.TowerFunc or None): the tower function to be used to build the graph.
              The tower function will be called under a `training=False` TowerContext.
              The default is `trainer.tower_func`,
              but you can change it to a different tower function
              if you need to inference with several different models.
      """
      if isinstance(gpus, int):
          gpus = list(range(gpus))
      self._devices = [_device_from_int(k) for k in gpus]
      self._tower_names = [
          "{}{}".format(tower_name, k) for k in range(len(gpus))
      ]
  
      if isinstance(input, DataFlow):
          input = QueueInput(input)
      assert isinstance(input, QueueInput), input
      super(DataParallelInferenceRunner, self).__init__(input, infs)
      assert (
          self._size > 0
      ), "Input for DataParallelInferenceRunner must have a size!"
  
      self._hooks = []
      self._hooks_parallel = []
      self._tower_func = tower_func
  
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 1 to create a new DataParallelInferenceRunner object from inherited base classes: InferenceRunnerBase with example: obj = DataParallelInferenceRunner(input, infs, gpus, tower_name, tower_func)