Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger - #15735

Merged
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger
Dec 17, 2025
Merged

Qualcomm AI Engine Direct - QNN ExecuTorch Intermediate Output Debugger#15735
cccclai merged 6 commits into
pytorch:mainfrom
CodeLinaro:dev1/winskuo/intermediate_tensor_debugger

Conversation

@winskuo-quic

Copy link
Copy Markdown
Collaborator

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.
image

What's Coming Next?

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

@pytorch-bot

pytorch-botBot commented Nov 11, 2025

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/15735

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 3 Unrelated Failures

As of commit 0dfbbd4 with merge base 82e37df (image):

NEW FAILURE - The following job has failed:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 11, 2025
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai

Copy link
Copy Markdown
Contributor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

@cccclai

Copy link
Copy Markdown
Contributor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

@billmguo

Copy link
Copy Markdown
Contributor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the QNN ExecuTorch Intermediate Output Debugger, a comprehensive debugging tool for comparing intermediate tensor outputs between QNN and CPU execution to identify accuracy issues in model inference.

Key Changes

  • New Debugging Framework: Added a complete debugger implementation with support for custom metrics, multiple output formats (SVG, CSV, raw files), and per-layer accuracy comparison
  • API and Utilities: Introduced QNNIntermediateDebugger class with configurable evaluators and output format options
  • Enhanced Testing: Updated test infrastructure to validate intermediate output dumping and comparison for various model architectures

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 21 comments.

Show a summary per file
FileDescription
backends/qualcomm/debugger/qnn_intermediate_debugger.pyCore debugger implementation for matching and processing QNN vs CPU intermediate tensors
backends/qualcomm/debugger/metrics_evaluator.pyBase class and concrete metric evaluators (cosine similarity, ATOL, MSE) for tensor comparison
backends/qualcomm/debugger/format_outputs.pyOutput formatting utilities for SVG graph visualization, CSV export, and raw tensor dumps
backends/qualcomm/debugger/README.mdComprehensive documentation with tutorial, design flow, and usage examples
examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.pyEnd-to-end demo script using InceptionV3 to showcase debugger capabilities
examples/qualcomm/utils.pyAdded debugger integration to build_executorch_binary() with spelling fixes
backends/qualcomm/tests/utils.pyEnhanced test utilities to validate intermediate output dumping and comparison
backends/qualcomm/tests/test_qnn_delegate.pyAdded new tests for intermediate debugger and refactored existing tests
backends/qualcomm/qnn_preprocess.pyMoved axis order metadata cleanup logic from partitioner to preprocessor
backends/qualcomm/partition/qnn_partitioner.pyRemoved axis order cleanup (now handled in preprocessor)
backends/qualcomm/builders/node_visitor.pyAdded QNN tensor name tracking to node metadata for debugger
backends/qualcomm/utils/constants.pyAdded QCOM_TENSOR_NAME constant for intermediate output tracking
examples/qualcomm/executor_runner/qnn_executor_runner.cppIncreased debug buffer size from 20MB to 100MB
examples/qualcomm/qnn_intermediate_output_inspector.pyRemoved obsolete intermediate output inspector (replaced by new debugger)
examples/qualcomm/README.mdAdded documentation for new util_scripts folder
examples/qualcomm/qaihub_scripts/llama/README.mdFixed spelling: "futher" → "further"
backends/qualcomm/quantizer/README.mdFixed spelling: "utilies" → "utilities", "converage" → "coverage"
backends/qualcomm/builders/README.mdFixed spelling and typos in documentation
backends/qualcomm/README.mdUpdated code review authors list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py Outdated
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/metrics_evaluator.py
Comment threadbackends/qualcomm/debugger/qnn_intermediate_debugger.py
Comment threadbackends/qualcomm/tests/test_qnn_delegate.py Outdated
Comment threadbackends/qualcomm/tests/utils.py
Comment threadbackends/qualcomm/debugger/README.md Outdated
Comment threadbackends/qualcomm/tests/utils.py Outdated
Comment on lines +311 to +312
self.assertTrue(
len(node_tensor_map) == expected_compared_events,

CopilotAINov 17, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(
len(node_tensor_map)==expected_compared_events,
self.assertEqual(
len(node_tensor_map),expected_compared_events,

Copilot uses AI. Check for mistakes.
@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Seems like missing some dependency

ModuleNotFoundError: No module named 'pydot'

Do you want to introduce this dependency in general?

Hi @cccclai,
Thanks for the suggestion. As there are a couple of install_requirments.sh in the codebase, do you have any suggestions on which install_requirments.sh should we put this under?

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

@winskuo-quic do you able to try story llm and see what is cos similarity it is?

Hi @billmguo,
As mentioned in the PR summary and Limitations section under backends/qualcomm/debugger/README.md, LLM models are currently unsupported. This is in our TODO list and we will enable this in the future.
Thanks.

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @winskuo-quic Thanks so much for this contribution — we really appreciate the Qualcomm team’s work here.

One question I had while reviewing the PR: several parts of the implementation seem to re-create functionality that already exists in ExecuTorch’s devtools (intermediate_output_capturer, numeric comparators, Inspector.calculate_numeric_gap, debug-handle-based operator matching, etc.), rather than extending the shared workflows.

This might simply be a gap in my understanding, so I’d love to learn more about your experience here:

  • Were there limitations or missing features in the current Inspector-based workflow that made it difficult to apply to QNN?
  • Or were there usability concerns that motivated introducing a separate set of APIs?

If there are gaps, we’d be very happy to collaborate and strengthen the shared debugging tools so all backends can benefit from a unified workflow.

Looking forward to your thoughts!

Hi @Gasoonjia,

Thanks for reviewing the PR.
The reason is written under the PR summary.
I will also share more details in the email thread.
Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thank you for enable this feature and the detailed documentation! Just minor comments, can we move examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py just a bit clear on the content inside the folder

Just to confirm, do you mean moving from executorch/examples/qualcomm/util_scripts/qnn_intermediate_debugger_demo.py to examples/qualcomm/devtools/qnn_intermediate_debugger_demo.py? Thanks.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 4bdfe6e to fe228e9CompareNovember 24, 2025 06:27
@Gasoonjia

Copy link
Copy Markdown
Contributor

hi @winskuo-quic:

Thanks again for your feedback, and your email reply. I understand this work may have been created quite a while ago in a private context.

I’m wondering whether there is any plan to migrate or align the current QNN debugger with the native ExecuTorch devtools — not just reusing intermediate_output_capturer or debug_handle, but more broadly integrating with the full debugging pipeline built around the Inspector.calculate_numeric_gap API for intermediate-output numerical discrepancy detection. Unifying these efforts could help us reduce divergence, avoid reinventing functionality, and centralize future debugging capabilities.

If you’re open to it, I’d be happy to discuss potential directions for collaboration on operator-level numerical discrepancy detection and how we can streamline the work going forward.

Thanks

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap.
We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined.
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Hi @Gasoonjia,

I think we are also aiming to reduce as much of code redundancy as possible, which should align with your plan. I believe Inspector.calculate_numeric_gap will be helpful when debugging, however, we might still want to keep some of our features, such as ability to draw the .svg gaphs. Also, if I understand correctly, Inspector.calculate_numeric_gap currently only supports MSE, L1, and SNR. It would be awesome if we could have some public APIs for users to define their own metrics for numeric gap. We could discuss more in detail in future on how we could potentially unify and migrate some features if you think they can be combined. Thanks

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Thanks for efficient reply and sharing your thoughts regarding our current API.

with #15969, now we can create customize metrics for numerical gap detection.

Im more than happy to have your more thoughts regarding the API, and looking forward to the future cooperation!

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled!
In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you?
Thanks

@Gasoonjia

Copy link
Copy Markdown
Contributor

Thanks for sharing the PR. I think this would be super helpful. Looking forward for future cooperation and transition to this API once the debug_handle is enabled! In the meanwhile, since debug_handle is not yet enabled, do you think this PR looks fine to you? Thanks

This PR looks fine to me and thanks for your contribution! Will let @cccclai for final stamp.
I'm looking forward to working with you to contribute into devtool directly together In the future.

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from e17f559 to 6328f7bCompareNovember 25, 2025 05:28
Comment thread.ci/scripts/test_model.sh Outdated
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/
export PYTHONPATH=$EXECUTORCH_ROOT/..

pip install pydot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we introduce the dependency here, let's also add it in the build.sh

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
I have removed pip install pydot from .ci/scripts/test_model.sh & .ci/scripts/test_qnn_static_llm.sh, and I have introduced the dependency under build.sh since both ci files will call build.sh.

from executorch.backends.qualcomm.debugger.qnn_intermediate_debugger import QNNIntermediateDebugger

qnn_intermediate_debugger = QNNIntermediateDebugger()
build_executorch_binary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use to_edge..._qnn() API as it's the newer API?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
Actually, build_executorch_binary should still be a more official API to users. Inside build_executorch_binary, we will then call to_edge_transform_and_lower_to_qnn. I think it will be more straightforward to use build_executorch_binary since to_edge_transform_and_lower_to_qnn requires users to handle quantization themself and some post processing after the model is lowered.
We might consider moving these API functions to a new file in the future, making it easier for users to know what are some released APIs to use.

@meta-codesync

Copy link
Copy Markdown
Contributor

@cccclai has imported this pull request. If you are a Meta employee, you can view this in D87936803.

@cccclaicccclai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for clarifying. Can you rebase the PR? The CI stopped working last week

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from dfe9aeb to 724ad4cCompareDecember 2, 2025 01:21
@cccclai

Copy link
Copy Markdown
Contributor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 724ad4c to b154f22CompareDecember 2, 2025 06:13
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

It seems like I merge some PRs that have conflict with this PR...can you rebase again?

Done. Thanks

@cccclai

Copy link
Copy Markdown
Contributor

There are some internal errors, I need to send a patch

@cccclai

Copy link
Copy Markdown
Contributor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from b154f22 to 2c1598cCompareDecember 16, 2025 01:59
@winskuo-quic
winskuo-quicforce-pushed the dev1/winskuo/intermediate_tensor_debugger branch from 2c1598c to 0dfbbd4CompareDecember 16, 2025 02:00
@winskuo-quic

Copy link
Copy Markdown
CollaboratorAuthor

Can you apply these changes

--- a/executorch/backends/qualcomm/debugger/TARGETS
+++ b/executorch/backends/qualcomm/debugger/TARGETS
@@ -10,3 +10,21 @@
"fbsource//third-party/pypi/pandas:pandas",
]
)
+
+runtime.python_library(
+ name = "qnn_intermediate_debugger",
+ srcs = [
+ "format_outputs.py",
+ "metrics_evaluator.py",
+ "qnn_intermediate_debugger.py",
+ ],
+ deps = [
+ "//caffe2:torch",
+ "//executorch/backends/qualcomm/_passes:passes",
+ "//executorch/backends/qualcomm/utils:utils",
+ "//executorch/devtools:lib",
+ "//executorch/exir:sym_util",
+ "fbsource//third-party/pypi/graphviz:graphviz",
+ "fbsource//third-party/pypi/pandas:pandas",
+ ],
+)
diff --git a/executorch/backends/qualcomm/tests/TARGETS b/executorch/backends/qualcomm/tests/TARGETS
--- a/executorch/backends/qualcomm/tests/TARGETS
+++ b/executorch/backends/qualcomm/tests/TARGETS
@@ -35,6 +35,7 @@
"//executorch/examples/qualcomm:utils",
"//executorch/examples/models:models",
"//executorch/backends/qualcomm/debugger:utils",
+ "//executorch/backends/qualcomm/debugger:qnn_intermediate_debugger",
],
)

Sorry I missed this message. I have applied the patch and pushed a new commit. Please have a look. Thanks

etdump_path=f"{args.artifact}/etdump.etdp",
debug_buffer_path=f"{args.artifact}/debug_output.bin",
)
qnn_intermediate_debugger.intermediate_output_module(*(inputs[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a separate tool? doesnt inspector's intermediate compare util work?

@winskuo-quicwinskuo-quicDec 17, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kimishpatel,
Thanks for reviewing the PR.
As mentioned above, we are working on enabling debug handle, which is required for the ExecuTorch Inspector API. After this PR is merged, we will have the debug_handle PR and reusing Inspector tool ready shortly. Thanks

```

#### Additional Options
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I yhink what we should really want is the inspector's intermediate otuput compare be the single API, regardless of the backend, to enable comparison of the intermediate values.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to the response above. Thanks

@cccclai
cccclai merged commit ee7ab19 into pytorch:mainDec 17, 2025
139 of 143 checks passed
xingguo01 pushed a commit to xingguo01/executorch that referenced this pull request Dec 18, 2025
…er (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`
jirioc pushed a commit to nxp-upstream/executorch that referenced this pull request Dec 19, 2025
…er (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`
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@winskuo-quic@cccclai@billmguo@Gasoonjia@kimishpatel