We provide a list of util functions to give users insights on what happened to the graph modules during the to_backend() stage.
The get_delegation_info() method provides a summary of what happened to the model after the to_backend() call:
import torch from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner from executorch.exir import to_edge_transform_and_lower from torch.export import Dim, export from torchvision.models.mobilenetv2 import MobileNet_V2_Weights import torchvision.models as models # Dependency needed for debugging delegates from executorch.devtools.backend_debug import get_delegation_info from tabulate import tabulate model = models.mobilenetv2.mobilenet_v2(weights=MobileNet_V2_Weights.DEFAULT).eval() sample_inputs = (torch.randn(1, 3, 224, 224), ) et_program = to_edge_transform_and_lower( torch.export.export(model, sample_inputs), partitioner=[XnnpackPartitioner()] ) graph_module = et_program.exported_program().graph_module delegation_info = get_delegation_info(graph_module) # print the summary like the number of delegated nodes, non-delegated nodes, etc print(delegation_info.get_summary()) df = delegation_info.get_operator_delegation_dataframe() # print the table including op_type, occurrences_in_delegated_graphs, occurrences_in_non_delegated_graphs print(tabulate(df, headers="keys", tablefmt="fancy_grid"))
Example printout:
Total delegated subgraphs: 2 Number of delegated nodes: 203 Number of non-delegated nodes: 4
| op_type | occurrences_in_delegated_graphs | occurrences_in_non_delegated_graphs | |
|---|---|---|---|
| 0 | aten__native_batch_norm_legit_no_training_default | 52 | 0 |
| 1 | aten_add_tensor | 10 | 0 |
| 2 | aten_convolution_default | 52 | 0 |
| 3 | aten_hardtanh_default | 35 | 0 |
| 4 | aten_linear_default | 1 | 0 |
| 5 | aten_mean_dim | 1 | 0 |
| 6 | aten_view_copy_default | 0 | 1 |
| 7 | dim_order_ops__clone_dim_order_default | 0 | 1 |
| 8 | getitem | 52 | 2 |
| 9 | Total | 203 | 4 |
From the table, the operator aten_view_copy_default appears 0 times in delegate graphs and 1 times in non-delegated graphs. Users can use information like this to debug. get_item node is a special case, it means getting the output from the delegate subgraph.
To see a more detailed view, use the format_delegated_graph() method to get a string representation of the entire graph or use print_delegated_graph() to print directly:
from executorch.exir.backend.utils import format_delegated_graph graph_module = et_program.exported_program().graph_module print(format_delegated_graph(graph_module)) # or call print_delegated_graph(graph_module)
It will print the whole model as well as the subgraph consumed by the backend. The generic debug function provided by fx like print_tabular() or print_readable() will only show call_delegate and hide the subgraph consumed by the backend, while this function exposes the contents inside the subgraph.
In the example printout below, observe that there are two subgraphs, aten_view_copy_default is not delegated, while most of the others ops are delegated.