You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger (pytorch#15735)
### Summary
- Enabled ExecuTorch QNN Intermediate Tensor Debugger.
- Provide an API for users to define their own metrics
- Offers a variety of output format to visualize the debug results: svg,
csv, raw files.
- A README file and tutorial script to guide users on how to debug a
model. Example script: `python
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py -b
build-android -m SM8550 --device $DEVICE --dataset ../imagenet-mini/val/
--dump_intermediate_outputs`
#### An example use case
MobileVit V2 has significant drop in accuracy in certain QNN versions,
while QNN 2.29 has good accuracy. With the help of accuracy debugger, we
have targeted the node native_group_norm_default_1 in the model. As
shown below, in QNN 2.29, this node has a cos_similarity (QNN V.S. CPU)
of 0.997, while all other QNN versions has cos_similarity of 0, which
provides us some hint it is possibly this group_norm node that is
causing accuracy drop.
<img width="1385" height="596" alt="image"
src="https://github.com/user-attachments/assets/ba81bb5f-1cae-4d3b-a945-a00ca92efeef"
/>
#### What's Coming Next?
- Currently, we dump CPU outputs by manually inserting observer nodes.
However, ExecuTorch actually has built in methods
(intermediate_output_capturer) that could dump intermediate output for
us, in format of a dict{debug_handle : tensor_output}. We will enable
`debug_handle` and reuse
https://github.com/pytorch/executorch/blob/main/devtools/inspector/_intermediate_output_capturer.py
in future instead.
- Support graph with partitions
- Support LLM models
### Test plan
- E2E example script test
- `python backends/qualcomm/tests/test_qnn_delegate.py -k
TestExampleUtilsScript.test_intermediate_debugger -s $DEVICE --model
SM8650 --build_folder build-android/ --executorch_root . --image_dataset
../imagenet-mini/val/ --artifact ./e2e_test_debug`
- Simple model test
- `python backends/qualcomm/tests/test_qnn_delegate.py -k
TestQNNQuantizedUtils.test_qnn_backend_dump_intermediate_outputs_simple_model
--model SM8550 --device $DEVICE --build_folder build-android`
- `python backends/qualcomm/tests/test_qnn_delegate.py -k
TestQNNQuantizedUtils.test_qnn_backend_dump_intermediate_outputs_topk
--model SM8550 --device $DEVICE --build_folder build-android`
Copy file name to clipboardExpand all lines: backends/qualcomm/builders/README.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -225,7 +225,7 @@ Now, we can start to fill in function body step by step:
225
225
- **tensor_source_node**: current graph source node of the tensor
226
226
- **target_build_node**: current node to build, which is important for fixed point mixed-precision to work properly
227
227
- **tensor**: torch tensor emitted by node
228
-
- **tensor_type**: type compatible with QNN SDK, oftenly use `QNN_TENSOR_TYPE_NATIVE`for intermediate outputs and `QNN_TENSOR_TYPE_STATIC`for constant parameters
228
+
- **tensor_type**: type compatible with QNN SDK, often use `QNN_TENSOR_TYPE_NATIVE`for intermediate outputs and `QNN_TENSOR_TYPE_STATIC`for constant parameters
229
229
- **nodes_to_wrappers**: dictionary of graph node and its output tensor (note: the tensor here is not a torch tensor but a wrapped object for QNN)
230
230
- **node_name**: (optional) tensor name for user to specify
231
231
- **wrapper_idx**: (optional) defaults to zero if node is not a tuple, otherwise it acts as an indexer to output tensors. e.g. when slicing input tensor into multiple outputs, `wrapper_idx` is necessary for getting correct wrapped tensor object
@@ -280,7 +280,7 @@ Now, we can start to fill in function body step by step:
280
280
nodes_to_wrappers,
281
281
)
282
282
```
283
-
Althought the input / output activations might map to the graph IOs (a.k.a. user inputs / outputs) with corresponding type `QNN_TENSOR_TYPE_APP_READ` / `QNN_TENSOR_TYPE_APP_WRITE`. Users are still expected to have `QNN_TENSOR_TYPE_NATIVE` for all nodes' IOs and leave the detection logic handled inside `define_tensor` method.
283
+
Although the input / output activations might map to the graph IOs (a.k.a. user inputs / outputs) with corresponding type `QNN_TENSOR_TYPE_APP_READ` / `QNN_TENSOR_TYPE_APP_WRITE`. Users are still expected to have `QNN_TENSOR_TYPE_NATIVE` for all nodes' IOs and leave the detection logic handled inside `define_tensor` method.
284
284
285
285
5. Generate operator object in QNN graph:
286
286
```python
@@ -330,7 +330,7 @@ Now, we can start to fill in function body step by step:
330
330
- **data_type**: type compatible with QNN SDK, e.g. `QNN_DATATYPE_FLOAT_32`, `QNN_DATATYPE_UINT_32`, etc.
331
331
- **rank**: dimensions of tensor
332
332
- **dims**: shape of tensor
333
-
- **data**: tesnor data
333
+
- **data**: tensor data
334
334
- **copy_data**: user should specify to True for constant parameters
335
335
336
336
8. Last, return operator object for partitioner to conduct validation:
- If online prepare mode is `enabled`, the following artifacts will be generated:
19
19
-`model`.dlc
20
20
-`optrace`.json
21
-
-`QHAS`
21
+
-`QHAS`.json
22
22
- If online prepare mode is `disabled`, the following artifacts will be generated:
23
23
-`model`.bin
24
24
-`optrace`.json
@@ -92,3 +92,160 @@ Note: Files ending with `.bin ` do not support graph visualization in qairt_visu
92
92
</figure>
93
93
94
94
For more details, visit the [QAIRT Visualizer](https://pypi.org/project/qairt-visualizer/).
95
+
96
+
97
+
# ExecuTorch QNN Intermediate Output Debugger
98
+
99
+
ExecuTorch QNN Intermediate Output Debugger is a tool that helps users debug intermediate output accuracy by comparing CPU outputs with QNN outputs. This tool offers a variety of output formats and flexibility for users to define their own metrics when debugging.
100
+
101
+
Below, we will go through the details step by step on how to customize your own debugger. By the end of this tutorial, users should understand the mechanism behind the ExecuTorch QNN Debugger and how to apply the debugger to the desired model. In the rest of the tutorial, we will use the term `intermediate output` and `per-layer dump` interchangeably.
102
+
103
+
To make the implementation process smooth, we have also provided an example script, [qnn_intermediate_debugger_demo.py](../../../examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py), which is an end-to-end example that goes through the steps for implementation. Refer to [Example Script](#example-script) section for more information.
104
+
105
+
## Introduction
106
+
107
+
1. Why do we need ExecuTorch QNN Intermediate Output Debugger?
108
+
During inference, there might be gaps between QNN and CPU final outputs. This leaves developers unsure about the root cause of accuracy drop. By using this debugger, users can gain better insight into which operation is causing the accuracy drop. Please note that the accuracy drop here refers to comparing QNN with CPU outputs, not the ground truth.
109
+
110
+
2. Who is this tool for?
111
+
This tool is mainly for developers aiming to align QNN with CPU accuracy. Users will be able to identify which layer in the model is causing the accuracy drop, helping them either circumvent the issue by replacing the layer with other operations or contact authors in Qualcomm AI Engine Direct to resolve the accuracy issue. Please refer to the last section under [README.md](../README.md) for authors to contact when encountering any issues.
112
+
113
+
114
+
## Design Flow
115
+
```mermaid
116
+
flowchart TB;
117
+
nn.Module;
118
+
nn.Module --> edge_program["Edge Program"];
119
+
edge_program --> qnn_lower["QNN with Per-Layer Dump"];
120
+
qnn_lower --> qnn_inference[QNN Inference];
121
+
qnn_inference --> debug
122
+
edge_program --> cpu_lower["Edge CPU with Per-Layer Dump"];
123
+
cpu_lower --> cpu_inference["CPU Inference"];
124
+
cpu_inference --> debug["Debug"];
125
+
debug --> output["Output Results"]
126
+
```
127
+
128
+
## Instructions
129
+
130
+
### 1. Setup
131
+
1. Follow the [tutorial](https://pytorch.org/executorch/main/getting-started-setup) to set up ExecuTorch.
132
+
2. Follow the [tutorial](https://pytorch.org/executorch/stable/build-run-qualcomm-ai-engine-direct-backend.html) to build Qualcomm AI Engine Direct Backend.
133
+
134
+
### 2. Enable Flag
135
+
136
+
When executing the script, please add the flag `--dump_intermediate_outputs`. This tells QNN to dump all intermediate tensors during execution.
137
+
138
+
### 3. Add debugger to the example script
139
+
Initialize a `QNNIntermediateDebugger`. Please pass initialized `QNNIntermediateDebugger` and the `args.dump_intermediate_outputs` to `build_executorch_binary` method as well.
140
+
#### Example:
141
+
```python
142
+
from executorch.examples.qualcomm.utils import build_executorch_binary
143
+
from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger
dump_intermediate_outputs=args.dump_intermediate_outputs, # Add this flag
153
+
qnn_intermediate_debugger=qnn_intermediate_debugger, # Add this flag
154
+
)
155
+
```
156
+
157
+
### 4. Set data num to 1
158
+
It is perfectly fine for users to pass the desired amount of datasets to `build_executorch_binary`, which helps achieve better quantization results. However, after `build_executorch_binary` is called, we need to ensure that we only perform one inference during execution. Please ensure that CPU and QNN is using the same input during execution; otherwise, the debugging results might not be accurate.
159
+
160
+
### 5. Pass flag to SimpleADB
161
+
When creating `SimpleADB`, please also pass the flag `args.dump_intermediate_outputs`. This tells the runner to create files that store the intermediate output schema and binary data.
dump_intermediate_outputs=args.dump_intermediate_outputs, # Add this flag
174
+
)
175
+
```
176
+
177
+
### 6: Pull and process the results.
178
+
After QNN execution with the runner, if the previous steps are done correctly, we should be able to get two files: `etdump.etdp` and `debug_output.bin`.
179
+
The following example pulls the files back and calls a callback function to process the results. In this callback function, we create the `Inspector`. Then we perform CPU inference to get CPU intermediate results. Now, we have both QNN and CPU intermediate results, we can start generating results to compare the accuracy. Taking the following example, we should be able to get `debug_graph.svg` as an output in the current directory.
180
+
#### Example:
181
+
```python
182
+
from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import OutputFormat
The above example sets output formats as SVG and evaluation metrics using Cosine Similarity. Based on different needs, users can choose other output formats as shown in the `OutputFormat` class under [qnn_intermediate_debugger](./qnn_intermediate_debugger.py)
204
+
```python
205
+
classOutputFormat(IntEnum):
206
+
SVG_GRAPHS=0
207
+
CSV_FILES=1
208
+
DUMP_RAW=2
209
+
```
210
+
211
+
For evaluation metrics, if users would like to implement their own metrics, we have provided the option to implement [MetricEvaluatorBase](./metrics_evaluator.py). The following shows how to define custom metrics.
We have provided an inception_v3 demo script to help users better understand how to apply the debugger to their scripts. Please refer to [qnn_intermediate_debugger_demo.py](../../../examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py) for the example script.
239
+
240
+
Before running the example script, please ensure that dataset is downloaded. Example dataset can be retrieved [here](https://www.kaggle.com/datasets/ifigotin/imagenetmini-1000).
1. The current debugger only supports performing one execution. Multiple executions may cause unknown behavior and are not recommended.
249
+
2. Please ignore this if you are using `qnn_executor_runner`. If you have decided to write your own runner, please follow the [tutorial](https://pytorch.org/executorch/stable/etdump.html) on how to implement etdump into your own runner.
250
+
3. The current debugger does not support graph with partitions. (WIP)
251
+
4. The current debugger does not support LLM models. (WIP)
0 commit comments