Skip to content

Commit 6ef7b38

Browse files
[https://nvbugs/6221450][fix] AutoDeploy: Qwen3.5 400B NVFP4 accuracy regression fix (#14667)
Signed-off-by: Taylor Yeonbok Lee <249374542+taylor-yb-lee@users.noreply.github.com>
1 parent e58e758 commit 6ef7b38

5 files changed

Lines changed: 72 additions & 35 deletions

File tree

examples/auto_deploy/model_registry/configs/qwen3.5_moe_400b.yaml

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,10 @@ transforms:
2929
enabled: true
3030
fuse_nvfp4_moe:
3131
backend: trtllm_gen
32-
detect_sharding:
33-
# for long input, tp8ep1 gives better performance
34-
# dist_mapping: {moe_tp: 8, moe_ep: 1}
32+
apply_sharding_hints:
3533
allreduce_strategy: SYMM_MEM
36-
shard_all_unprocessed: true
37-
simple_shard_filter: "lm_head"
38-
sharding_dims: ['tp','ep', 'bmm']
39-
# use only manual config for TP sharding
40-
sharding_source: ['manual']
41-
manual_config:
42-
tp_plan:
43-
# GDN layer
44-
"in_proj_qkv": "delta"
45-
# attention layer
46-
"q_proj": "colwise"
47-
"k_proj": "colwise"
48-
"v_proj": "colwise"
49-
"o_proj": "rowwise"
50-
# lm_head: "gather" = column split + all_gather (not "colwise" which
51-
# requires a LayerSubgraph and crashes for standalone unprocessed nodes)
52-
"lm_head": "gather"
53-
# replicating shared experts (keep them commented out)
54-
# "shared_expert_gate_proj": "colwise"
55-
# "shared_expert_up_proj": "colwise"
56-
# "shared_expert_down_proj": "rowwise"
57-
# gating layer should be replicated as well
58-
# "gate": "gather"
34+
# Shared expert is excluded from sharding for performance purpose
35+
shard_layers: ["moe", "delta", "mha"]
5936
multi_stream_moe:
6037
stage: compile
6138
enabled: true

tensorrt_llm/_torch/auto_deploy/custom_ops/linear/swiglu.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def torch_swiglu_mlp(
5151
gate_bias: Optional[torch.Tensor],
5252
up_bias: Optional[torch.Tensor],
5353
down_bias: Optional[torch.Tensor],
54+
layer_type: str = "unknown",
5455
) -> torch.Tensor:
5556
"""Standardized SwiGLU MLP operation.
5657
@@ -86,6 +87,7 @@ def _(
8687
gate_bias: Optional[torch.Tensor],
8788
up_bias: Optional[torch.Tensor],
8889
down_bias: Optional[torch.Tensor],
90+
layer_type: str = "unknown",
8991
) -> torch.Tensor:
9092
"""Fake implementation for tracing."""
9193
# Output shape is [..., hidden_size] where hidden_size = down_weight.shape[0]
@@ -159,6 +161,7 @@ def torch_nvfp4_swiglu_mlp(
159161
down_input_scale: torch.Tensor,
160162
down_weight_scale: torch.Tensor,
161163
down_alpha: torch.Tensor,
164+
layer_type: str = "unknown",
162165
) -> torch.Tensor:
163166
"""NVFP4 quantized SwiGLU MLP operation (intermediate representation).
164167
@@ -230,6 +233,7 @@ def _(
230233
down_input_scale: torch.Tensor,
231234
down_weight_scale: torch.Tensor,
232235
down_alpha: torch.Tensor,
236+
layer_type: str = "unknown",
233237
) -> torch.Tensor:
234238
"""Fake implementation for tracing."""
235239
# Output shape: [..., hidden_size] where hidden_size = down_weight.shape[0]
@@ -323,6 +327,7 @@ def torch_finegrained_fp8_swiglu_mlp(
323327
gate_weight_scale: torch.Tensor,
324328
up_weight_scale: torch.Tensor,
325329
down_weight_scale: torch.Tensor,
330+
layer_type: str = "unknown",
326331
) -> torch.Tensor:
327332
"""FineGrained FP8 quantized SwiGLU MLP operation (intermediate representation).
328333
@@ -382,6 +387,7 @@ def _(
382387
gate_weight_scale: torch.Tensor,
383388
up_weight_scale: torch.Tensor,
384389
down_weight_scale: torch.Tensor,
390+
layer_type: str = "unknown",
385391
) -> torch.Tensor:
386392
"""Fake implementation for tracing."""
387393
# Output shape: [..., hidden_size] where hidden_size = down_weight.shape[0]

tensorrt_llm/_torch/auto_deploy/models/custom/modeling_qwen3_5_moe.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -630,21 +630,21 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:
630630
self.gate_proj.weight,
631631
self.gate_proj.bias,
632632
tp_mode="colwise",
633-
layer_type="moe",
633+
layer_type="shared_expert",
634634
)
635635
up = torch.ops.auto_deploy.torch_linear_simple(
636636
x,
637637
self.up_proj.weight,
638638
self.up_proj.bias,
639639
tp_mode="colwise",
640-
layer_type="moe",
640+
layer_type="shared_expert",
641641
)
642642
return torch.ops.auto_deploy.torch_linear_simple(
643643
self.act_fn(gate) * up,
644644
self.down_proj.weight,
645645
self.down_proj.bias,
646646
tp_mode="rowwise",
647-
layer_type="moe",
647+
layer_type="shared_expert",
648648
)
649649

650650

@@ -765,8 +765,11 @@ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
765765
layer_type="moe",
766766
)
767767

768-
expert_output = expert_output + shared_expert_output
768+
# The shared expert is replicated (excluded from TP sharding), so all-reduce
769+
# the sharded routed-expert output first, then add the replicated shared
770+
# output; adding before would scale it by the TP world size.
769771
expert_output = torch.ops.auto_deploy.all_reduce(expert_output, layer_type="moe")
772+
expert_output = expert_output + shared_expert_output
770773

771774
expert_output = expert_output.reshape(batch_size, sequence_length, hidden_dim)
772775
return expert_output

tensorrt_llm/_torch/auto_deploy/transform/library/fuse_swiglu.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
The SwiGLU pattern is: silu(x @ gate.T) * (x @ up.T) @ down.T
2323
"""
2424

25+
from contextlib import contextmanager
2526
from typing import Tuple, Type
2627

2728
import torch
@@ -47,7 +48,7 @@
4748
eliminate_dead_code,
4849
get_attr_by_name,
4950
)
50-
from ...utils.node_utils import is_op
51+
from ...utils.node_utils import extract_op_args, is_op, set_op_args
5152
from ...utils.pattern_matcher import ADPatternMatcherPass, register_ad_pattern
5253
from ...utils.quantization_utils import ensure_tma_col_major
5354
from ..interface import (
@@ -59,6 +60,42 @@
5960
)
6061

6162

63+
def _weight_key(node: Node):
64+
"""Stable key for a weight arg: the get_attr target FQN (survives node re-creation
65+
during pattern replacement), falling back to node identity. ``args[1]`` is the
66+
weight for both linear and SwiGLU ops."""
67+
w = node.args[1] if len(node.args) > 1 else None
68+
if not isinstance(w, Node):
69+
return None
70+
return w.target if w.op == "get_attr" else w
71+
72+
73+
@contextmanager
74+
def preserve_layer_types(gm: GraphModule, linear_op, fused_op):
75+
"""Carry the ``layer_type`` hint across a fusion that consumes ``linear_op`` nodes
76+
and emits ``fused_op`` nodes (which would otherwise drop the hint).
77+
78+
Snapshots each source weight's ``layer_type`` before the rewrite, then re-applies
79+
it to the fused node keyed by weight, so hint-driven sharding (``shard_layers``)
80+
can still classify the fused node. Wrap the matcher's ``patterns.apply`` call.
81+
"""
82+
wmap = {}
83+
for n in gm.graph.nodes:
84+
if is_op(n, linear_op):
85+
[lt] = extract_op_args(n, "layer_type")
86+
key = _weight_key(n)
87+
if lt is not None and key is not None:
88+
wmap[key] = lt
89+
yield
90+
if not wmap:
91+
return
92+
for n in gm.graph.nodes:
93+
if is_op(n, fused_op):
94+
key = _weight_key(n)
95+
if key is not None and key in wmap:
96+
set_op_args(n, layer_type=wmap[key])
97+
98+
6299
def _maybe_to_deepgemm_layout(
63100
weight: torch.Tensor, scale: torch.Tensor
64101
) -> Tuple[torch.Tensor, torch.Tensor]:
@@ -242,7 +279,12 @@ def _apply(
242279
dummy_args=dummy_args_with_bias,
243280
)
244281

245-
num_matches = patterns.apply(gm.graph)
282+
with preserve_layer_types(
283+
gm,
284+
torch.ops.auto_deploy.torch_linear_simple.default,
285+
torch.ops.auto_deploy.torch_swiglu_mlp.default,
286+
):
287+
num_matches = patterns.apply(gm.graph)
246288

247289
if num_matches > 0:
248290
gm.recompile()
@@ -554,7 +596,12 @@ def _apply(
554596
dummy_args=dummy_args,
555597
)
556598

557-
num_matches = patterns.apply(gm.graph)
599+
with preserve_layer_types(
600+
gm,
601+
torch.ops.auto_deploy.torch_fake_quant_nvfp4_linear.default,
602+
torch.ops.auto_deploy.torch_nvfp4_swiglu_mlp.default,
603+
):
604+
num_matches = patterns.apply(gm.graph)
558605

559606
if num_matches > 0:
560607
gm.recompile()
@@ -835,7 +882,12 @@ def _apply(
835882
dummy_args=dummy_args,
836883
)
837884

838-
num_matches = patterns.apply(gm.graph)
885+
with preserve_layer_types(
886+
gm,
887+
torch.ops.auto_deploy.torch_fake_quant_finegrained_fp8_linear.default,
888+
torch.ops.auto_deploy.torch_finegrained_fp8_swiglu_mlp.default,
889+
):
890+
num_matches = patterns.apply(gm.graph)
839891

840892
if num_matches > 0:
841893
gm.recompile()

tests/integration/test_lists/waives.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ accuracy/test_llm_api_autodeploy.py::TestModelRegistryAccuracy::test_autodeploy_
1919
accuracy/test_llm_api_autodeploy.py::TestNemotronNanoV3::test_accuracy[nvfp4-1-trtllm] SKIP (https://nvbugs/6200112)
2020
accuracy/test_llm_api_autodeploy.py::TestNemotronUltraV3::test_accuracy[nvfp4-8] SKIP (https://nvbugs/6248757)
2121
accuracy/test_llm_api_autodeploy.py::TestQwen3_5_397B_MoE::test_bf16_small[4] SKIP (https://nvbugs/6158397)
22-
accuracy/test_llm_api_autodeploy.py::TestQwen3_5_397B_MoE::test_nvfp4[8] SKIP (https://nvbugs/6211441)
2322
accuracy/test_llm_api_pytorch.py::TestDeepSeekR1::test_fp8_blockscale[throughput_mtp_trtllm] SKIP (https://nvbugs/6191524)
2423
accuracy/test_llm_api_pytorch.py::TestDeepSeekR1::test_nvfp4_multi_gpus[throughput] SKIP (https://nvbugs/6084775)
2524
accuracy/test_llm_api_pytorch.py::TestDeepSeekR1::test_nvfp4_multi_gpus[throughput_mtp] SKIP (https://nvbugs/6029882)

0 commit comments

Comments
 (0)