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.
| 181 | |
| 182 | |
| 183 | class DataParallelInferenceRunner(InferenceRunnerBase): |
| 184 | """ |
| 185 | Inference with data-parallel support on multiple GPUs. |
| 186 | It will build one predict tower on each GPU, and run prediction |
| 187 | with a large total batch in parallel on all GPUs. |
| 188 | It will run the remainder (when the total size of input is not a multiple of #GPU) |
| 189 | sequentially. |
| 190 | """ |
| 191 | def __init__(self, input, infs, gpus, tower_name='InferenceTower', tower_func=None): |
| 192 | """ |
| 193 | Args: |
| 194 | input (DataFlow or QueueInput) |
| 195 | gpus (int or list[int]): #gpus, or list of GPU id |
| 196 | tower_name (str): the name scope of the tower to build. |
| 197 | If multiple InferenceRunner are used, each needs a different tower_name. |
| 198 | tower_func (tfutils.TowerFunc or None): the tower function to be used to build the graph. |
| 199 | The tower function will be called under a `training=False` TowerContext. |
| 200 | The default is `trainer.tower_func`, |
| 201 | but you can change it to a different tower function |
| 202 | if you need to inference with several different models. |
| 203 | """ |
| 204 | if isinstance(gpus, int): |
| 205 | gpus = list(range(gpus)) |
| 206 | self._devices = [_device_from_int(k) for k in gpus] |
| 207 | self._tower_names = ['{}{}'.format(tower_name, k) for k in range(len(gpus))] |
| 208 | |
| 209 | if isinstance(input, DataFlow): |
| 210 | input = QueueInput(input) |
| 211 | assert isinstance(input, QueueInput), input |
| 212 | super(DataParallelInferenceRunner, self).__init__(input, infs) |
| 213 | assert self._size > 0, "Input for DataParallelInferenceRunner must have a size!" |
| 214 | |
| 215 | self._hooks = [] |
| 216 | self._hooks_parallel = [] |
| 217 | self._tower_func = tower_func |
| 218 | |
| 219 | def _setup_graph(self): |
| 220 | self._handles = [] |
| 221 | if self._tower_func is None: |
| 222 | assert self.trainer.tower_func is not None, "You must set tower_func of the trainer to use InferenceRunner!" |
| 223 | self._tower_func = self.trainer.tower_func |
| 224 | |
| 225 | input_callbacks = self._input_source.setup(self._tower_func.input_signature) |
| 226 | with tf.variable_scope(tf.get_variable_scope(), reuse=True): |
| 227 | for idx, dev in enumerate(self._devices): |
| 228 | vs_name = self.trainer._vs_name_for_predictor(idx) |
| 229 | with tf.device(dev), PredictTowerContext( |
| 230 | self._tower_names[idx], vs_name=vs_name): |
| 231 | logger.info("[InferenceRunner] Building tower '{}' on device {} {}...".format( |
| 232 | self._tower_names[idx], dev, |
| 233 | "with variable scope '{}'".format(vs_name) if vs_name else '')) |
| 234 | # TODO log for tower creation, here or in tower.py? |
| 235 | self._tower_func(*self._input_source.get_input_tensors()) |
| 236 | self._handles.append(self._tower_func.towers[-1]) |
| 237 | |
| 238 | # setup callbacks and hooks |
| 239 | self._input_callbacks = Callbacks(input_callbacks) |
| 240 |
no outgoing calls
no test coverage detected