Skip to content

Commit 55b8cd0

Browse files
authored
[BugFix][Model] Remove redundant SFA o_proj TP weights (vllm-project#11068)
### What this PR does / why we need it? This PR fixes the SFA v1 DSA-CP mixed `o_proj` path so the original TP shard remains the only persistent source of truth. SFA DSA-CP mixed execution already couples DSA-CP with the `o_proj` TP path. This PR keeps that behavior and documents the intended storage invariant instead of adding a new user-facing switch: - Decode-only batches exchange SFA outputs with TP all-to-all, then run the original TP-sharded `o_proj`. - Prefill/mixed batches temporarily all-gather the TP-sharded `o_proj` weight and input-sharded quantization parameters into full-weight buffers before the `o_proj` forward. - `o_proj_tp_*` tensors alias the original parameter storage instead of cloning it. - `o_proj_full_*` tensors remain temporary gather destinations for the prefill/mixed path only. - Input-sharded `o_proj` quant params are discovered by `input_dim == 1` instead of hard-coded parameter names. - The DSA-CP mixed `o_proj` data path is documented in `docs/source/developer_guide/Design_Documents/context_parallel.md`. - Load-time `.contiguous()` is kept for W4A4 MXFP4 and W8A8 MXFP8 linear transpose paths that feed NPU quant matmul. W8A8 restore now uses `reshape` instead of `view` for the restored scale shape so the contiguous transformed scale can still be restored for weight loading. The previous clone-based setup made the mixed path look like two persistent `o_proj` weights. This patch keeps the existing runtime behavior but removes the redundant persistent TP copy and records the intended storage invariant in the design doc and code comments. ### Does this PR introduce _any_ user-facing change? No. There is no new config switch or public API change. The existing SFA DSA-CP mixed behavior remains coupled with the `o_proj` TP path. ### How was this patch tested? Static/unit checks run in an A5 environment with this PR checkout: ```bash python -m pytest tests/ut/attention/test_sfa_o_proj_tp.py -q # 2 passed python -m pytest \ tests/ut/quantization/methods/test_w4a4_mxfp4.py \ tests/ut/quantization/methods/test_w8a8_mxfp8.py -q # 18 passed python -m ruff check \ vllm_ascend/attention/sfa_v1.py \ vllm_ascend/quantization/methods/w4a4_mxfp4.py \ vllm_ascend/quantization/methods/w8a8_mxfp8.py \ tests/ut/attention/test_sfa_o_proj_tp.py \ tests/ut/quantization/methods/test_w4a4_mxfp4.py \ tests/ut/quantization/methods/test_w8a8_mxfp8.py python -m ruff format --check \ vllm_ascend/attention/sfa_v1.py \ vllm_ascend/quantization/methods/w4a4_mxfp4.py \ vllm_ascend/quantization/methods/w8a8_mxfp8.py \ tests/ut/attention/test_sfa_o_proj_tp.py \ tests/ut/quantization/methods/test_w4a4_mxfp4.py \ tests/ut/quantization/methods/test_w8a8_mxfp8.py python -m py_compile vllm_ascend/quantization/methods/w8a8_mxfp8.py ``` NPU validation: | Case | Checkpoint | TP | DSA-CP | Loading model weights | Available / current KV cache | | --- | --- | ---: | --- | ---: | ---: | | W4A4 baseline | GLM-5.1-W4A4C8-mxfp4-rot | 8 | off | 52.3663 GB | 27.59 GiB / 27.59 GiB | | W4A4 DSA-CP | GLM-5.1-W4A4C8-mxfp4-rot | 8 | on | 57.6788 GB | 22.26 GiB / 22.26 GiB | The W4A4 before/after DSA-CP comparison keeps the same TP size, model length, batch-token limit, quantization mode, and memory utilization. The DSA-CP run has higher persistent weight accounting than the non-DSA baseline because DSA-CP changes the attention-side layout beyond `o_proj`; the PR removes the redundant persistent `o_proj` TP clone within that mixed path. - vLLM version: v0.23.0 - vLLM main: vllm-project/vllm@dc68bd8 Signed-off-by: maoxx241 <maomaoyu870@gmail.com>
1 parent 5e19a5a commit 55b8cd0

7 files changed

Lines changed: 187 additions & 37 deletions

File tree

docs/source/developer_guide/Design_Documents/context_parallel.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,25 @@ By predefining the maximum amount of KV cache processed per round, we sequential
121121

122122
![PCP-ChunkedPrefill](../../assets/cp/chunkedprefill.png)
123123

124+
### SFA DSA-CP Mixed `o_proj` Path
125+
126+
SFA DSA-CP mixed execution intentionally reuses the normal TP-sharded `o_proj`.
127+
This is part of the DSA-CP mixed data path, not a standalone user-facing `o_proj` TP switch.
128+
The mixed path is used when one instance may handle both decode-only and prefill/mixed batches, so `o_proj` must support two layouts at runtime:
129+
130+
- **Decode-only batches** keep the decode TP path.
131+
SFA outputs are exchanged with an all-to-all in the TP group, then the original TP-sharded `o_proj` runs normally.
132+
- **Prefill or mixed batches** produce SFA outputs that are not directly compatible with the TP-sharded `o_proj` input layout.
133+
Before `o_proj` forward, each rank all-gathers the TP-sharded `o_proj` weight and all input-sharded quantization parameters into temporary full-weight buffers.
134+
The full-weight `o_proj` forward runs once for that batch, and the module is then restored to the TP parameter aliases.
135+
136+
The storage invariant is that the original TP-sharded `o_proj` parameter remains the only persistent source of truth.
137+
`o_proj_tp_*` tensors are aliases of the original parameter storage.
138+
`o_proj_full_*` tensors are reusable communication buffers for prefill/mixed full-gather execution only.
139+
They must not become a second persistent copy of the TP weight.
140+
141+
This coupling preserves the existing decode TP behavior, supports prefill/mixed DSA-CP batches, and avoids adding an extra configuration path whose state can drift from DSA-CP mixed execution.
142+
124143
### Related Files
125144

126145
- slot_mapping computation: `vllm_ascend/worker/block_table.py`
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
# This file is a part of the vllm-ascend project.
14+
#
15+
import sys
16+
from unittest.mock import MagicMock
17+
18+
import torch
19+
20+
from tests.ut.base import TestBase
21+
22+
if "torch_npu._inductor" not in sys.modules:
23+
sys.modules["torch_npu._inductor"] = MagicMock()
24+
25+
from vllm_ascend.attention.sfa_v1 import AscendSFAImpl
26+
27+
28+
class TestAscendSFAOProjTPParams(TestBase):
29+
class _OProj(torch.nn.Module):
30+
def __init__(self):
31+
super().__init__()
32+
self.weight = torch.nn.Parameter(torch.randn(4, 3), requires_grad=False)
33+
self.aclnn_input_scale = torch.nn.Parameter(torch.randn(3), requires_grad=False)
34+
self.weight_scale_second = torch.nn.Parameter(torch.randn(4, 2), requires_grad=False)
35+
self.weight_scale_second.input_dim = 1
36+
self.weight_offset_second = torch.nn.Parameter(torch.randn(4, 2), requires_grad=False)
37+
self.weight_offset_second.input_dim = 1
38+
self.extra_input_scale = torch.nn.Parameter(torch.randn(4, 2), requires_grad=False)
39+
self.extra_input_scale.input_dim = 1
40+
self.weight_scale = torch.nn.Parameter(torch.randn(4), requires_grad=False)
41+
42+
def setUp(self):
43+
AscendSFAImpl.o_proj_full_pools.clear()
44+
45+
def _make_impl(self):
46+
impl = AscendSFAImpl.__new__(AscendSFAImpl)
47+
impl.tp_size = 2
48+
impl.o_proj = self._OProj()
49+
impl._is_o_proj_unquantized = lambda: False
50+
return impl
51+
52+
def test_o_proj_tp_params_alias_original_storage(self):
53+
impl = self._make_impl()
54+
o_proj = impl.o_proj
55+
56+
impl._init_o_proj_tp_full_params()
57+
58+
self.assertEqual(impl.o_proj_tp_weight.data_ptr(), o_proj.weight.data_ptr())
59+
self.assertEqual(
60+
impl.o_proj_tp_aclnn_input_params["aclnn_input_scale"].data_ptr(),
61+
o_proj.aclnn_input_scale.data_ptr(),
62+
)
63+
self.assertEqual(
64+
impl.o_proj_tp_input_sharded_quant_params["weight_scale_second"].data_ptr(),
65+
o_proj.weight_scale_second.data_ptr(),
66+
)
67+
self.assertEqual(
68+
impl.o_proj_tp_input_sharded_quant_params["weight_offset_second"].data_ptr(),
69+
o_proj.weight_offset_second.data_ptr(),
70+
)
71+
self.assertEqual(
72+
impl.o_proj_tp_input_sharded_quant_params["extra_input_scale"].data_ptr(),
73+
o_proj.extra_input_scale.data_ptr(),
74+
)
75+
self.assertNotIn("weight_scale", impl.o_proj_tp_input_sharded_quant_params)
76+
77+
def test_o_proj_full_weight_forward_restores_tp_storage(self):
78+
impl = self._make_impl()
79+
impl._init_o_proj_tp_full_params()
80+
original_weight_ptr = impl.o_proj.weight.data_ptr()
81+
original_scale_ptr = impl.o_proj.weight_scale_second.data_ptr()
82+
full_weight_ptr = impl.o_proj_full_pool.data_ptr()
83+
full_scale_ptr = impl.o_proj_full_input_sharded_quant_params["weight_scale_second"].data_ptr()
84+
85+
def _apply_with_full_weight(_attn_output):
86+
self.assertEqual(impl.o_proj.weight.data_ptr(), full_weight_ptr)
87+
self.assertEqual(impl.o_proj.weight_scale_second.data_ptr(), full_scale_ptr)
88+
return torch.ones(2, 4)
89+
90+
impl._apply_o_proj_full_weight = MagicMock(side_effect=_apply_with_full_weight)
91+
92+
output, require_o_proj_forward = impl._handle_o_proj_weight_switch_and_forward(
93+
attn_output=torch.randn(2, 3),
94+
output=torch.empty(2, 4),
95+
o_proj_full_handle=None,
96+
o_proj_full_param_handles=[],
97+
should_shard_weight=True,
98+
)
99+
100+
self.assertEqual(impl.o_proj.weight.data_ptr(), original_weight_ptr)
101+
self.assertEqual(impl.o_proj.weight_scale_second.data_ptr(), original_scale_ptr)
102+
self.assertFalse(require_o_proj_forward)
103+
self.assertTrue(torch.equal(output, torch.ones(2, 4)))

tests/ut/quantization/methods/test_w4a4_mxfp4.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def test_process_weights_after_loading_transposes(self):
4040
self.scheme.process_weights_after_loading(layer)
4141
self.assertEqual(layer.weight.shape, (128, 128))
4242
self.assertEqual(layer.weight_scale.shape[0], 4)
43+
self.assertTrue(layer.weight.data.is_contiguous())
44+
self.assertTrue(layer.weight_scale.data.is_contiguous())
4345

4446
@patch("vllm_ascend.quantization.methods.w4a4_mxfp4.torch_npu")
4547
def test_apply_3d_input(self, mock_npu):
@@ -102,6 +104,10 @@ def test_process_weights_transposes_weights(self):
102104
self.scheme.process_weights_after_loading(layer)
103105
self.assertEqual(layer.w13_weight.shape, (8, 64, 256))
104106
self.assertEqual(layer.w13_weight_scale.shape, (8, 2, 256, 2))
107+
self.assertFalse(layer.w13_weight.data.is_contiguous())
108+
self.assertFalse(layer.w2_weight.data.is_contiguous())
109+
self.assertFalse(layer.w13_weight_scale.data.is_contiguous())
110+
self.assertFalse(layer.w2_weight_scale.data.is_contiguous())
105111

106112
@patch("vllm_ascend.quantization.methods.w4a4_mxfp4.torch_npu")
107113
@patch("vllm_ascend.quantization.methods.w4a4_mxfp4._EXTRA_CTX")

tests/ut/quantization/methods/test_w8a8_mxfp8.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ def test_process_weights_stores_original_shapes(self):
4747
self.assertEqual(layer._mxfp8_original_shapes["weight"], (128, 256))
4848
self.assertTrue(layer._mxfp8_transformed)
4949
self.assertEqual(layer.weight_scale.shape, (4, 128, 2))
50+
self.assertTrue(layer.weight.data.is_contiguous())
51+
self.assertTrue(layer.weight_scale.data.is_contiguous())
5052

5153
def test_restore_after_process_returns_original_shape(self):
5254
layer = nn.Module()
@@ -120,6 +122,10 @@ def test_process_weights_stores_original_shapes(self):
120122
self.assertTrue(hasattr(layer, "_mxfp8_original_shapes"))
121123
self.assertIn("w13_weight", layer._mxfp8_original_shapes)
122124
self.assertEqual(layer.w13_weight.shape, (original_shape[0], original_shape[2], original_shape[1]))
125+
self.assertFalse(layer.w13_weight.data.is_contiguous())
126+
self.assertFalse(layer.w2_weight.data.is_contiguous())
127+
self.assertFalse(layer.w13_weight_scale.data.is_contiguous())
128+
self.assertFalse(layer.w2_weight_scale.data.is_contiguous())
123129

124130
def test_restore_weights_for_rl_loading(self):
125131
layer = create_mxfp_moe_layer(

vllm_ascend/attention/sfa_v1.py

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@
7272
"aclnn_input_scale_reciprocal",
7373
"aclnn_input_offset",
7474
)
75-
O_PROJ_INPUT_SHARDED_QUANT_PARAMS = ("weight_scale_second", "weight_scale")
7675

7776

7877
def _get_indexer_types(configs: tuple[Any, ...]) -> Any | None:
@@ -581,8 +580,12 @@ def __init__(
581580
# Enable layer sharding via DSA-CP on the P node in the PD-disaggregated setup.
582581
self.enable_dsa_cp_with_layer_shard = enable_dsa_cp_with_layer_shard()
583582

584-
# use original TP o_proj weight in PD mix stage, and full gather
585-
# for o_proj weight for prefill stage.
583+
# SFA DSA-CP mixed deployments keep o_proj in the existing TP layout.
584+
# Decode can use the TP-sharded o_proj directly after an activation
585+
# all-to-all, while prefill/mixed batches temporarily gather the TP
586+
# shards into a full-weight buffer because their SFA output is not
587+
# TP-sharded. This is part of the DSA-CP mixed-mode data path rather
588+
# than an independent user-facing feature switch.
586589
self.enable_dsa_cp_with_o_proj_tp = enable_dsa_cp_with_o_proj_tp()
587590

588591
if self.enable_dsa_cp:
@@ -657,7 +660,7 @@ def process_weights_after_loading(self, act_dtype: torch.dtype):
657660
for layer in self.layer_sharding_kwargs or []:
658661
if is_hidden_layer(layer):
659662
post_process_after_loading_for_shard_weight_series(layer)
660-
else:
663+
elif self.enable_dsa_cp_with_o_proj_tp:
661664
self._init_o_proj_tp_full_params()
662665

663666
if self.enable_mlapo:
@@ -861,12 +864,20 @@ def rope_single(
861864

862865
def _init_o_proj_tp_full_params(self):
863866
"""
864-
Initialize TP-mode and Full-mode parameters for o_proj weight,
865-
preparing for weight switching in PD mix stage.
866-
867-
For PD mix stage:
868-
- Use original TP o_proj weight for decode phase
869-
- Need full-gather o_proj weight from all TP ranks for prefill phase
867+
Initialize TP-mode aliases and Full-mode buffers for DSA-CP o_proj.
868+
869+
In SFA DSA-CP mixed execution, the same model instance can run both
870+
decode-only and prefill/mixed batches:
871+
- Decode-only batches all-to-all the SFA output in the TP group, then
872+
run the original TP-sharded o_proj.
873+
- Prefill/mixed batches produce SFA output that is not directly
874+
compatible with TP-sharded o_proj, so each rank all-gathers the TP
875+
o_proj shards and input-sharded quant params before running o_proj.
876+
877+
The original TP parameter storage remains the persistent source of
878+
truth. The o_proj_tp_* tensors below alias that storage, while the
879+
o_proj_full_* tensors are temporary gather destinations reused across
880+
forwards. They are not a second persistent copy of the TP weight.
870881
"""
871882
sample = self.o_proj.weight
872883
self.o_proj_full_weight_gather_dim = 1 if self._is_o_proj_unquantized() else 0
@@ -895,36 +906,41 @@ def _init_o_proj_tp_full_params(self):
895906
else:
896907
self.o_proj_full_pool = self.o_proj_full_gather_pool.transpose(0, 1)
897908

898-
# Save TP-mode parameters (original sharded weights)
899-
self.o_proj_tp_weight = self.o_proj.weight.clone().detach()
909+
# TP tensors alias the original parameter storage. The TP shard remains
910+
# the single source of truth; full-weight tensors below are temporary
911+
# gather destinations only.
912+
self.o_proj_tp_weight = self.o_proj.weight.detach()
900913
if self.o_proj_full_weight_gather_dim == 0:
901914
self.o_proj_tp_weight_gather_input = self.o_proj_tp_weight
902915
else:
916+
# Communication scratch only: all_gather_into_tensor concatenates on
917+
# dim0, while unquantized row-parallel o_proj is sharded on dim1.
903918
self.o_proj_tp_weight_gather_input = self.o_proj_tp_weight.transpose(0, 1).contiguous()
904919
self.o_proj_tp_aclnn_input_params = {}
905920
self.o_proj_full_aclnn_input_params = {}
906921
for param_name in O_PROJ_ACLNN_INPUT_PARAMS:
907922
param = getattr(self.o_proj, param_name, None)
908923
if param is None:
909924
continue
910-
self.o_proj_tp_aclnn_input_params[param_name] = param.clone().detach()
925+
self.o_proj_tp_aclnn_input_params[param_name] = param.detach()
911926
self.o_proj_full_aclnn_input_params[param_name] = param.repeat(self.tp_size)
912927

913928
self.o_proj_tp_input_sharded_quant_params = {}
914929
self.o_proj_full_input_sharded_quant_params = {}
915-
for param_name in O_PROJ_INPUT_SHARDED_QUANT_PARAMS:
916-
param = getattr(self.o_proj, param_name, None)
917-
if param is None or getattr(param, "input_dim", None) != 1:
918-
continue
919-
self.o_proj_tp_input_sharded_quant_params[param_name] = param.clone().detach()
930+
for param_name, param in self._iter_o_proj_input_sharded_quant_params():
931+
self.o_proj_tp_input_sharded_quant_params[param_name] = param.detach()
920932
self.o_proj_full_input_sharded_quant_params[param_name] = torch.empty(
921933
(param.shape[0] * self.tp_size, *param.shape[1:]), dtype=param.dtype, device=param.device
922934
)
923935

924-
# Initially switch to TP mode for graph capture
925-
self.o_proj.weight.set_(self.o_proj_tp_weight)
926-
self._switch_o_proj_params(self.o_proj_tp_aclnn_input_params)
927-
self._switch_o_proj_params(self.o_proj_tp_input_sharded_quant_params)
936+
def _iter_o_proj_input_sharded_quant_params(self):
937+
if not isinstance(self.o_proj, nn.Module):
938+
return
939+
for param_name, param in self.o_proj.named_parameters(recurse=False):
940+
if param_name == "weight" or param_name in O_PROJ_ACLNN_INPUT_PARAMS:
941+
continue
942+
if getattr(param, "input_dim", None) == 1:
943+
yield param_name, param
928944

929945
def _switch_o_proj_params(self, params: dict[str, torch.Tensor]):
930946
for param_name, param in params.items():
@@ -960,15 +976,13 @@ def _handle_o_proj_weight_switch_and_forward(
960976
if handle is not None:
961977
handle.wait()
962978

963-
# Switch o_proj to Full-mode (gathered weight from all TP ranks)
979+
# Temporarily switch o_proj to the gathered full-weight view for
980+
# prefill/mixed DSA-CP, whose attention output is not TP-sharded.
964981
self.o_proj.weight.set_(self.o_proj_full_pool)
965982
self._switch_o_proj_params(self.o_proj_full_aclnn_input_params)
966983
self._switch_o_proj_params(self.o_proj_full_input_sharded_quant_params)
967-
968-
# Apply quantization method and execute forward computation
969984
output[...] = self._apply_o_proj_full_weight(attn_output)
970-
971-
# Switch o_proj back to TP-mode for subsequent decode operations
985+
# Restore TP aliases so later decode batches keep using TP storage.
972986
self.o_proj.weight.set_(self.o_proj_tp_weight)
973987
self._switch_o_proj_params(self.o_proj_tp_aclnn_input_params)
974988
self._switch_o_proj_params(self.o_proj_tp_input_sharded_quant_params)
@@ -1312,8 +1326,8 @@ def forward(
13121326
# all-gather o_proj weight for prefill stage of PD mix node
13131327
o_proj_full_handle = None
13141328
o_proj_full_param_handles = None
1315-
# if is PD mix stage, using original TP o_proj weight, and also need to full gather for o_proj
1316-
# weight for prefill stage.
1329+
# Prefill/mixed DSA-CP computes o_proj with a temporary full weight.
1330+
# Decode keeps the original TP path and only exchanges activations.
13171331
full_gather_o_proj_enabled = self.enable_dsa_cp_with_o_proj_tp and attn_metadata.attn_state not in {
13181332
AscendAttentionState.DecodeOnly,
13191333
AscendAttentionState.SpecDecoding,
@@ -1621,9 +1635,9 @@ def forward(
16211635
)
16221636

16231637
if self.enable_dsa_cp_with_o_proj_tp:
1624-
# When using SFA-CP with pd mixed, o_proj has two cases:
1625-
# 1. prefill: o_proj is a TP weight, we need to all-gather o_proj weight to switch TP=1.
1626-
# 2. decode: all-to-all the hidden_state before the o_proj forward.
1638+
# SFA DSA-CP mixed mode keeps o_proj weight sharded in the TP domain:
1639+
# 1. prefill/mixed: gather TP shards into a temporary full weight.
1640+
# 2. decode-only: all-to-all hidden states, then run TP o_proj.
16271641
result, require_o_proj_forward = self._handle_o_proj_weight_switch_and_forward(
16281642
attn_output=attn_output,
16291643
output=output,

vllm_ascend/quantization/methods/w4a4_mxfp4.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ def process_weights_after_loading(self, layer):
111111

112112
n_dim, k_dim = layer.weight_scale.data.shape
113113
layer.weight_scale.data = layer.weight_scale.data.reshape(n_dim, k_dim // 2, 2)
114-
layer.weight.data = layer.weight.data.transpose(0, 1)
115-
layer.weight_scale.data = layer.weight_scale.data.transpose(0, 1)
114+
layer.weight.data = layer.weight.data.transpose(0, 1).contiguous()
115+
layer.weight_scale.data = layer.weight_scale.data.transpose(0, 1).contiguous()
116116

117117

118118
@register_scheme("W4A4_MXFP4", "moe")
@@ -252,6 +252,8 @@ def process_weights_after_loading(self, layer):
252252
layer.w13_weight_scale.data = layer.w13_weight_scale.data.reshape(g_num, n_size, k_size // 2, 2)
253253
g_num, n_size, k_size = layer.w2_weight_scale.shape
254254
layer.w2_weight_scale.data = layer.w2_weight_scale.data.reshape(g_num, n_size, k_size // 2, 2)
255+
# The A5 MXFP4 fused grouped-matmul-swiglu op relies on the
256+
# transpose stride to interpret packed FP4 weights as logical K.
255257
layer.w13_weight.data = layer.w13_weight.data.transpose(1, 2)
256258
layer.w2_weight.data = layer.w2_weight.data.transpose(1, 2)
257259
layer.w13_weight_scale.data = layer.w13_weight_scale.data.transpose(1, 2)

vllm_ascend/quantization/methods/w8a8_mxfp8.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ def process_weights_after_loading(self, layer):
138138
layer.weight_scale.data = layer.weight_scale.data.reshape(n_dim, k_dim // 2 + 1, 2)
139139
else:
140140
layer.weight_scale.data = layer.weight_scale.data.reshape(n_dim, k_dim // 2, 2)
141-
layer.weight.data = layer.weight.data.transpose(0, 1)
142-
layer.weight_scale.data = layer.weight_scale.data.transpose(0, 1)
141+
layer.weight.data = layer.weight.data.transpose(0, 1).contiguous()
142+
layer.weight_scale.data = layer.weight_scale.data.transpose(0, 1).contiguous()
143143

144144
# Mark as transformed
145145
layer._mxfp8_transformed = True
@@ -184,7 +184,7 @@ def restore_weights_for_rl_loading(self, layer):
184184
# Current shape: (k_dim//2, n_dim, 2)
185185
# Target shape: (n_dim, k_dim)
186186
target_scale = layer.weight_scale.data.transpose(0, 1).reshape(orig_scale_shape).contiguous()
187-
layer.weight_scale.data = layer.weight_scale.data.transpose(0, 1).view(orig_scale_shape)
187+
layer.weight_scale.data = layer.weight_scale.data.transpose(0, 1).reshape(orig_scale_shape)
188188
layer.weight_scale.data.copy_(target_scale)
189189

190190
# Mark as not transformed (ready for weight loading)

0 commit comments

Comments
 (0)