Handle wo_a as bfloat16 (unquantized in NVFP4 checkpoint)
o_a_proj is NOT quantized by modelopt in the checkpoint (bfloat16), but the attention forward pass expects FP8 (weight + weight_scale_inv). - Create wo_a with quant_config=None to load bfloat16 weights - Add FP8 quantization of wo_a in finalize_mega_moe_weights: per-tensor symmetric quantization to float8_e4m3fn + weight_scale_inv - This matches what the fused_inv_rope_fp8_quant + einsum expects
This commit is contained in:
@@ -989,11 +989,15 @@ class DeepseekV4Attention(nn.Module):
|
||||
)
|
||||
|
||||
self.kv_norm = RMSNorm(self.head_dim, self.eps)
|
||||
# wo_a is NOT quantized in the NVFP4 checkpoint (modelopt left it as bfloat16),
|
||||
# but the attention forward pass expects FP8 (weight + weight_scale_inv).
|
||||
# Pass quant_config=None to load bfloat16, then process_weights_after_loading
|
||||
# will handle the FP8 quantization.
|
||||
self.wo_a = ColumnParallelLinear(
|
||||
self.n_heads * self.head_dim // self.n_groups,
|
||||
self.n_groups * self.o_lora_rank,
|
||||
bias=False,
|
||||
quant_config=quant_config,
|
||||
quant_config=None,
|
||||
return_bias=False,
|
||||
prefix=f"{prefix}.wo_a",
|
||||
)
|
||||
@@ -1624,6 +1628,34 @@ class DeepseekV4Model(nn.Module):
|
||||
def finalize_mega_moe_weights(self) -> None:
|
||||
for layer in islice(self.layers, self.start_layer, self.end_layer):
|
||||
layer.ffn.finalize_mega_moe_weights()
|
||||
# Quantize wo_a to FP8 (checkpoint has bfloat16, forward expects FP8)
|
||||
attn = layer.self_attn
|
||||
if hasattr(attn, 'wo_a') and attn.wo_a.weight.dtype == torch.bfloat16:
|
||||
self._quantize_wo_a_to_fp8(attn.wo_a)
|
||||
|
||||
@staticmethod
|
||||
def _quantize_wo_a_to_fp8(wo_a: ColumnParallelLinear) -> None:
|
||||
"""Quantize wo_a weight from bfloat16 to float8_e4m3fn.
|
||||
|
||||
The attention forward pass (fused_inv_rope_fp8_quant + einsum)
|
||||
expects wo_a.weight as FP8 and wo_a.weight_scale_inv as float32.
|
||||
The NVFP4 checkpoint stores wo_a as bfloat16, so we quantize here.
|
||||
Uses per-tensor symmetric quantization (same as modelopt FP8).
|
||||
"""
|
||||
weight_bf16 = wo_a.weight.data
|
||||
# Per-tensor FP8 quantization: scale = amax / fp8_max
|
||||
fp8_max = torch.finfo(torch.float8_e4m3fn).max # 448.0
|
||||
amax = weight_bf16.abs().max().float()
|
||||
scale = amax / fp8_max
|
||||
# Avoid division by zero
|
||||
if scale == 0:
|
||||
scale = torch.tensor(1.0, device=scale.device)
|
||||
scale_inv = 1.0 / scale
|
||||
weight_fp8 = (weight_bf16.float() * scale).to(torch.float8_e4m3fn)
|
||||
wo_a.weight = torch.nn.Parameter(weight_fp8, requires_grad=False)
|
||||
wo_a.weight_scale_inv = torch.nn.Parameter(
|
||||
scale_inv.clone(), requires_grad=False
|
||||
)
|
||||
|
||||
|
||||
@torch.compile(backend=current_platform.simple_compile_backend)
|
||||
|
||||
Reference in New Issue
Block a user