vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
# DeepSeek V4 Pro → NVFP4 Quantization + vLLM Serving
|
2026-05-06 23:47:07 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
Full NVFP4 quantization of DeepSeek V4 Pro and vLLM serving on 8× NVIDIA B200 GPUs.
|
|
|
|
|
|
|
|
|
|
|
|
## Quick Status
|
|
|
|
|
|
|
|
|
|
|
|
| Component | Status |
|
|
|
|
|
|
|-----------|--------|
|
|
|
|
|
|
| NVFP4 Quantization | ✅ 881GB (Run 11), modelopt 0.45.0.dev64 |
|
|
|
|
|
|
| Weight Loading | ✅ 95 safetensors shards, all 8 TP ranks |
|
|
|
|
|
|
| NVFP4→FP8 Conversion (wo_a) | ✅ DeepGEMM block-scale format |
|
|
|
|
|
|
| NVFP4→BF16 Dequantization | ✅ 305 attn/shared, 91 compressor layers |
|
|
|
|
|
|
| Compressor Reconstruction | ✅ Separate kv_proj/gate_proj → fused_wkv_wgate |
|
|
|
|
|
|
| MoE Expert Serving | ✅ FusedMoE NVFP4 (FLASHINFER_TRTLLM backend) |
|
|
|
|
|
|
| Profile/Warmup Run | ✅ Passes |
|
|
|
|
|
|
| API Server | ✅ Running on port 8000 |
|
2026-05-11 04:28:38 +00:00
|
|
|
|
| Output Quality | 🔧 Garbled — likely remaining dequant/scale bug |
|
2026-05-11 02:01:46 +00:00
|
|
|
|
|
|
|
|
|
|
## B200 Node
|
|
|
|
|
|
|
|
|
|
|
|
- **IP**: `45.76.247.107`
|
|
|
|
|
|
- **User**: `root`
|
|
|
|
|
|
- **Password**: see `.env`
|
|
|
|
|
|
- **GPUs**: 8× NVIDIA B200 (SM100)
|
|
|
|
|
|
- **RAM**: ~2.7 TB
|
|
|
|
|
|
- **Model weights**: `/root/nvidia-meeting/DeepSeek-V4-Pro-NVFP4/`
|
|
|
|
|
|
- **BF16 reference**: `/root/nvidia-meeting/DeepSeek-V4-Pro-BF16/`
|
2026-05-07 00:11:31 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## Architecture
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
```
|
|
|
|
|
|
DeepSeek V4 Pro (1.2T params, 61 layers)
|
|
|
|
|
|
├── MLA Attention (61 layers)
|
|
|
|
|
|
│ ├── fused_wqa_wkv → BF16 (UnquantizedLinearMethod)
|
|
|
|
|
|
│ ├── wo_a → FP8 (DeepGEMM block-scale, BMM einsum)
|
|
|
|
|
|
│ ├── wo_b → BF16 (UnquantizedLinearMethod)
|
|
|
|
|
|
│ └── compressor.fused_wkv_wgate → BF16 (reconstructed from NVFP4)
|
|
|
|
|
|
├── MoE Experts (384 experts, 61 layers)
|
|
|
|
|
|
│ ├── w13_weight → NVFP4 (FusedMoE, FLASHINFER_TRTLLM backend)
|
|
|
|
|
|
│ └── w2_weight → NVFP4 (FusedMoE, FLASHINFER_TRTLLM backend)
|
|
|
|
|
|
└── Shared Expert → FP8 (Fp8LinearMethod, DeepGEMM)
|
|
|
|
|
|
```
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## The NVFP4 → vLLM Gap
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
ModelOpt quantizes to NVFP4 (4-bit FP4 with block scales). vLLM's DeepSeek V4
|
|
|
|
|
|
attention code expects FP8 with DeepGEMM block-scale einsum. These formats were
|
|
|
|
|
|
**never integrated** — we're ahead of NVIDIA on this. Key gaps we had to bridge:
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### 1. wo_a: NVFP4 → FP8 + DeepGEMM Block Scale
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Problem**: `wo_a` uses `deepseek_v4_fp8_einsum` (BMM with DeepGEMM), which expects:
|
|
|
|
|
|
- Weight: `float8_e4m3fn` in 3D shape `(g, r, d)` for batched matmul
|
|
|
|
|
|
- Scale: DeepGEMM-formatted block scale tensor (not a per-tensor scalar)
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
Our NVFP4 weights are uint8 packed FP4 with separate block/global scales.
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Solution** (`_convert_nvfp4_to_fp8`):
|
|
|
|
|
|
1. Unpack NVFP4 uint8 → BF16 using E2M1 lookup table
|
2026-05-11 04:28:38 +00:00
|
|
|
|
2. Dequantize: `weight_bf16 * block_scale * global_scale` (NO input_scale — it's for activations)
|
2026-05-11 02:01:46 +00:00
|
|
|
|
3. Re-quantize BF16 → FP8 e4m3 with per-tensor scale (`w_amax / fp8_max`)
|
|
|
|
|
|
4. Create block scale tensor filled with `fp8_scale` (same scale for every 128×128 block)
|
|
|
|
|
|
5. Call `deepgemm_post_process_fp8_weight_block(wq, ws, quant_block_shape=(128,128), use_e8m0=True, is_bmm=True, bmm_batch_size=N)`
|
|
|
|
|
|
6. Store: `weight_scale_inv = dg_ws` (DeepGEMM-formatted scale), `weight = w_fp8` (3D BMM shape)
|
2026-05-10 08:23:11 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Why `weight_scale_inv`?** The attention forward reads `self.wo_a.weight_scale_inv` as
|
|
|
|
|
|
`b_scale` for `deepseek_v4_fp8_einsum` → DeepGEMM `fp8_einsum`. This must be the
|
|
|
|
|
|
DeepGEMM block-scale tensor, not a per-tensor scalar.
|
2026-05-10 08:23:11 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Why `fp8_scale` in the block scale (not all-ones)?** DeepGEMM divides by the block
|
|
|
|
|
|
scale at runtime. If the block scale is all-ones, it divides by 1.0, producing garbage.
|
|
|
|
|
|
Each block needs the actual per-tensor scale value.
|
2026-05-10 08:23:11 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### 2. Attention Layers: NVFP4 → BF16
|
2026-05-10 08:59:28 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Problem**: `fused_wqa_wkv`, `wo_b` use standard `torch.nn.functional.linear`.
|
|
|
|
|
|
NVFP4 weights (uint8) can't be used directly.
|
2026-05-10 08:59:28 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Solution** (`_convert_nvfp4_to_bf16`):
|
|
|
|
|
|
1. Unpack NVFP4 → BF16
|
2026-05-11 04:28:38 +00:00
|
|
|
|
2. Dequantize with block/global scales (input_scale is for activations, not weights)
|
2026-05-11 02:01:46 +00:00
|
|
|
|
3. Replace `mod.weight` with BF16 parameter
|
|
|
|
|
|
4. Set `quant_method = UnquantizedLinearMethod()`
|
|
|
|
|
|
5. Remove NVFP4 scale attributes (`weight_scale`, `weight_scale_2`, `input_scale`)
|
2026-05-10 08:23:11 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### 3. Compressor: Reconstructing fused_wkv_wgate from NVFP4
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Problem**: The compressor's `fused_wkv_wgate` is a `MergedColumnParallelLinear`
|
|
|
|
|
|
with `disable_tp=True`. NVFP4 uint8 data can't be loaded into the BF16 parameter
|
|
|
|
|
|
(shape mismatch: uint8 is half the input dim). The default weight loader silently
|
|
|
|
|
|
skips these weights, leaving the parameter uninitialized.
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Solution** (`_reconstruct_compressor_weight`):
|
|
|
|
|
|
1. Read original `kv_proj.weight` and `gate_proj.weight` directly from safetensors
|
|
|
|
|
|
2. Unpack NVFP4 → BF16, dequantize with scales
|
|
|
|
|
|
3. Concatenate: `fused = cat([wkv, wgate], dim=0)`
|
|
|
|
|
|
4. Replace the uninitialized parameter
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Critical detail**: The **indexer** compressor is at a different checkpoint path:
|
|
|
|
|
|
- Main: `model.layers.N.self_attn.compressor.{kv_proj,gate_proj}.weight`
|
|
|
|
|
|
- Indexer: `model.layers.N.self_attn.compressor.indexer.{kv_proj,gate_proj}.weight`
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
Using the wrong prefix loads the main compressor weight into the indexer's
|
|
|
|
|
|
`fused_wkv_wgate`, causing a 4× shape mismatch and `split_with_sizes` crash.
|
2026-05-07 00:11:31 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### 4. MoE Experts: NVFP4 FusedMoE
|
2026-05-06 23:47:07 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Problem**: vLLM's DeepSeek V4 uses `DeepseekV4MegaMoEExperts` with DeepGEMM
|
|
|
|
|
|
grouped GEMM. NVFP4 experts need a different kernel path.
|
2026-05-08 17:02:07 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Solution**: The existing `ModelOptNvFp4LinearMethod` + `FusedMoE` infrastructure
|
|
|
|
|
|
handles NVFP4 experts natively. We just need to:
|
|
|
|
|
|
- Keep expert weights as NVFP4 uint8 + block/global scales
|
|
|
|
|
|
- Use `FLASHINFER_TRTLLM` MoE backend (auto-selected)
|
|
|
|
|
|
- Skip any conversion in `process_weights_after_loading`
|
2026-05-06 23:47:07 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### 5. BF16 wo_a Layers: BF16 → FP8
|
2026-05-08 17:02:07 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Problem**: Some `wo_a` layers were NOT quantized by modelopt (BF16 in checkpoint).
|
|
|
|
|
|
The attention forward still reads them as FP8 for the einsum path.
|
2026-05-08 17:02:07 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
**Solution** (`_convert_bf16_to_fp8`): Same as #1 but skip the NVFP4 unpack step.
|
|
|
|
|
|
Directly quantize BF16 → FP8 with block scale.
|
2026-05-08 17:17:48 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## Bugs Found and Fixed
|
2026-05-08 17:17:48 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### DeepGEMM `sf.dim()` Assertion (layout.hpp:94)
|
|
|
|
|
|
- **Root cause**: `weight_scale_inv` was a 1D per-tensor scale `(g,)`. DeepGEMM expects
|
|
|
|
|
|
2D/3D block-scale tensor formatted by `transform_sf_into_required_layout`.
|
|
|
|
|
|
- **Fix**: Use `deepgemm_post_process_fp8_weight_block` to produce correctly formatted
|
|
|
|
|
|
block scales, store result in `weight_scale_inv`.
|
2026-05-08 17:17:48 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### Block Scale dtype (`float8_e4m3fn` vs `float32`)
|
|
|
|
|
|
- **Root cause**: `deepgemm_post_process_fp8_weight_block` expects `float32` or
|
|
|
|
|
|
`float8_e8m0fnu` block scales. We initially used `float8_e4m3fn`.
|
|
|
|
|
|
- **Fix**: Create block scale as `dtype=torch.float32`.
|
2026-05-09 06:07:22 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### Missing `deepgemm_post_process` args
|
|
|
|
|
|
- **Root cause**: Function signature changed to require `quant_block_shape` and `use_e8m0`.
|
|
|
|
|
|
- **Fix**: Pass `quant_block_shape=(128, 128)` and `use_e8m0=True`.
|
2026-05-08 17:17:48 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### Compressor Indexer Shape Mismatch
|
|
|
|
|
|
- **Root cause**: `_reconstruct_compressor_weight` used the same checkpoint prefix
|
|
|
|
|
|
for both main and indexer compressors. The indexer's keys have `.indexer.` in the path.
|
|
|
|
|
|
- **Fix**: Add `sub_path` parameter; pass `".indexer"` for indexer compressors.
|
2026-05-08 17:17:48 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
### All-Ones Block Scale → Garbage Output
|
|
|
|
|
|
- **Root cause**: Block scale was `torch.ones(...)` (scale=1.0). DeepGEMM divides by
|
|
|
|
|
|
the block scale at runtime, so the output was divided by 1.0 instead of the actual
|
|
|
|
|
|
per-tensor scale, producing incoherent text.
|
|
|
|
|
|
- **Fix**: Use `torch.full(..., fp8_scale.item())` to fill the block scale with the
|
|
|
|
|
|
correct per-tensor FP8 quantization scale.
|
2026-05-08 17:17:48 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## Running
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-11 02:01:46 +00:00
|
|
|
|
# On B200 node
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
cd /root/nvidia-meeting
|
|
|
|
|
|
docker compose up -d
|
|
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
# Check logs
|
|
|
|
|
|
docker logs -f nvidia-meeting-vllm-1
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
# Test
|
|
|
|
|
|
curl http://localhost:8000/v1/models
|
|
|
|
|
|
curl http://localhost:8000/v1/chat/completions \
|
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
|
-d '{"model": "/model", "messages": [{"role": "user", "content": "Hello"}], "max_tokens": 50}'
|
vLLM serving: patched deepseek_v4.py, disabled mega_moe, updated docs
- Add patches/deepseek_v4.py: patched vllm source file with modelopt NVFP4
weight name mappings (expert gate_proj→w1, mlp→ffn, self_attn→attn.mla_attn,
compressor.kv_proj→wkv, etc.), E2M1 FP4→BF16 unpacking for stacked params,
skip patterns for NVFP4 scale tensors on MergedColumnParallelLinear, and
resilient loading for unknown params.
- Update docker-compose.yml: copy patched deepseek_v4.py over original at
container startup, remove --moe-backend=deep_gemm_mega_moe (no NVFP4 kernel).
- Update patches/patch_vllm_weights.py: legacy runtime monkey-patch approach
(doesn't work with worker processes), kept for reference.
- Update README.md: added vLLM serving run history table (S1-S10), documented
all open issues (MergedColumnParallelLinear+NVFP4, no mega_moe kernel,
resilient loading), added vLLM-specific bug list and key notes.
- Update scripts/serve_vllm.py: add WARN comment on mega_moe flag.
2026-05-10 16:14:17 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## Files
|
2026-05-09 08:10:04 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
| File | Purpose |
|
|
|
|
|
|
|------|---------|
|
|
|
|
|
|
| `patches/deepseek_v4.py` | Main patch: NVFP4 post-load conversion, weight reconstruction, DeepGEMM block-scale |
|
|
|
|
|
|
| `patches/modelopt.py` | ModelOpt FP4 config patches for weight loading |
|
|
|
|
|
|
| `.env` | B200 node credentials |
|
|
|
|
|
|
| `docker-compose.yml` | Container config (8 GPU, TP=8, EP=8, NVFP4 quant) |
|
2026-05-09 23:00:17 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## Conversion Flow
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
```
|
|
|
|
|
|
Checkpoint (NVFP4 safetensors)
|
|
|
|
|
|
│
|
|
|
|
|
|
├── [weight loader] ──→ vLLM model (NVFP4 uint8 params)
|
|
|
|
|
|
│
|
|
|
|
|
|
└── [process_weights_after_loading]
|
|
|
|
|
|
├── wo_a (is_bmm=True):
|
|
|
|
|
|
│ NVFP4→BF16→FP8 + DeepGEMM block scale
|
|
|
|
|
|
│ weight_scale_inv = dg_ws, weight = 3D FP8
|
|
|
|
|
|
│
|
|
|
|
|
|
├── fused_wqa_wkv, wo_b, shared_expert:
|
|
|
|
|
|
│ NVFP4→BF16, UnquantizedLinearMethod
|
|
|
|
|
|
│
|
|
|
|
|
|
├── compressor.fused_wkv_wgate:
|
|
|
|
|
|
│ Read kv_proj+gate_proj from checkpoint
|
|
|
|
|
|
│ NVFP4→BF16, cat into fused weight
|
|
|
|
|
|
│
|
|
|
|
|
|
└── MoE experts: stay NVFP4 (FusedMoE backend)
|
|
|
|
|
|
```
|
2026-05-08 17:23:10 +00:00
|
|
|
|
|
2026-05-11 04:28:38 +00:00
|
|
|
|
## Bugs Found and Fixed (continued)
|
|
|
|
|
|
|
|
|
|
|
|
### `input_scale` Multiplied into Weight Dequantization (CRITICAL)
|
|
|
|
|
|
- **Root cause**: `_convert_nvfp4_to_bf16`, `_convert_nvfp4_to_fp8`, and
|
|
|
|
|
|
`_reconstruct_compressor_weight` all multiplied by `input_scale` during weight
|
|
|
|
|
|
dequantization. `input_scale` is for **activations**, not weights. The correct
|
|
|
|
|
|
formula is: `weight_bf16 = e2m1 * block_scale * global_scale` (NO input_scale).
|
|
|
|
|
|
Including it made weights ~5000× too small, causing garbage output.
|
|
|
|
|
|
- **Fix**: Removed `* input_scale` from all three dequant paths.
|
|
|
|
|
|
|
|
|
|
|
|
### `fused_skip_regex` Skipping Non-Fused Layer Scales (CRITICAL)
|
|
|
|
|
|
- **Root cause**: The skip list included `q_b_proj`, `o_a_proj`, `o_b_proj` weight
|
|
|
|
|
|
scales. These are **NOT fused/stacked** — they're individual Linear layers
|
|
|
|
|
|
(`wq_b`, `wo_a`, `wo_b`) converted in-place. Skipping their scales caused
|
|
|
|
|
|
`process_weights_after_loading` to read `torch.empty()` garbage for
|
|
|
|
|
|
`weight_scale_inv`, producing garbled output.
|
|
|
|
|
|
- **Fix**: Removed `q_b_proj`, `o_a_proj`, `o_b_proj` scale entries from
|
|
|
|
|
|
`fused_skip_regex`. Only truly stacked params remain skipped:
|
|
|
|
|
|
`compressor.{kv_proj,gate_proj}` → `fused_wkv_wgate`,
|
|
|
|
|
|
`self_attn.{kv_proj,q_a_proj}` → `fused_wqa_wkv`,
|
|
|
|
|
|
`shared_experts.{gate_proj,up_proj}` → `gate_up_proj`.
|
|
|
|
|
|
|
|
|
|
|
|
## Version Banner
|
|
|
|
|
|
|
|
|
|
|
|
The patch prints a version banner at import time (visible in `docker logs`):
|
|
|
|
|
|
```
|
|
|
|
|
|
======================================================================
|
|
|
|
|
|
DeepSeek V4 NVFP4 Patch
|
|
|
|
|
|
Commit: 26aaaba
|
|
|
|
|
|
Loaded: 2026-05-11 04:25:00 UTC
|
|
|
|
|
|
Node: ...
|
|
|
|
|
|
|
|
|
|
|
|
Architecture: ...
|
|
|
|
|
|
Bugs fixed: #1-#6
|
|
|
|
|
|
======================================================================
|
|
|
|
|
|
```
|
|
|
|
|
|
This ensures you can always verify what's running inside the container.
|
|
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## Known Issues
|
2026-05-08 17:02:07 +00:00
|
|
|
|
|
2026-05-11 04:28:38 +00:00
|
|
|
|
1. **Output quality**: Model produces tokens but they're garbled/incoherent.
|
|
|
|
|
|
All 6 known bugs are fixed. The remaining issue is under investigation —
|
|
|
|
|
|
likely a subtle dequantization bug (sign handling, scale ordering, or
|
|
|
|
|
|
E2M1 unpack edge case). The version banner in the logs helps debug which
|
|
|
|
|
|
patch version is active.
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
2. **Runtime performance**: Not yet benchmarked. The DeepGEMM einsum + FusedMoE
|
|
|
|
|
|
path should be efficient on B200, but the BF16 layers go through
|
|
|
|
|
|
`UnquantizedLinearMethod` which may be slower than dedicated kernels.
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
## Quantization Details
|
2026-05-09 16:09:09 +00:00
|
|
|
|
|
2026-05-11 02:01:46 +00:00
|
|
|
|
- **Model**: DeepSeek V4 Pro (1.2T parameters)
|
|
|
|
|
|
- **Format**: NVIDIA NVFP4 (4-bit floating point with 128-element block scales)
|
|
|
|
|
|
- **Tool**: modelopt 0.45.0.dev64 + transformers 5.8.0.dev0
|
|
|
|
|
|
- **Run**: Run 11 (881GB), 8× B200, ~$161/run
|
|
|
|
|
|
- **Checkpoint**: 95 safetensors shards
|