fix npu qwen3.5 fla patch and use fla GDN#9746
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces helper functions to dynamically import flash linear attention kernels and patches the transformers' flash linear attention availability check for NPU environments. The review feedback suggests retrieving patched module-level attributes via sys.modules since they are not directly available on the model instance, and adding fallbacks to the standalone causal_conv1d package when fla is not installed to improve NPU compatibility.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| _try_import_flash_linear_attention_kernels() | ||
| mod._swift_fla_causal_conv1d_fn = getattr(mod, 'causal_conv1d_fn', None) or causal_conv1d | ||
| mod.chunk_gated_delta_rule = getattr(mod, 'chunk_gated_delta_rule', None) or chunk_gated_delta_rule |
There was a problem hiding this comment.
Since mod is an instance of Qwen3_5GatedDeltaNet, module-level patched variables like chunk_gated_delta_rule (patched via setattr(module, ...) in patch_qwen3_5_chunk_gated_delta_rule_with_mindspeed) are not attributes of the instance mod. Therefore, getattr(mod, 'chunk_gated_delta_rule', None) will return None. By looking up the module of mod via sys.modules, we can correctly retrieve the patched mindspeed version of chunk_gated_delta_rule and causal_conv1d, preventing an ImportError when fla is not installed.
| _try_import_flash_linear_attention_kernels() | |
| mod._swift_fla_causal_conv1d_fn = getattr(mod, 'causal_conv1d_fn', None) or causal_conv1d | |
| mod.chunk_gated_delta_rule = getattr(mod, 'chunk_gated_delta_rule', None) or chunk_gated_delta_rule | |
| import sys | |
| _try_import_flash_linear_attention_kernels() | |
| module = sys.modules.get(mod.__module__) if mod else None | |
| mod._swift_fla_causal_conv1d_fn = ( | |
| getattr(mod, 'causal_conv1d_fn', None) | |
| or (getattr(module, 'causal_conv1d_fn', None) if module else None) | |
| or (getattr(module, 'causal_conv1d', None) if module else None) | |
| or causal_conv1d | |
| ) | |
| mod.chunk_gated_delta_rule = ( | |
| getattr(mod, 'chunk_gated_delta_rule', None) | |
| or (getattr(module, 'chunk_gated_delta_rule', None) if module else None) | |
| or chunk_gated_delta_rule | |
| ) |
| if causal_conv1d is None: | ||
| try: | ||
| from fla.modules.convolution import causal_conv1d as _causal_conv1d | ||
| causal_conv1d = _causal_conv1d | ||
| except Exception: | ||
| pass |
There was a problem hiding this comment.
If fla is not installed, we can fallback to importing causal_conv1d_fn from the standalone causal_conv1d package, which is commonly installed on NPU environments.
| if causal_conv1d is None: | |
| try: | |
| from fla.modules.convolution import causal_conv1d as _causal_conv1d | |
| causal_conv1d = _causal_conv1d | |
| except Exception: | |
| pass | |
| if causal_conv1d is None: | |
| try: | |
| from fla.modules.convolution import causal_conv1d as _causal_conv1d | |
| causal_conv1d = _causal_conv1d | |
| except Exception: | |
| try: | |
| from causal_conv1d import causal_conv1d_fn as _causal_conv1d | |
| causal_conv1d = _causal_conv1d | |
| except Exception: | |
| pass |
| def _is_flash_linear_attention_importable_on_npu() -> bool: | ||
| try: | ||
| from fla.modules.convolution import causal_conv1d # noqa: F401 | ||
| from fla.ops.gated_delta_rule import chunk_gated_delta_rule # noqa: F401 | ||
| return True | ||
| except Exception: | ||
| return False |
There was a problem hiding this comment.
If fla is not installed, but the standalone causal_conv1d package is installed, we can still support flash linear attention on NPU because we have the embedded mindspeed version of chunk_gated_delta_rule. Adding this fallback allows the NPU patch to be successfully enabled without requiring fla to be installed.
| def _is_flash_linear_attention_importable_on_npu() -> bool: | |
| try: | |
| from fla.modules.convolution import causal_conv1d # noqa: F401 | |
| from fla.ops.gated_delta_rule import chunk_gated_delta_rule # noqa: F401 | |
| return True | |
| except Exception: | |
| return False | |
| def _is_flash_linear_attention_importable_on_npu() -> bool: | |
| try: | |
| from fla.modules.convolution import causal_conv1d # noqa: F401 | |
| from fla.ops.gated_delta_rule import chunk_gated_delta_rule # noqa: F401 | |
| return True | |
| except Exception: | |
| try: | |
| from causal_conv1d import causal_conv1d_fn # noqa: F401 | |
| return True | |
| except Exception: | |
| return False |
There was a problem hiding this comment.
Pull request overview
This PR updates the Ascend NPU Qwen3.5 linear-attention “packing/padding-free” path to rely on upstream flash-linear-attention (FLA) public operators (causal-conv + GDN), adds NPU-specific FLA availability probing for Transformers, and introduces restoration logic to keep FLA’s native Triton-Ascend GDN callable from being replaced by MindSpeed patching/caching. It also removes ms-swift’s embedded MindSpeed GDN copy and updates related NPU/Qwen3.5 documentation.
Changes:
- Add an NPU-only supplement to Transformers’
is_flash_linear_attention_available()that treats FLA as available when its public causal-conv and GDN entry points are importable. - Prefer upstream FLA GDN in Megatron/MindSpeed flows by restoring
fla.ops.gated_delta_rule.chunk_gated_delta_rule(and refreshingmcore-bridge’s cached reference when present). - Remove
swift.model.chunk_gated_delta_ruleand update Qwen3.5 docs/NPU docs to reflect the new runtime dispatch and recommended FLA installation.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| swift/model/npu_patcher.py | Re-export the new MindSpeed→FLA GDN restoration helper for NPU flows. |
| swift/model/npu_patch/init.py | Export patch_mindspeed_fla_gdn_implementation from the NPU patch package. |
| swift/model/npu_patch/model.py | Add NPU-only FLA import-based availability probing and avoid CUDA-init fused norm on NPU. |
| swift/model/npu_patch/mindspeed.py | Add best-effort restoration logic for upstream FLA GDN and refresh mcore-bridge cache. |
| swift/model/models/qwen.py | Avoid permanently caching missing FLA kernels; retry imports at runtime for packing/sequence-parallel. |
| swift/model/chunk_gated_delta_rule.py | Remove embedded MindSpeed-derived GDN implementation (duplicate maintenance). |
| swift/megatron/pipelines/train/sft.py | Restore upstream FLA GDN after MindSpeed repatch() in the Megatron SFT pipeline. |
| swift/megatron/init.py | Restore upstream FLA GDN during Megatron environment initialization on NPU. |
| docs/source/BestPractices/Qwen3_5-Best-Practice.md | Update NPU guidance and call-path notes for FLA GDN usage. |
| docs/source/BestPractices/NPU-support.md | Update NPU Qwen3.5 patch notes to reflect upstream FLA Triton-Ascend GDN usage. |
| docs/source_en/BestPractices/Qwen3_5-Best-Practice.md | English equivalent documentation updates for FLA GDN on NPU. |
| docs/source_en/BestPractices/NPU-support.md | English equivalent NPU Qwen3.5 patch notes updates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def patch_mindspeed_fla_gdn_implementation() -> None: | ||
| """Best-effort preference for upstream FLA while preserving the current GDN implementation.""" | ||
| from mindspeed.patch_utils import MindSpeedPatchesManager | ||
|
|
||
| try: | ||
| _patch_mindspeed_fla_gdn_implementation(MindSpeedPatchesManager) | ||
| except Exception as e: | ||
| logger.warning('Failed to apply the optional FLA GDN patch; keep the current implementation: %s', e) |
| 2. MindSpeed 初始化以及训练参数 `repatch()` 可能会替换 FLA 的公共 GDN 入口。ms-swift 会在构建 `mcore-bridge` 前和 `repatch()` 后,优先从 MindSpeed patch manager 保存的 `orig_func` 恢复 FLA 原始 callable;如果 FLA 缺失或恢复失败,则保留 MindSpeed/Megatron 提供的原生 Torch callable。如果 `mcore-bridge` 已缓存该 callable,也会同步刷新。启动日志会记录最终选择或降级原因。原生 Torch fallback 不支持 packed/varlen GDN;这类输入仍需要安装 FLA。 | ||
|
|
||
| 3. 因此当前建议:transformers 后端避免设置 `--sequence_parallel_size` 大于 `1`,并避免使用 `--packing true` / `--padding_free true`;Megatron-SWIFT 后端`--context_parallel_size` 保持为 `1`,并同样避免使用 `--packing true` / `--padding_free true`。只有在目标 MindSpeed/FLA 版本明确补齐支持并完成分层验证后,才重新开启这些特性。 | ||
| 3. 当前已验证 Qwen3.5-4B 在 8 卡数据并行、`USE_MCORE_GDN=0`、`packing=true`、TP=1、CP=1 下完成 2 个有限值 loss/grad_norm 训练步并保存 checkpoint;各 rank 最终记录的 GDN callable 均来自 FLA 的 `fla.ops.gated_delta_rule.chunk`。这证明了该组合下的运行时分发、前向、反向与保存链路。 |
|
|
||
| 2. MindSpeed initialization and training-argument `repatch()` may replace FLA's public GDN entry point. Before building `mcore-bridge` and again after `repatch()`, ms-swift first tries to restore FLA's original callable from the `orig_func` saved by the MindSpeed patch manager. If FLA is missing or restoration fails, it keeps the native Torch callable supplied by MindSpeed/Megatron. If `mcore-bridge` has already cached the callable, that reference is refreshed as well. Startup logs record the selected implementation or fallback reason. The native Torch fallback does not support packed/varlen GDN, which still requires FLA. | ||
|
|
||
| 3. Qwen3.5-4B has been validated for two training steps with finite loss and grad norm, followed by checkpoint saving, on 8-way data parallel with `USE_MCORE_GDN=0`, `packing=true`, TP=1, and CP=1. Every rank reported the final GDN callable from FLA's `fla.ops.gated_delta_rule.chunk`. This validates runtime dispatch, forward, backward, and saving for that tested combination. |
PR Type
PR Information
Background
When Qwen3.5 training enables
packing/ padding-free mode, the linear-attention layers need to invoke the causal-convolution and gated-delta-rule operators provided by FLA.This path currently has two compatibility issues on Ascend NPUs:
is_flash_linear_attention_available()check includes CUDA-related conditions. Even when the relevant FLA operators can be imported and run correctly on NPU, Transformers may still treat FLA as unavailable.repatch()may replacechunk_gated_delta_ruleat FLA’s public entry point. In addition,mcore-bridgemay cache the replaced callable early, preventing stable use of FLA’s native Triton-Ascend GDN implementation during training.Previously, ms-swift also maintained its own
chunk_gated_delta_rulecopied from MindSpeed. This implementation duplicates functionality already provided upstream by FLA and increases future maintenance and version-adaptation costs.Main Changes
1. Add FLA availability checks for Ascend NPUs
A NPU-specific FLA availability check is added. It verifies whether FLA is available by attempting to import the following public entry points:
fla.modules.convolution.causal_conv1dfla.ops.gated_delta_rule.chunk_gated_delta_ruleWhen both entry points can be imported successfully, the FLA fast path is enabled for Transformers’ Qwen3.5 and Qwen3.5-MoE modules.
This only supplements the availability check in NPU environments and does not change the existing Transformers behavior on GPUs or other devices.
2. Use upstream FLA operators in the Qwen3.5 packing path
Nonevalues from being permanently cached when the initial module import failed because of the availability check.causal_conv1dinterface.swift.model.chunk_gated_delta_ruleimplementation, so ms-swift no longer maintains a copy of MindSpeed’s GDN code.The expected call chain is:
3. Restore FLA GDN after MindSpeed patching
MindSpeed initialization and training-argument
repatch()may replace:This PR adds restoration logic:
orig_funcbyMindSpeedPatchesManager;chunkmodule;repatch();mcore-bridgehas already been imported and cached the GDN callable, refresh its cached reference as well;Expected log output:
4. Keep FLA as an optional dependency for regular Megatron training
FLA is the preferred implementation for the current Qwen3.5 NPU packing path, but it does not become a mandatory dependency for all NPU Megatron training workloads.
If FLA GDN cannot be imported or restored:
cu_seqlens, fail at the actual GDN invocation.Therefore, regular non-packing Megatron training will not exit during initialization solely because FLA is unavailable. Whether packing/variable-length GDN is available depends on the implementation actually selected at runtime.
5. Keep Qwen3.5’s native normalization implementation on NPU
FLA’s
FusedRMSNormGatedcurrently contains:This triggers CUDA-related initialization issues in NPU environments.
Therefore, this PR continues to use Qwen3.5’s native PyTorch gated RMSNorm implementation on NPU. This only affects normalization and does not prevent GDN from using FLA’s Triton-Ascend backend.
6. Documentation updates
mcore-bridge;mainbranch to obtain Triton-Ascend GDN support.Validation
Transformers/FSDP Training
The validated configuration is as follows:
alpaca-gpt4-data-zhpacking=truemax_length=512per_device_train_batch_size=1gradient_accumulation_steps=1During training, both loss and gradient norm remained finite, and model saving completed successfully.
Runtime logs confirmed that the final GDN callable came from:
GPU/NPU Numerical Comparison
A comparison experiment was performed on a GPU server using the same model, dataset, and training hyperparameters.
The GPU and NPU loss values and convergence trends were generally aligned.
Under the validated configuration above, Qwen3.5-4B can complete packing training on Ascend NPUs using FLA’s native Triton-Ascend GDN implementation. The forward pass, backward pass, and model-saving pipeline all run successfully, while maintaining broadly consistent loss values and convergence trends with GPU training.