Returns all tensors in `func_graph` that should be accumulated.
(func_graph)
| 429 | |
| 430 | |
| 431 | def _get_intermediates(func_graph): |
| 432 | """Returns all tensors in `func_graph` that should be accumulated.""" |
| 433 | # We currently accumulate output tensors of most ops in the function and rely |
| 434 | # on the pruning pass to get rid of the unused accumulators at runtime. |
| 435 | # However, this can bloat the GraphDef and make debugging harder so we perform |
| 436 | # some optimizations. |
| 437 | # |
| 438 | # Optimization we currently perform: |
| 439 | # 1. We do not accumulate tensors which already have an accumulator |
| 440 | # in the loop body. |
| 441 | # 2. We do not accumulate outputs of Identity nodes. When building the |
| 442 | # FuncGraph, we add an Identity node for each output (see |
| 443 | # `AutomaticControlDependencies.mark_as_return`). Accumulating outputs |
| 444 | # of all these nodes bloats the GraphDef quite a bit so we remove those. |
| 445 | # Since the gradient of an Identity node does not rely on its forward op's |
| 446 | # input this is safe to do. |
| 447 | # |
| 448 | # Other possible optimizations: |
| 449 | # 1. Only accumulate tensors that will be required by the backward pass. |
| 450 | # This will require running the gradient pass and hence would increase the |
| 451 | # graph building time for the forward pass. |
| 452 | # 2. Do not accumulate Const nodes created inside the loop body. |
| 453 | # 3. Do not accumulate loop vars that are returned as-is just like captured |
| 454 | # tensors. |
| 455 | intermediates = [] |
| 456 | reverse_captures = dict( |
| 457 | (v.experimental_ref(), k) for k, v in func_graph.captures) |
| 458 | |
| 459 | for op in func_graph.get_operations(): |
| 460 | if op.type == "Identity": |
| 461 | continue |
| 462 | # Accumulating mutexes can cause deadlock. |
| 463 | if op.type == "MutexLock": |
| 464 | continue |
| 465 | for o in op.outputs: |
| 466 | if (o is not func_graph.inputs[0] and # Loop counter. |
| 467 | o.dtype != dtypes.resource and # Do not accumulate resource tensors. |
| 468 | _get_accumulator(o) is None and # Has existing accumulator. |
| 469 | o.experimental_ref() not in reverse_captures |
| 470 | ): # Captured value, hence loop invariant. |
| 471 | intermediates.append(o) |
| 472 | return intermediates |
| 473 | |
| 474 | |
| 475 | def _preprocess_grad(grad, body_graph_output, while_op_output): |
no test coverage detected