[Qualcomm] Support native_layer_norm and affine-free LayerNorm in QNN backend#18990
[Qualcomm] Support native_layer_norm and affine-free LayerNorm in QNN backend#18990KevinUW114514 wants to merge 3 commits intopytorch:mainfrom
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/18990
Note: Links to docs will display an error until the docs builds have been completed. This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
Hi @KevinUW114514! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
@pytorchbot label "release notes: none" |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
There was a problem hiding this comment.
Pull request overview
Fixes a crash in the Qualcomm QNN PT2E quantizer by making _mark_nodes_as_annotated robust to None entries in node lists (e.g., when aten.layer_norm has optional affine args like weight=None).
Changes:
- Skip
Noneentries in_mark_nodes_as_annotatedto avoidAttributeErrorwhen accessingnode.meta.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -29,6 +29,8 @@ | |||
|
|
|||
| def _mark_nodes_as_annotated(nodes: List[Node]): | |||
| if node is None: | ||
| continue |
|
Hi @KevinUW114514 thank you for your contribution. I think the root cause is that we need to guard weight and bias creation in rules files for htp and lpai similar to #18219, let me know if you are willing to change it. Adding the guard might silently propagate bad configs like these in the pipeline and i think we should fail loudly. CC: @shewu-quic |
|
Hi @abhinaykukkadapu , thanks for the follow-up! Actually I also realized this root issue as I encountered the error in my downstream tasks. I am currently working on fixing this. I can edit the issue and PR to re-state the issue and submit a complete fix for it. Let me know if any concern. Thank you! |
Thanks, that would be awesome, will look forward to your changes. |
Fixes AttributeError when aten.native_layer_norm has optional weight=None. Both weight and bias are guarded to handle the None case gracefully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… backend - add QNN layer norm support for aten.native_layer_norm.default - handle missing weight/bias by creating identity weight and zero bias - always provide bias tensor for QNN LayerNorm op - add floating-point and quantized tests for native_layer_norm - print generated pte filename after export
[Qualcomm] Support native_layer_norm and affine-free LayerNorm in QNN backend
Summary
Adds QNN backend support for
aten.native_layer_norm.default(which is the decomposed form oftorch.nn.LayerNorm) and handles models where weight/bias are not provided (elementwise_affine=False).Problem
When exporting models with
torch.native_layer_normortorch.nn.LayerNorm(affine=False)to the QNN backend, the following issues occur:Missing
native_layer_normvisitor: The originalLayerNormVisitoronly targetsaten.layer_norm.default, but PyTorch decomposestorch.nn.LayerNormtoaten.native_layer_norm.defaultduring export.None weight/bias: When
elementwise_affine=False, the weight and bias arguments areNone. QNN x86_64 runtime cannot handleNonetensor inputs, causingAttributeErrorwhen callingget_parameter().Solution
1. Update visitor target (
op_layer_norm.py)Change the visitor target from
aten.layer_norm.defaulttoaten.native_layer_norm.default:This is correct because during ExecuTorch export,
aten.layer_norm.defaultis decomposed toaten.native_layer_norm.defaultbefore the QNN lowering stage.2. Handle None weight/bias (
op_layer_norm.py)When weight/bias are
None, create synthetic tensors:torch.ones(normalized_shapes)(identity transform)torch.zeros(normalized_shapes)(no offset)Create synthetic
fx.Nodeobjects to register these as QNN static tensors:3. Use same annotator for both ops (
htp_rules.py)The quantizer annotator registers both
aten.layer_norm.defaultandaten.native_layer_norm.defaultto the sameLayerNormclass, since both ops have identical argument schemas:4. Add None check to
get_parameter()(utils.py)Guard against
Nonenodes to preventAttributeError:Files Changed
builders/op_layer_norm.pynative_layer_normsupport + handle None weight/biasbuilders/utils.pyget_parameter()quantizer/annotators/htp_rules.pytests/models.pyNativeLayerNormtest modeltests/test_qnn_delegate.pyTest Plan
Run QNN delegate tests for layer_norm:
python backends/qualcomm/tests/test_qnn_delegate.py \ -k "test_qnn_backend_layer_norm or test_qnn_backend_native_layer_norm" \ --soc_model SM8650 \ --build_folder build-x86/ \ --executorch_root . \ --enable_x86_64Expected: 4 tests pass (2 floating-point, 2 quantized).
Release Notes
Release notes: qualcommRelated Issues
This resolves the issue where FLUX2 transformer export fails with:
[QNN Delegate Op Builder]: LayerNorm weight is None, skippingAttributeError: 'NoneType' object has no attribute 'name'Fixes #18989
@abhinaykukkadapu