Consolidate serving into kernel repo
- Dockerfile: COPY kernel source instead of git clone - docker-compose: build context at repo root, all debug flags OFF - vLLM patches: deepseek_v4.py, staging_kernel.py, deepseek_v4_attention.py - serve_vllm.py script - .dockerignore to keep image clean
This commit is contained in:
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
||||
docker/
|
||||
scripts/
|
||||
*.egg-info/
|
||||
.git/
|
||||
.gitignore
|
||||
README.md
|
||||
53
docker/Dockerfile
Normal file
53
docker/Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
||||
# DeepSeek V4 NVFP4 vLLM + CUTLASS NVFP4 Mega MoE Kernel
|
||||
FROM vllm/vllm-openai:nightly-x86_64
|
||||
|
||||
# Remove broken nixl_ep (built against CUDA 12, image is CUDA 13)
|
||||
RUN pip uninstall -y nixl-ep; rm -rf /usr/local/lib/python3.12/dist-packages/nixl_ep
|
||||
|
||||
RUN apt-get update && apt-get install -y git screen cmake libcusolver-dev-13-0 libcusparse-dev-13-0 libcublas-dev-13-0 libcurand-dev-13-0 libcufft-dev-13-0 libnvjitlink-dev-13-0 && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Remove the broken symlink if it exists
|
||||
RUN rm -f /usr/local/cuda/lib64/libcudart.so.12
|
||||
|
||||
ENV CUDA_HOME=/usr/local/cuda
|
||||
ENV TORCH_CUDA_ARCH_LIST="10.0"
|
||||
|
||||
# Clone latest CUTLASS (has NVFP4 block-scaled MMA support)
|
||||
ARG CUTLASS_CACHE_BUSTER=1
|
||||
RUN git clone --depth 1 https://github.com/NVIDIA/cutlass.git /root/cutlass
|
||||
|
||||
# Copy and install the NVFP4 mega_moe kernel (from this repo)
|
||||
COPY src/ /root/nvfp4-megamoe-kernel/src/
|
||||
COPY pyproject.toml /root/nvfp4-megamoe-kernel/pyproject.toml
|
||||
RUN cd /root/nvfp4-megamoe-kernel && pip install -e .
|
||||
|
||||
# Build the CUTLASS NVFP4 block-scaled GEMM extension
|
||||
RUN cd /root/nvfp4-megamoe-kernel/src/nvfp4_megamoe_kernel/cutlass_nvfp4_gemm && \
|
||||
mkdir -p cutlass_nvfp4_gemm && \
|
||||
CUTLASS_INCLUDE_DIR=/root/cutlass/include \
|
||||
TORCH_CUDA_ARCH_LIST=10.0 \
|
||||
python3 setup.py build_ext --inplace
|
||||
|
||||
# Install TileLang (for potential future use)
|
||||
RUN pip install tilelang
|
||||
|
||||
ENV PYTHONPATH="/root/nvfp4-megamoe-kernel/src/nvfp4_megamoe_kernel/cutlass_nvfp4_gemm:/root/nvfp4-megamoe-kernel:${PYTHONPATH}"
|
||||
|
||||
# Copy vLLM patches
|
||||
ARG PATCH_CACHE_BUSTER=82
|
||||
COPY patches/deepseek_v4.py /tmp/patches/deepseek_v4.py
|
||||
COPY patches/staging_kernel.py /tmp/patches/staging_kernel.py
|
||||
COPY patches/deepseek_v4_attention.py /tmp/patches/deepseek_v4_attention.py
|
||||
|
||||
# Apply patches
|
||||
RUN VLLM_MODELS_DIR=$(python3 -c "import vllm.model_executor.models; import os; print(os.path.dirname(vllm.model_executor.models.__file__))") && \
|
||||
VLLM_LAYERS_DIR=$(python3 -c "import vllm.model_executor.layers; import os; print(os.path.dirname(vllm.model_executor.layers.__file__))") && \
|
||||
cp /tmp/patches/deepseek_v4.py "$VLLM_MODELS_DIR/deepseek_v4.py" && \
|
||||
cp /tmp/patches/staging_kernel.py "$VLLM_MODELS_DIR/staging_kernel.py" && \
|
||||
cp /tmp/patches/deepseek_v4_attention.py "$VLLM_LAYERS_DIR/deepseek_v4_attention.py" && \
|
||||
rm -rf /tmp/patches
|
||||
|
||||
# Verify
|
||||
RUN python3 -c "import torch; import cutlass_nvfp4_gemm._C; print('CUTLASS NVFP4 OK')" && \
|
||||
python3 -c "import vllm; print('vLLM OK')" && \
|
||||
python3 -c "import nvfp4_megamoe_kernel; print('NVFP4 kernel OK')"
|
||||
31
docker/docker-compose.yml
Normal file
31
docker/docker-compose.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
services:
|
||||
vllm:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- OMP_NUM_THREADS=128
|
||||
- MEGA_MOE_DEBUG=0
|
||||
- MEGA_MOE_STATIC=0
|
||||
- MEGA_MOE_USE_CUTLASS=1
|
||||
- DG_JIT_DEBUG=0
|
||||
command:
|
||||
- /model
|
||||
- --trust-remote-code
|
||||
- --enable-expert-parallel
|
||||
- --tensor-parallel-size=8
|
||||
- --enforce-eager
|
||||
- --tokenizer-mode=deepseek_v4
|
||||
- --host=0.0.0.0
|
||||
- --port=8000
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
volumes:
|
||||
- /root/nvidia-meeting/DeepSeek-V4-Pro-NVFP4:/model:ro
|
||||
2416
docker/patches/deepseek_v4.py
Normal file
2416
docker/patches/deepseek_v4.py
Normal file
File diff suppressed because it is too large
Load Diff
1155
docker/patches/deepseek_v4_attention.py
Normal file
1155
docker/patches/deepseek_v4_attention.py
Normal file
File diff suppressed because it is too large
Load Diff
270
docker/patches/staging_kernel.py
Normal file
270
docker/patches/staging_kernel.py
Normal file
@@ -0,0 +1,270 @@
|
||||
"""
|
||||
NVFP4 staging kernel — full FP4 (E2M1) activations + UE4M3 block16 scales.
|
||||
|
||||
The mxf4nvf4 PTX instruction requires BOTH A and B to be FP4 (E2M1 packed).
|
||||
This kernel quantizes BF16 activations → E2M1 packed uint8 with UE4M3 scales.
|
||||
"""
|
||||
import triton
|
||||
import triton.language as tl
|
||||
import torch
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _deepseek_v4_stage_mega_moe_inputs_kernel(
|
||||
hidden_states,
|
||||
x_fp4, # uint8, shape (M, K//2) — E2M1 packed, 2 values per byte
|
||||
x_sf, # int32, shape (M, K//64) — UE4M3 packed, 4 scales per int32
|
||||
topk_ids,
|
||||
topk_weights,
|
||||
topk_idx_out,
|
||||
topk_weights_out,
|
||||
hidden_stride_m: tl.constexpr,
|
||||
hidden_stride_k: tl.constexpr,
|
||||
x_stride_m: tl.constexpr,
|
||||
x_stride_k: tl.constexpr,
|
||||
x_sf_stride_m: tl.constexpr,
|
||||
x_sf_stride_k: tl.constexpr,
|
||||
topk_ids_stride_m: tl.constexpr,
|
||||
topk_ids_stride_k: tl.constexpr,
|
||||
topk_weights_stride_m: tl.constexpr,
|
||||
topk_weights_stride_k: tl.constexpr,
|
||||
topk_idx_stride_m: tl.constexpr,
|
||||
topk_idx_stride_k: tl.constexpr,
|
||||
topk_weights_out_stride_m: tl.constexpr,
|
||||
topk_weights_out_stride_k: tl.constexpr,
|
||||
hidden_size: tl.constexpr,
|
||||
top_k: tl.constexpr,
|
||||
BLOCK_K: tl.constexpr, # 128 elements (loaded from hidden)
|
||||
GROUP_K: tl.constexpr, # 16 (NVFP4 group_size)
|
||||
BLOCK_TOPK: tl.constexpr,
|
||||
) -> None:
|
||||
token_id = tl.program_id(0)
|
||||
k_block_id = tl.program_id(1)
|
||||
|
||||
k_offsets = k_block_id * BLOCK_K + tl.arange(0, BLOCK_K)
|
||||
k_mask = k_offsets < hidden_size
|
||||
hidden = tl.load(
|
||||
hidden_states + token_id * hidden_stride_m + k_offsets * hidden_stride_k,
|
||||
mask=k_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
|
||||
num_groups: tl.constexpr = BLOCK_K // GROUP_K # 8
|
||||
hidden_groups = tl.reshape(hidden, [num_groups, GROUP_K])
|
||||
abs_groups = tl.reshape(tl.abs(hidden), [num_groups, GROUP_K])
|
||||
amax = tl.max(abs_groups, axis=1)
|
||||
amax = tl.maximum(amax, 1.0e-4)
|
||||
|
||||
# ---- UE4M3 scale computation ----
|
||||
# scale = amax / 6.0 (E2M1 max value = 6)
|
||||
# Then quantize scale to UE4M3 format
|
||||
scale = amax / 6.0
|
||||
scale_bits = scale.to(tl.uint32, bitcast=True)
|
||||
scale_exp = (scale_bits >> 23) & 0xFF
|
||||
scale_mant = scale_bits & 0x7FFFFF
|
||||
|
||||
# Convert FP32 → E4M3 manually (with subnormal support)
|
||||
# FP32 bias=127, E4M3 bias=7 → raw exp = scale_exp - 120
|
||||
e4m3_exp_raw = scale_exp - 120 # can be negative → subnormal
|
||||
|
||||
# Normal path: exp >= 1, just truncate mantissa top 3 bits
|
||||
# RNE rounding: need guard (bit 19), sticky (OR of bits 18:0), and LSB of result
|
||||
normal_mant = scale_mant >> 20
|
||||
guard_bit = (scale_mant >> 19) & 1
|
||||
sticky_bit = tl.where((scale_mant & 0x7FFFF) != 0, 1, 0) # OR of bits [18:0]
|
||||
result_lsb = normal_mant & 1
|
||||
# RNE: round up if (guard=1 and sticky=1) or (guard=1 and sticky=0 and lsb=1)
|
||||
round_up = guard_bit & (sticky_bit | result_lsb)
|
||||
normal_mant = normal_mant + round_up
|
||||
normal_exp = e4m3_exp_raw
|
||||
|
||||
# Subnormal path: exp_raw <= 0
|
||||
# Insert implicit leading 1 and right-shift by (1 - exp_raw)
|
||||
# E4M3 subnormal: value = (mant/8) * 2^(1-7) = (mant/8) * 2^-6
|
||||
# So we need: (1 + mant_fp32/2^23) * 2^(exp_raw - 7) = (shifted_mant/8) * 2^-6
|
||||
# shifted_mant = (implicit_1 | mant_fp32) >> (1 - exp_raw - 1) then take top 3 bits
|
||||
shift = 1 - e4m3_exp_raw # positive when subnormal
|
||||
mant_with_leading = (0x800000 | scale_mant) # insert implicit 1
|
||||
# Right-shift to get into the 3-bit E4M3 mantissa window
|
||||
# We want bits [shift+19 : shift+23) of mant_with_leading for 3 mantissa bits + 1 round bit
|
||||
subnormal_mant = (mant_with_leading >> (shift.to(tl.int32) + 20)) & 0x7
|
||||
sub_guard_bit = (mant_with_leading >> (shift.to(tl.int32) + 19)) & 1
|
||||
# Sticky: OR of all bits below the guard bit in the shifted result
|
||||
# shift ≤ 8 in practice (amax floor = 1e-4 → scale ≈ 2^-15 → exp_raw ≈ -7), so mask ≤ 2^27
|
||||
sub_sticky_mask = (1 << (shift.to(tl.int32) + 19)) - 1
|
||||
sub_sticky_bit = tl.where((mant_with_leading & sub_sticky_mask) != 0, 1, 0)
|
||||
sub_result_lsb = subnormal_mant & 1
|
||||
sub_round_up = sub_guard_bit & (sub_sticky_bit | sub_result_lsb)
|
||||
subnormal_mant = subnormal_mant + sub_round_up
|
||||
|
||||
is_normal = e4m3_exp_raw >= 1
|
||||
e4m3_mant = tl.where(is_normal, normal_mant, subnormal_mant)
|
||||
e4m3_exp = tl.where(is_normal, normal_exp, 0) # exp=0 for subnormals
|
||||
|
||||
# Handle mantissa overflow after rounding
|
||||
overflow = e4m3_mant >= 8
|
||||
e4m3_mant = tl.where(overflow, 0, e4m3_mant)
|
||||
e4m3_exp = tl.where(overflow, e4m3_exp + 1, e4m3_exp)
|
||||
e4m3_exp = tl.maximum(e4m3_exp, 0)
|
||||
e4m3_exp = tl.minimum(e4m3_exp, 15)
|
||||
scale_e4m3_bits = (e4m3_exp << 3) | e4m3_mant
|
||||
|
||||
# Reconstruct dequantized scale by decoding the STORED E4M3 bits.
|
||||
# This guarantees the E2M1 quantization divides by exactly the value
|
||||
# the CUDA kernel will multiply back — same bits, single decode, no
|
||||
# possibility of encode/decode disagreement.
|
||||
stored_exp = (scale_e4m3_bits >> 3) & 0xF
|
||||
stored_mant = scale_e4m3_bits & 0x7
|
||||
e4m3_exp_for_recon = tl.maximum(stored_exp.to(tl.int32) - 7, -126)
|
||||
two_pow_exp_bits = (e4m3_exp_for_recon + 127).to(tl.uint32) << 23
|
||||
two_pow_exp = two_pow_exp_bits.to(tl.float32, bitcast=True)
|
||||
normal_value = (1.0 + stored_mant.to(tl.float32) / 8.0) * two_pow_exp
|
||||
subnormal_value = (stored_mant.to(tl.float32) / 8.0) * 0.015625
|
||||
e4m3_value = tl.where(stored_exp == 0, subnormal_value, normal_value)
|
||||
|
||||
# ---- E2M1 FP4 quantization (unpacked, 1 byte/element) ----
|
||||
# E2M1 LUT (unsigned): [0, 0.5, 1, 1.5, 2, 3, 4, 6]
|
||||
# Nearest-neighbor using thresholds (midpoints between consecutive values)
|
||||
scaled = hidden_groups * (1.0 / tl.maximum(e4m3_value, 1e-6))[:, None]
|
||||
# Clamp to E2M1 range [-6, 6]
|
||||
scaled = tl.maximum(scaled, -6.0)
|
||||
scaled = tl.minimum(scaled, 6.0)
|
||||
|
||||
abs_s = tl.abs(scaled)
|
||||
# Thresholds: midpoints between [0, 0.5, 1, 1.5, 2, 3, 4, 6]
|
||||
# [0, 0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5.0, INF]
|
||||
e2m1_idx = tl.where(abs_s < 0.25, 0,
|
||||
tl.where(abs_s < 0.75, 1,
|
||||
tl.where(abs_s < 1.25, 2,
|
||||
tl.where(abs_s < 1.75, 3,
|
||||
tl.where(abs_s < 2.5, 4,
|
||||
tl.where(abs_s < 3.5, 5,
|
||||
tl.where(abs_s < 5.0, 6, 7)))))))
|
||||
sign_bit = (scaled < 0).to(tl.int32)
|
||||
e2m1_4bit = (sign_bit << 3) | e2m1_idx # 4-bit: (sign << 3) | index
|
||||
|
||||
# Pack E2M1 pairs into single bytes (2 per byte, low nibble first)
|
||||
# mxf4nvf4 reads FP4 packed from SMEM — must match kernel's TMA layout
|
||||
e2m1_flat = tl.reshape(e2m1_4bit, [BLOCK_K])
|
||||
e2m1_lo = e2m1_flat[0::2] # even indices → low nibble
|
||||
e2m1_hi = e2m1_flat[1::2] # odd indices → high nibble
|
||||
e2m1_packed = (e2m1_hi << 4 | e2m1_lo).to(tl.uint8) # [BLOCK_K // 2]
|
||||
|
||||
k_offsets_out = k_block_id * (BLOCK_K // 2) + tl.arange(0, BLOCK_K // 2)
|
||||
k_mask_out = k_offsets_out < (hidden_size // 2)
|
||||
tl.store(
|
||||
x_fp4 + token_id * x_stride_m + k_offsets_out * x_stride_k,
|
||||
e2m1_packed,
|
||||
mask=k_mask_out,
|
||||
)
|
||||
|
||||
# Pack UE4M3 bytes into int32 (NVFP4: group_size=16, 4 groups per 64 elements)
|
||||
# 8 groups per k_block of 128 → 2 int32s per k_block
|
||||
# int32 can only pack 4 bytes (shifts >= 32 are UB), so split into two packs
|
||||
scale_offsets = tl.arange(0, num_groups) # [0..7]
|
||||
first_half = scale_offsets < 4 # groups 0-3 → int32[0]
|
||||
second_half = scale_offsets >= 4 # groups 4-7 → int32[1]
|
||||
|
||||
packed_lo = tl.sum(
|
||||
tl.where(first_half, scale_e4m3_bits.to(tl.int32) << (scale_offsets * 8), 0),
|
||||
axis=0,
|
||||
).to(tl.int32)
|
||||
packed_hi = tl.sum(
|
||||
tl.where(second_half, scale_e4m3_bits.to(tl.int32) << ((scale_offsets - 4) * 8), 0),
|
||||
axis=0,
|
||||
).to(tl.int32)
|
||||
|
||||
# Write 2 int32s per k_block: x_sf shape is (M, K//64) = (M, num_k_blocks * 2)
|
||||
sf_base = token_id * x_sf_stride_m + k_block_id * 2 * x_sf_stride_k
|
||||
tl.store(x_sf + sf_base, packed_lo)
|
||||
tl.store(x_sf + sf_base + x_sf_stride_k, packed_hi)
|
||||
|
||||
if k_block_id == 0:
|
||||
topk_offsets = tl.arange(0, BLOCK_TOPK)
|
||||
topk_mask = topk_offsets < top_k
|
||||
|
||||
ids = tl.load(
|
||||
topk_ids + token_id * topk_ids_stride_m + topk_offsets * topk_ids_stride_k,
|
||||
mask=topk_mask,
|
||||
other=0,
|
||||
).to(tl.int64)
|
||||
tl.store(
|
||||
topk_idx_out
|
||||
+ token_id * topk_idx_stride_m
|
||||
+ topk_offsets * topk_idx_stride_k,
|
||||
ids,
|
||||
mask=topk_mask,
|
||||
)
|
||||
|
||||
weights = tl.load(
|
||||
topk_weights
|
||||
+ token_id * topk_weights_stride_m
|
||||
+ topk_offsets * topk_weights_stride_k,
|
||||
mask=topk_mask,
|
||||
other=0.0,
|
||||
)
|
||||
tl.store(
|
||||
topk_weights_out
|
||||
+ token_id * topk_weights_out_stride_m
|
||||
+ topk_offsets * topk_weights_out_stride_k,
|
||||
weights,
|
||||
mask=topk_mask,
|
||||
)
|
||||
|
||||
|
||||
def _stage_deepseek_v4_mega_moe_inputs(
|
||||
hidden_states: torch.Tensor,
|
||||
topk_weights: torch.Tensor,
|
||||
topk_ids: torch.Tensor,
|
||||
x_fp4: torch.Tensor, # uint8, shape (M, K//2)
|
||||
x_sf: torch.Tensor, # int32, shape (M, K//64)
|
||||
topk_idx_out: torch.Tensor,
|
||||
topk_weights_out: torch.Tensor,
|
||||
) -> None:
|
||||
num_tokens, hidden_size = hidden_states.shape
|
||||
if num_tokens == 0:
|
||||
return
|
||||
if hidden_size % 128 != 0:
|
||||
raise ValueError(
|
||||
"DeepSeek V4 MegaMoE input staging requires hidden_size to be "
|
||||
"a multiple of 128."
|
||||
)
|
||||
top_k = topk_ids.shape[1]
|
||||
if topk_weights.shape != topk_ids.shape:
|
||||
raise ValueError(
|
||||
"DeepSeek V4 MegaMoE input staging requires topk_weights and "
|
||||
"topk_ids to have the same shape."
|
||||
)
|
||||
|
||||
block_k = 128
|
||||
grid = (num_tokens, triton.cdiv(hidden_size, block_k))
|
||||
block_topk = triton.next_power_of_2(top_k)
|
||||
_deepseek_v4_stage_mega_moe_inputs_kernel[grid](
|
||||
hidden_states,
|
||||
x_fp4,
|
||||
x_sf,
|
||||
topk_ids,
|
||||
topk_weights,
|
||||
topk_idx_out,
|
||||
topk_weights_out,
|
||||
hidden_states.stride(0),
|
||||
hidden_states.stride(1),
|
||||
x_fp4.stride(0),
|
||||
x_fp4.stride(1),
|
||||
x_sf.stride(0),
|
||||
x_sf.stride(1),
|
||||
topk_ids.stride(0),
|
||||
topk_ids.stride(1),
|
||||
topk_weights.stride(0),
|
||||
topk_weights.stride(1),
|
||||
topk_idx_out.stride(0),
|
||||
topk_idx_out.stride(1),
|
||||
topk_weights_out.stride(0),
|
||||
topk_weights_out.stride(1),
|
||||
hidden_size,
|
||||
top_k,
|
||||
BLOCK_K=block_k,
|
||||
GROUP_K=16, # NVFP4: group_size=16 (scale_vec::4X)
|
||||
BLOCK_TOPK=block_topk,
|
||||
num_warps=4,
|
||||
)
|
||||
100
scripts/serve_vllm.py
Normal file
100
scripts/serve_vllm.py
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DeepSeek V4 Pro NVFP4 — vLLM OpenAI-compatible server.
|
||||
|
||||
Run from the venv on the B200 node:
|
||||
source /root/nvidia-meeting/venv/bin/activate
|
||||
python3 /root/nvidia-meeting/deepseek-v4-quant/scripts/serve_vllm.py
|
||||
|
||||
Or in the background:
|
||||
nohup python3 /root/nvidia-meeting/deepseek-v4-quant/scripts/serve_vllm.py \
|
||||
> /root/nvidia-meeting/vllm_serve.log 2>&1 &
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# ── Patch: Add compress_ratios to DeepseekV4Config ──────────────────────────
|
||||
# transformers 5.8.0 renamed compress_ratios → compress_rates (dict format).
|
||||
# vllm 0.20.2 still expects compress_ratios as a list indexed by layer_id.
|
||||
# We patch the Config class to expose compress_ratios as a property that
|
||||
# converts the new dict format back to the list format vllm expects.
|
||||
import transformers
|
||||
try:
|
||||
from transformers import DeepseekV4Config
|
||||
|
||||
_orig_init = DeepseekV4Config.__init__
|
||||
|
||||
def _patched_init(self, *args, **kwargs):
|
||||
_orig_init(self, *args, **kwargs)
|
||||
# If compress_ratios already exists as a list, leave it alone
|
||||
if hasattr(self, 'compress_ratios') and isinstance(self.compress_ratios, list):
|
||||
return
|
||||
# Convert compress_rates dict → compress_ratios list
|
||||
if hasattr(self, 'compress_rates') and isinstance(self.compress_rates, dict):
|
||||
rates = self.compress_rates
|
||||
# Build per-layer list from the dict schema
|
||||
# V4 pattern: layers 0-1=128, then alternating 4/128, last=0
|
||||
n_layers = getattr(self, 'num_hidden_layers', 61)
|
||||
cr = rates.get('compressed_sparse_attention', 4)
|
||||
hr = rates.get('heavily_compressed_attention', 128)
|
||||
ratios = []
|
||||
for i in range(n_layers):
|
||||
if i < 2:
|
||||
ratios.append(hr)
|
||||
elif i == n_layers - 1:
|
||||
ratios.append(0)
|
||||
else:
|
||||
ratios.append(cr if i % 2 == 0 else hr)
|
||||
self.compress_ratios = ratios
|
||||
elif hasattr(self, 'compress_rates') and isinstance(self.compress_rates, list):
|
||||
self.compress_ratios = self.compress_rates
|
||||
|
||||
DeepseekV4Config.__init__ = _patched_init
|
||||
print("✓ Patched DeepseekV4Config.__init__ to add compress_ratios")
|
||||
except ImportError:
|
||||
print("⚠ DeepseekV4Config not found, skipping compress_ratios patch")
|
||||
|
||||
MODEL = "/root/nvidia-meeting/DeepSeek-V4-Pro-NVFP4"
|
||||
|
||||
# These flags are critical for V4 — do not change without understanding why:
|
||||
# --trust-remote-code V4 needs custom modeling code
|
||||
# --kv-cache-dtype fp8 Match our kv_cache_qformat=fp8_cast quantization
|
||||
# --block-size 256 V4 recommended block size
|
||||
# --enable-expert-parallel Distribute expert computation across GPUs (critical for 256-expert MoE)
|
||||
# --tensor-parallel-size 8 8× B200
|
||||
# --compilation-config CUDA graphs for throughput — FULL_AND_PIECEWISE + all custom ops
|
||||
# --attention_config FP4 indexer cache for V4 MLA attention
|
||||
# --moe-backend deep_gemm_mega_moe — optimized MoE kernel for Blackwell
|
||||
# --tokenizer-mode deepseek_v4 — V4-specific tokenizer
|
||||
# --tool-call-parser deepseek_v4 — native tool calling
|
||||
# --enable-auto-tool-choice Auto tool choice for function calling
|
||||
# --reasoning-parser deepseek_v4 — reasoning/thinking output parsing
|
||||
# --speculative_config MTP speculative decoding (2 speculative tokens)
|
||||
|
||||
cmd = [
|
||||
sys.executable, "-m", "vllm.entrypoints.openai.api_server",
|
||||
"--model", MODEL,
|
||||
"--trust-remote-code",
|
||||
"--kv-cache-dtype", "fp8",
|
||||
"--block-size", "256",
|
||||
"--enable-expert-parallel",
|
||||
"--tensor-parallel-size", "8",
|
||||
"--compilation-config", '{"cudagraph_mode":"FULL_AND_PIECEWISE", "custom_ops":["all"]}',
|
||||
"--attention_config.use_fp4_indexer_cache=True",
|
||||
"--moe-backend", "deep_gemm_mega_moe", # WARN: No NVFP4 mega_moe kernel. Use docker-compose (omits this flag) instead.
|
||||
"--tokenizer-mode", "deepseek_v4",
|
||||
"--tool-call-parser", "deepseek_v4",
|
||||
"--enable-auto-tool-choice",
|
||||
"--reasoning-parser", "deepseek_v4",
|
||||
"--speculative_config", '{"method":"mtp","num_speculative_tokens":2}',
|
||||
"--host", "0.0.0.0",
|
||||
"--port", "8000",
|
||||
]
|
||||
|
||||
print(f"Starting vLLM server for {MODEL}")
|
||||
print(f"Command: {' '.join(cmd)}")
|
||||
print(f"Log: /root/nvidia-meeting/vllm_serve.log")
|
||||
print()
|
||||
|
||||
sys.exit(subprocess.call(cmd))
|
||||
Reference in New Issue
Block a user