2026-05-08 17:02:07 +00:00
# DeepSeek V4 Pro → NVFP4 Quantization
2026-05-06 23:47:07 +00:00
2026-05-09 06:07:22 +00:00
Full NVFP4 quantization of DeepSeek V4 Pro on a single B200 node (8× B200, 2.7TB RAM, 13TB NVMe). Target: ~600GB.
2026-05-07 00:11:31 +00:00
2026-05-09 06:44:17 +00:00
**Cost:** ~$161/run at $23/hr (7 hours each). Don't waste runs.
2026-05-08 17:17:48 +00:00
## Pipeline
2026-05-07 00:11:31 +00:00
2026-05-08 17:17:48 +00:00
### Step 1: Dequantize FP8 → BF16
2026-05-06 23:47:07 +00:00
2026-05-08 17:17:48 +00:00
```bash
python3 scripts/dequant_fp8_to_bf16.py /root/nvidia-meeting/DeepSeek-V4-Pro-FP8 /root/nvidia-meeting/DeepSeek-V4-Pro-BF16
```
2026-05-08 17:02:07 +00:00
2026-05-08 17:17:48 +00:00
The original V4 weights use mixed precision (FP8 attention + FP4/E2M1 experts with per-tensor scales). We dequantize everything to pure BF16 so modelopt can run calibration without hitting broken FP8 kernel paths on Blackwell (DeepGEMM unsupported, Triton finegrained FP8 matmul shape mismatches).
2026-05-06 23:47:07 +00:00
2026-05-08 17:17:48 +00:00
This is not a blind upcast — it applies the actual scale factors:
2026-05-08 17:02:07 +00:00
2026-05-08 17:17:48 +00:00
```
W_bf16 = dequantize_fp4_weight(W_int, S) # per-tensor scale dequant, not .to(bfloat16)
```
2026-05-08 17:02:07 +00:00
2026-05-09 06:07:22 +00:00
**Byte-exact verified** — matmul diff is 0.000000 against the official inference path.
2026-05-08 17:17:48 +00:00
2026-05-09 06:07:22 +00:00
### Step 2: Run NVFP4 Quantization
2026-05-08 17:17:48 +00:00
2026-05-09 06:07:22 +00:00
```bash
2026-05-09 06:44:17 +00:00
cd /root/nvidia-meeting/modelopt-repo/examples/llm_ptq
python3 /root/nvidia-meeting/deepseek-v4-quant/scripts/quantize_nvfp4.py
2026-05-08 17:17:48 +00:00
```
2026-05-09 06:44:17 +00:00
Must run from the modelopt example directory (relative imports).
2026-05-09 06:07:22 +00:00
2026-05-09 06:44:17 +00:00
Pipeline steps:
2026-05-09 08:10:04 +00:00
1. **Load ** BF16 model via modelopt's `get_model()` (handles memory limits properly for 3TB model)
2. **Patch ** modelopt at runtime (load_calib_amax forces CPU, export_amax CPU fallback, graceful clamp)
2026-05-09 06:07:22 +00:00
3. **Quantize + Calibrate ** (5-6 hours, 128 samples)
2026-05-09 06:44:17 +00:00
4. **Snapshot amax to CPU ** — copies all quantizer state to CPU and saves to disk (~50MB)
5. **Save model state ** — full state dict to disk (insurance against export crashes)
6. **Export ** to HF safetensors
2026-05-08 17:17:48 +00:00
2026-05-09 06:44:17 +00:00
If the export crashes:
2026-05-08 17:17:48 +00:00
```bash
2026-05-09 06:44:17 +00:00
python3 /root/nvidia-meeting/deepseek-v4-quant/scripts/quantize_nvfp4.py --export-only
2026-05-08 17:17:48 +00:00
```
2026-05-09 06:44:17 +00:00
To validate saved state without running anything:
```bash
python3 /root/nvidia-meeting/deepseek-v4-quant/scripts/quantize_nvfp4.py --validate-only
```
2026-05-08 17:17:48 +00:00
2026-05-09 06:44:17 +00:00
**Config:** `nvfp4` , 128 calib samples, calib_seq=512, kv_fp8_cast, gpu_mem_pct=0.7
2026-05-08 17:17:48 +00:00
2026-05-09 06:07:22 +00:00
**Calibration datasets:** `abisee/cnn_dailymail` + `nvidia/Nemotron-Post-Training-Dataset-v2` (gated — requires HF token).
2026-05-08 17:17:48 +00:00
2026-05-09 06:07:22 +00:00
**Runtime:** Model loading ~53 min. Calibration ~5.5 hours. Export ~30-60 min. Total 7-8 hours.
2026-05-09 06:44:17 +00:00
## Run History (forward progression)
2026-05-09 06:47:26 +00:00
| Run | Date | Commit | Calib | Result | Root Cause | Fix |
2026-05-09 06:44:17 +00:00
|-----|------|--------|-------|--------|------------|-----|
2026-05-09 06:47:26 +00:00
| 1 | May 7 | shell wrapper (pre-repo) | 256 | ❌ Crashed at batch probing | `o_b_proj` shape mismatch — finegrained_fp8 wraps MLA projections incorrectly with FP8 source | Use BF16 source (dequantized) |
| 2 | May 8-9 | shell wrapper (pre-repo) | 128 | ❌ Crashed at export (128/128 calib ✅) | `get_activation_scaling_factor` reads stale GPU amax → CUDA illegal memory access | Snapshot amax to CPU immediately after calibration |
2026-05-09 08:10:04 +00:00
| 3 | May 9 06:10 | `3907838` | 128 | ❌ Crashed at model loading | `AutoModelForCausalLM.from_pretrained` OOM during expert weight `torch.cat` (31.5GB alloc, 25.9GB free) | Use modelopt `get_model()` which handles max_memory properly |
| 4 | May 9 ~08:05 | `f9bbef8` | 128 | 🔄 Running | — | — |
2026-05-09 06:47:26 +00:00
2026-05-09 08:10:04 +00:00
**If Run 4 succeeds**, current code is good. No further changes needed.
**If Run 4 fails**, check the log, identify the crash point, add it to this table.
2026-05-09 06:44:17 +00:00
**Key lesson from Run 2:** The `use_seq_device_map` mode shuffles layers through GPU for calibration. Quantizer amax tensors sit in VRAM for 5+ hours while CUDA's allocator churns memory. By export time, the GPU tensor metadata is valid but the underlying memory has been recycled — reading it triggers `cudaErrorIllegalAddress` . The fix is to copy amax to CPU * immediately * after calibration, before any further GPU operations.
2026-05-09 08:10:04 +00:00
**Key lesson from Run 3:** Don't use `AutoModelForCausalLM.from_pretrained` directly for models with large expert weights. The weight conversion step does `torch.cat` on GPU which can OOM. Use modelopt's `get_model()` which sets `max_memory` per GPU before loading.
2026-05-09 06:44:17 +00:00
**Do NOT repeat these mistakes:**
- Don't use FP8 source model — kernel issues on Blackwell (Run 1)
- Don't use `--low_memory_mode` with V4 — meta device errors
- Don't use calib_size=256 — OOMs with 3TB BF16 on CPU offload
2026-05-09 08:10:04 +00:00
- Don't use `AutoModelForCausalLM.from_pretrained` directly — OOM during expert weight concat (Run 3)
2026-05-09 06:44:17 +00:00
- Don't assume GPU tensor integrity after 5+ hours of sequential calibration
2026-05-09 08:10:04 +00:00
## Runtime Patches Applied by quantize_nvfp4.py
These are monkey-patches applied at runtime — no modelopt source files are modified.
1. * * `load_calib_amax` ** — After calibration writes `_amax` to GPU, immediately moves it to CPU. Prevents stale GPU tensors.
2. * * `export_amax` ** — If any `_amax` is still on GPU at export time, moves to CPU before reading. Safety net.
3. * * `get_activation_scaling_factor` ** — Moves amax to CPU, clamps bad values instead of hard assert. Prevents crash on garbage from GPU corruption.
4. * * `snapshot_amax_to_cpu()` ** — After calibration, walks all quantizers, copies `_amax` to CPU, saves to disk (~50MB). Insurance policy.
5. * * `force_all_amax_to_cpu()` ** — Moves `_pre_quant_scale` , `_global_amax` to CPU too. Nuclear option.
2026-05-09 06:44:17 +00:00
## Bugs Found (V4 + modelopt 0.45.0.dev64)
2026-05-09 06:07:22 +00:00
2026-05-09 06:44:17 +00:00
1. ~~`QuantDeepseekV4Experts` AttributeError~~ — **Already fixed ** in modelopt 0.45.0.dev64 (handles `nn.ModuleList` quantizers natively).
2026-05-09 06:07:22 +00:00
2. `--low_memory_mode` → meta device error. Don't use with V4.
3. Missing `kernels` package for FP8 ops. `pip install -U kernels` .
2026-05-09 06:44:17 +00:00
4. ~~Shell script arg names~~ — No longer relevant (in-process script).
5. **Export crash — stale GPU tensors in `export_amax()`. ** After hours of calibration, quantizer `_amax` on GPU becomes unreadable. Fixed by patching `export_amax` to move `_amax` to CPU before reading.
6. **Export crash — `assert torch.all(activation_scaling_factor > 0)`. ** Amax values from stale GPU reads are garbage (zeros, negatives, NaN). Fixed by clamping instead of asserting, plus snapshotting valid amax to CPU before corruption can occur.
2026-05-09 08:10:04 +00:00
7. **Model loading OOM during expert weight conversion. ** `AutoModelForCausalLM.from_pretrained` does `torch.cat` on GPU for expert `gate_up_proj` (31.5GB alloc), but only 25.9GB free with `device_map="sequential"` . Fixed by using modelopt's `get_model()` which sets `max_memory` per GPU before loading.
2026-05-08 17:02:07 +00:00
2026-05-08 17:23:10 +00:00
## Dependencies (pinned versions)
- **nvidia-modelopt:** `0.45.0.dev64+g579fc6c31` (installed from git, not PyPI)
- **transformers:** `5.8.0.dev0` (from git, required for DeepSeekV4 support)
- **kernels:** latest (`pip install -U kernels` — needed for finegrained FP8 ops)
- **Python:** 3.10
2026-05-09 06:44:17 +00:00
The patches in `quantize_nvfp4.py` are for **modelopt 0.45.0.dev64 ** specifically. Later versions may include fixes natively — check before applying.
2026-05-08 17:23:10 +00:00
2026-05-08 17:02:07 +00:00
## Key Notes
2026-05-09 06:07:22 +00:00
- V4 is NOT BF16 — it ships as mixed-precision FP8/FP4. You MUST dequantize to BF16 first (Step 1).
- `--low_memory_mode` causes meta device errors with V4 — don't use.
- modelopt has no explicit V4 support — relies on auto-detection of fused experts.
- The calibration state save (`v4_nvfp4_calibrated_state.pt` ) is ~1.5TB. It lives on NVMe, not in git.
2026-05-09 06:44:17 +00:00
- The amax snapshot (`v4_nvfp4_amax_snapshots.pt` ) is ~50MB. Small, critical, cheap insurance.
2026-05-09 08:10:04 +00:00
- Only divergence from modelopt example path: `get_model()` instead of `AutoModelForCausalLM.from_pretrained` (avoids OOM). Everything else uses the same `hf_ptq` functions.