Runs a computation graph. It's possible to pass arguments to `tf.Session.run()` via `session_kwargs`. In particular additional operations via `fetches` argument and additional tensor substitutions via `feed_dict` arguments. Note that given substitutions are merged with substitutions from `i
| 3293 | |
| 3294 | |
| 3295 | class GraphExecutionFunction(object): |
| 3296 | """Runs a computation graph. |
| 3297 | |
| 3298 | It's possible to pass arguments to `tf.Session.run()` via `session_kwargs`. |
| 3299 | In particular additional operations via `fetches` argument and additional |
| 3300 | tensor substitutions via `feed_dict` arguments. Note that given |
| 3301 | substitutions are merged with substitutions from `inputs`. Even though |
| 3302 | `feed_dict` is passed once in the constructor (called in `model.compile()`) |
| 3303 | we can modify the values in the dictionary. Through this feed_dict we can |
| 3304 | provide additional substitutions besides Keras inputs. |
| 3305 | |
| 3306 | Arguments: |
| 3307 | inputs: Feed placeholders to the computation graph. |
| 3308 | outputs: Output tensors to fetch. |
| 3309 | updates: Additional update ops to be run at function call. |
| 3310 | name: A name to help users identify what this function does. |
| 3311 | session_kwargs: Arguments to `tf.Session.run()`: |
| 3312 | `fetches`, `feed_dict`, `options`, `run_metadata`. |
| 3313 | """ |
| 3314 | |
| 3315 | def __init__(self, inputs, outputs, updates=None, name=None, |
| 3316 | **session_kwargs): |
| 3317 | updates = updates or [] |
| 3318 | if not isinstance(updates, (list, tuple)): |
| 3319 | raise TypeError('`updates` in a Keras backend function ' |
| 3320 | 'should be a list or tuple.') |
| 3321 | |
| 3322 | self._inputs_structure = inputs |
| 3323 | self.inputs = nest.flatten(inputs, expand_composites=True) |
| 3324 | self._outputs_structure = outputs |
| 3325 | self.outputs = cast_variables_to_tensor( |
| 3326 | nest.flatten(outputs, expand_composites=True)) |
| 3327 | # TODO(b/127668432): Consider using autograph to generate these |
| 3328 | # dependencies in call. |
| 3329 | # Index 0 = total loss or model output for `predict`. |
| 3330 | with ops.control_dependencies([self.outputs[0]]): |
| 3331 | updates_ops = [] |
| 3332 | for update in updates: |
| 3333 | if isinstance(update, tuple): |
| 3334 | p, new_p = update |
| 3335 | updates_ops.append(state_ops.assign(p, new_p)) |
| 3336 | else: |
| 3337 | # assumed already an op |
| 3338 | updates_ops.append(update) |
| 3339 | self.updates_op = control_flow_ops.group(*updates_ops) |
| 3340 | self.name = name |
| 3341 | # additional tensor substitutions |
| 3342 | self.feed_dict = session_kwargs.pop('feed_dict', None) |
| 3343 | # additional operations |
| 3344 | self.fetches = session_kwargs.pop('fetches', []) |
| 3345 | if not isinstance(self.fetches, list): |
| 3346 | self.fetches = [self.fetches] |
| 3347 | self.run_options = session_kwargs.pop('options', None) |
| 3348 | self.run_metadata = session_kwargs.pop('run_metadata', None) |
| 3349 | # The main use case of `fetches` being passed to a model is the ability |
| 3350 | # to run custom updates |
| 3351 | # This requires us to wrap fetches in `identity` ops. |
| 3352 | self.fetches = [array_ops.identity(x) for x in self.fetches] |