vLLM-Moet 2-bit 양자화를 사용하여 DGX Spark에서 DeepSeek-V4-Flash-0731 (155 GB MoE) 실행하기
요약
vLLM-Moet 2-bit 양자화를 활용하여 단일 DGX Spark 환경에서 DeepSeek-V4-Flash 모델을 실행하는 기술적 방법을 공유합니다. ARM64 아키텍처에서의 빌드 주의사항과 MTP(Multi-Token Prediction) 적용 시의 성능 변화를 다룹니다.
핵심 포인트
- vLLM-Moet 2-bit 양자화로 대규모 MoE 모델 서빙 가능
- ARM64 환경에서는 소스 코드 직접 빌드 및 GPU 아키텍처 수정 필수
- 모델 로딩 시 대용량 스왑 파일 설정 권장
- MTP 적용 시 디코드 성능 향상 및 특정 조건에서의 성능 변화 확인
vLLM-Moet 2-bit 양자화를 사용하여 DGX Spark에서 DeepSeek-V4-Flash-0731 (155 GB MoE) 실행하기
저는 Deepseek-v4-Flash-0731 클라우드 API를 사용하였고, 단일 DGX Spark에서 2-bit 양자화로 MTP를 포함한 deepseek-v4-flash를 실행하기 위해 vllm-moet을 설정했습니다. 다른 분들에게 도움이 될까 하여 공유합니다. 아래 내용은 제 AI Agent가 작성한 요약본이므로 제가 직접 작성한 것은 아닙니다. 주의해야 할 몇 가지 중요한 사항이 있으며, AI에게 이를 수행하도록 안내해야 합니다. AI 혼자서는 제대로 완료하지 못할 것입니다.
- ARM64에서 vllm-moet 재빌드: 저장소에 PR #11을 pull 하여 소스 코드를 빌드하고, 지원되지 않는 sm121 GPU에 대해 오류를 발생하는 코드를 수정하도록 AI에게 요청하세요.
- 기본 VLLM 타임아웃(timeout) 시간 늘리기: 모델 로딩에 매우 오랜 시간이 걸려 잘못된 타임아웃이 발생할 수 있습니다.
아래 절차를 그대로 따라 하여 복제하는 것은 권장하지 않습니다. 대신 아래 텍스트를 귀하의 AI Agent에게 입력하여 프로세스를 처리하고 수정하도록 하세요.
- 매우 큰 스왑 파일(swapfile)을 설정해야 합니다. 그렇지 않으면 로딩이 실패합니다. 스왑 파일은 모델 로딩 중에만 필요합니다.
편의를 위해 프리뷰 버전에서 가져온 MTP 헤드(head) 저장소를 만들었습니다. 다른 분들에게 도움이 된다면, 여기에서 확인하실 수 있습니다: https://huggingface.co/ycui7/DeepSeek-V4-Flash-MTP
성능 측면에서, 프리필(prefill)은 안정적인 1000 tps를 유지합니다. 디코드(decode)는 다음과 같습니다.
Aggregate (tok/s) | Concurrency | MTP | no-MTP | Δ |
|---|---|---|---| |
| 1 | 25.2 | 19.1 | +31.5% |
| 2 | 30.9 | 26.4 | +17.3% |
| 4 | 43.2 | 45.6 | −5.3% |
Per-request (tok/s) | Concurrency | MTP | no-MTP |
|---|---|---| |
| 1 | 25.2 | 19.1 |
| 2 | 23.0 | 17.7 |
| 4 | 14.5 | 13.9 |
== 아래는 AI의 답변입니다 ==
요약 (TL;DR): vLLM-Moet를 사용하여 단일 DGX Spark (GB10, 121.7 GiB 통합 메모리, aarch64)에서 새로운 deepseek-ai/DeepSeek-V4-Flash-0731 체크포인트를 서빙합니다.
이미지는 반드시 Spark 자체에서 빌드되어야 하며 (x86→arm64 전송은 불가능함), Dockerfile 베이스 다이제스트(digest)는 amd64 전용이므로 멀티 아키텍처(multi-arch) 태그가 필요합니다. sm_120 cubin은 GB10의 sm_121에서 잘 작동하며, 0731 리비전의 DSpark MTP 헤드는 이 스택에서 드래프트(draft)로 작동하지 않습니다 — 일반 디코드(plain decode)를 수행하거나 메인 리포지토리의 1-레이어 MTP 헤드를 별도의 드래프트 모델로 로드해야 합니다 (디코드 성능 +48%).
스택 (The stack)
- 모델 (Model):
deepseek-ai/DeepSeek-V4-Flash-0731— 155.43 GiB FP8, 48개 샤드 (shards) - 엔진 (Engine): vLLM-Moet (vLLM v0.25.0 + 약 7.4k 라인의 패치) — 수작업으로 작성된 SM120 SASS 커널 기반의 2-bit MoE 전문가 (experts)
- 하드웨어 (Hardware): DGX Spark — GB10, aarch64, sm_121, 개별 VRAM 없음 (121.7 GiB 통합 메모리 풀), 128 GiB 스왑 파일 (swapfile)
측정 결과 (Spark, 512K, FORCE_RESIDENT, delta off)
| 지표 (Metric) | 값 (Value) |
|---|---|
| 2-bit planes | 43 layers × 1.69 GiB ≈ 73 GiB |
| KV cache u/512K / util 0.90 | 4.56M tokens (8.7× 동시성 (concurrency)) |
| 디코드 (Decode) — 일반 (plain) | ~19 tok/s (LPDDR5X 대역폭 제한) |
| 디코드 (Decode) — +MTP head | 26.6 tok/s (+48%) |
| 부팅 (Boot) — v025 warm plane cache | ~10분 (콜드 부팅 시 31–46분) |
MTP vs 일반 (pp2048/tg512, 3회 실행): conc1 25.2→19.1 (+31.5%), conc2 +17.3%, conc4 −5.3% (높은 동시성에서 전체적인 변동 발생; 요청당 성능 저하는 없음). 1-레이어 헤드에는 k=2가 최적입니다.
DGX Spark를 위해 수정해야 할 핵심 사항 (실제 주의사항)
1. Spark에서 빌드하십시오 — 이미지를 전송하지 마십시오.
PRO 6000 이미지는 linux/amd64입니다. vLLM은 아키텍처 전용이므로, x86에서 arm으로 docker save/load를 하는 것은 무의미합니다. aarch64에서 네이티브로 빌드하십시오.
2. Dockerfile 베이스 다이제스트는 amd64 전용입니다.
Dockerfile.sm120-v025는 vllm/vllm-openai:v0.25.0@sha256:e1c1ff…를 고정(pin)하고 있습니다. 이 다이제스트는 *단일 amd64 매니페스트(manifest)*입니다. 멀티 아키텍처 태그인 vllm/vllm-openai:v0.25.0으로 교체하십시오 (Spark에서 arm64 2f726d…로 해결됨). 기존 다이제스트는 이유를 적은 주석과 함께 남겨두십시오.
3. git bundle + 명시적 브랜치 fetch를 통한 리포지토리 전송.
git bundle create v025.bundle v025 → scp → git clone <bundle>, 그 다음 git fetch <bundle> v025:v025 && git checkout v025를 수행하십시오.
번들 클론(bundle clone)이 잘못된 브랜치(master)에 내려받아지는 경우 — fetch 작업이 필수적입니다. 4. sm_121을 위해 SASS를 다시 빌드하지 마십시오. GB10은 CC 12.1입니다. 저장소에 내장된 sm_120 cubin들과 TORCH_CUDA_ARCH_LIST=12.0a는 문제없이 로드됩니다 (v024 및 v025에서 검증된 마이너 버전 전방 호환성). flashinfer가 aarch64 cu130 wheel(0.6.14)을 배포하므로, 그 외에는 변경 사항이 없습니다. 5. 128 GiB 스왑 파일(swapfile)은 반드시 /etc/fstab에 있어야 합니다. 155 GiB 체크포인트는 121 GiB RAM에 스테이징할 수 없으므로, 로딩은 스왑(swap) 성능에 종속됩니다. 실행 스크립트의 swapon은 수동으로 재생성할 때만 실행되므로, 호스트를 재부팅하면 스왑이 0B가 되어 결정론적인 EngineCore OOM-kill이 발생하고 --restart 크래시 루프가 발생합니다 (26시간 동안 88회 재시작). 해결 방법: echo '/swapfile none swap sw 0 0' >> /etc/fstab. 가중치 로드 중 스왑 피크는 69 GiB로 관찰되었으며, 플레인 빌드(plane build) 후에는 약 2.4 GiB로 회수되었습니다. 6. 0731 버전의 MTP 헤드는 DSpark입니다 — 이 스택에서는 드래프트(draft)를 수행할 수 없습니다. 해당 리비전은 3레이어 DSpark 헤드(main_proj/main_norm/markov_head/confidence_head/hc_head)를 포함하고 있습니다. 포크(fork)된 버전의 MTP 경로는 이를 복제할 수 없습니다 (MTP 활성화 시 KeyError: mtp_block.main_norm.weight 발생 또는 드래프트 수락률 0%). 작동 가능한 두 가지 옵션은 다음과 같습니다: - **일반 디코딩 (Plain decode)** (--speculative-config제거) — 가장 간단하며, 약 19 tok/s 속도 - **메인 저장소의 MTP 헤드를 별도의 드래프트 모델로 사용 (+48%)** — 메인DeepSeek-V4-Flash저장소의 마지막 샤드(3.4 GB,num_nextn_predict_layers: 1)에서 1레이어 헤드를 추출하거나, 이미 배포된 ycui7/DeepSeek-V4-Flash-MTP를 사용하십시오: ```bash --speculative-config '{"method":"deepseek_mtp","model":"/models/DeepSeek-V4-Flash-MTP","num_speculative_tokens":2}' ``` **7. 읽기 전용(read-only) 모델 마운트를 주의하십시오.** 모델 디렉토리가 :ro로 바인드 마운트된 경우: VLLM_MOE_W2_STORE_DIR를 해당 디렉토리에 지정하면 **아무것도 저장되지 않으며** (매 재시작 시 약 14분이 소요됨), VLLM_MOE_W2_DELTA_GB>0설정은 **하드 크래시(hard-crash)**를 유발합니다 (델타 스토어가 잠금 파일(lock file)을 생성하려고 시도하여OSError: Errno 30 read-only` 발생). STORE_DIR을 별도의 쓰기 가능한 볼륨으로 지정하십시오. **8.
No nvidia-smi; FORCE_RESIDENT'의 경고는 무시해도 괜찮습니다.** nvidia-smi가 [N/A]를 표시하고, 장치 메모리(device memory)가 통합 풀(unified pool)을 채우는 동안 EngineCore RSS는 약 3 GiB로 유지됩니다. free -h/docker stats와 moe_w2: layer N planes built 로그를 통해 모니터링하십시오. "RESIDENT planes exceed budget by 69.7 GiB" 경고는 GB10에서 예상되는 현상입니다. 부팅은 정상적으로 이루어집니다 (planes와 KV가 풀을 공유함). 9. Spark에서는 DELTA_GB=0 설정이 올바른 선택입니다. FP4 delta를 비활성화하면 약 20 GiB의 공간이 즉시 KV 캐시로 확보되어 (706K → 4.56M tokens u/512K), 부팅 시간이 46분에서 31분으로 단축됩니다. 디코딩(Decode) 속도는 변하지 않습니다 (~19 tok/s — 대역폭 제한(bandwidth-bound) 사항이며, 여기에서 delta는 속도 요인이 아니었습니다). 10. 인내심을 가지십시오 — 로딩은 조용하며 스왑(swap)에 의존합니다. 155 GiB가 스왑을 통해 스테이징되는 동안(EngineCore CPU 사용률 99%) 약 16분간 로그 출력이 없을 수 있으며, 그 후 plane 빌드가 진행됩니다 (~14분 소요, 레이어당 워밍업 25s→6s). 컨테이너를 강제로 종료하지 마십시오. ## 실행 (운영 환경) bash docker run -d -it --restart unless-stopped --name ds4f-vllm-moet \ --gpus all --network host --ipc host --shm-size 64g \ -v /models:/models:rw -v /plane-cache:/plane-cache \ -e VLLM_MOE_W2=1 -e VLLM_MOE_W2_FORCE_RESIDENT=1 \ -e VLLM_MOE_W2_BASE_CACHE_GB=0 -e VLLM_MOE_W2_DELTA_GB=0 \ -e VLLM_MOE_W2_STORE_DIR=/plane-cache/packs \ vllm-moet-sm120:v025 \ /models/DeepSeek-V4-Flash-0731 --port 8000 \ --served-model-name deepseek-v4-flash \ --trust-remote-code --kv-cache-dtype fp8 --block-size 256 \ --max-model-len 524288 --gpu-memory-utilization 0.90 \ --max-num-batched-tokens 2048 --max-num-seqs 1 \ --tokenizer-mode deepseek_v4 --no-scheduler-reserve-full-isl \ --enable-auto-tool-choice --tool-call-parser deepseek_v4 \ --reasoning-parser deepseek_v4 # 선택 사항 MTP: --speculative-config '{"method":"deepseek_mtp","model":"/models/DeepSeek-V4-Flash-MTP","num_speculative_tokens":2}' 확인: curl :8000/v1/models 실행 후 채팅 완료(chat completion)를 시도하십시오. submitted by /u/Puzzleheaded_Base302 [link] [comments]
AI 자동 생성 콘텐츠
본 콘텐츠는 r/LocalLLaMA의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기