Chuyển đến nội dung chính
Hướng dẫn AI

Chạy DeepSeek V4.1 Flash cục bộ trên hai DGX Spark với EXL3 và vLLM

Tìm hiểu cách tải xuống, cấu hình, khởi chạy, kiểm thử và tối ưu DeepSeek V4.1 Flash EXL3 trên hai hệ thống NVIDIA GB10 DGX Spark. Hướng dẫn này bao gồm trình khởi chạy tự động, bộ nhớ Engram, giải mã suy đoán DSpark, các biện pháp bảo vệ bộ nhớ, tùy chọn đồng bộ hóa, yêu cầu API, kiểm thử và tinh chỉnh hiệu năng.

Chạy DeepSeek V4.1 Flash cục bộ trên hai DGX Spark với EXL3 và vLLM

Tổng quan

Dự án này cung cấp DeepSeek V4.1 Flash cục bộ trên hai hệ thống NVIDIA GB10 DGX Spark. Dự án sử dụng checkpoint EXL3 2,9 bits-per-weight, song song tensor trên cả hai nút, kernel sm_121a gốc và API vLLM tương thích OpenAI trên cổng 8888.

Mã định danh model được cung cấp là DeepSeek-v4.1-Flash-EXL3. Hệ thống chỉ cung cấp văn bản và được thiết kế riêng cho bộ kit GB10 hai nút kết nối qua CX7.

Container bổ sung hỗ trợ EXL3 vào vllm/vllm-openai:deepseekv41-flash-0909, một image vLLM ARM64 có sẵn kiến trúc DeepseekV41 cần thiết.

Dự án cung cấp những gì

  • Cung cấp dịch vụ OpenAI tương thích cục bộ: Ứng dụng có thể gọi endpoint tiêu chuẩn /v1/chat/completions.
  • Song song tensor trên hai nút: Model chạy với kích thước tensor-parallel là 2 qua kết nối CX7.
  • Lượng tử hóa EXL3: Checkpoint có trung bình 2,9 bpw và sử dụng codebook mul1 cùng các thiết lập lượng tử hóa theo tensor.
  • Speculation DSpark gốc: Các draft expert đã được lưu dưới mtp.* trong checkpoint, vì vậy không cần model draft riêng.
  • Hỗ trợ ngữ cảnh dài: Cấu hình đi kèm đặt MAX_MODEL_LEN=600000 và sử dụng pool bộ nhớ đệm KV 2,5 GiB.
  • Bảng Engram dựa trên tệp: Dữ liệu Engram chưa lượng tử hóa được đọc từ các shard model gốc 47 và 48 thay vì được ghim vào bộ nhớ máy chủ.
  • Khởi động tự động: start.sh tải trọng số, lấy hoặc build image, chia sẻ tệp với worker, chạy kiểm tra GPU và khởi động API.
  • Bảo vệ thời gian chạy: Trình khởi chạy kiểm tra biên an toàn khi khởi động, giải phóng allocator giữa các giai đoạn prefill, loại bỏ page cache sau khi tải và điều chỉnh OOM score của các container.

Yêu cầu phần cứng và lưu trữ

Repository này nhắm chính xác đến hai nút NVIDIA GB10 DGX Spark. Địa chỉ mặc định của nút head là 10.0.0.1, các nút giao tiếp qua CX7. README xác định các giao diện đã được kiểm thử là enp1s0f1np1/rocep1s0f1 trên nút đầu tiên và enp1s0f0np0/rocep1s0f0 trên nút thứ hai.

Hãy dự trù khoảng 387 GiB dữ liệu model:

  • Khoảng 197 GiB cho 39 shard EXL3 từ Mia-AiLab/DeepSeek-V4.1-Flash-EXL3-2.9bpw.
  • Khoảng 190 GiB cho các shard DeepSeek gốc 47 và 48 cùng index. Các tệp này chứa bảng Engram gốc.
  • Khoảng 9 GiB cho image container đã phát hành nếu có thể pull image tương thích thay vì build.

Nếu bạn đã có thư mục DeepSeek-V4.1-Flash hoàn chỉnh, hãy trỏ ENGRAM_DIR đến thư mục đó để tránh tải lại nguồn Engram. Chỉ shard 47, 48 và index được đọc cho mục đích này.

Trên DGX Spark, RAM của máy chủ cũng là bộ nhớ GPU. Mỗi lần cấp phát CUDA ngay lập tức tiêu thụ bộ nhớ dùng chung, vì vậy nên thay đổi độc lập các thiết lập lưu trữ và bộ nhớ rồi kiểm thử bằng prompt dài.

Chuẩn bị nút Head

1. Clone repository và tạo tệp môi trường

Chạy quá trình triển khai từ thư mục repository trên Spark head:

cp .env.example .env

Trình khởi chạy cũng tạo .env trong lần chạy đầu tiên nếu tệp này chưa tồn tại. Hãy xem lại tệp và cập nhật các giá trị như WORKER_USER và GID khi hệ thống của bạn yêu cầu.

Các giá trị được ghi rõ trong .env được ưu tiên hơn tiền tố môi trường trên dòng lệnh vì start.sh nạp tệp này sau môi trường được kế thừa. Ví dụ, hãy thay đổi WEIGHT_SYNC bên trong .env thay vì chạy WEIGHT_SYNC=zfs ./start.sh.

2. Cài đặt công cụ tải xuống Hugging Face

Việc tự động tải model yêu cầu Hugging Face CLI có hỗ trợ truyền dữ liệu:

pip install -U 'huggingface_hub[hf_transfer]'

Quá trình tải có thể tiếp tục sau khi bị gián đoạn. Nếu một lần chạy bị gián đoạn khiến model chưa hoàn chỉnh, hãy chạy lại trình khởi chạy hoặc trình tải xuống.

3. Tùy chọn tải xuống mà không khởi động server

Để chuẩn bị các tệp EXL3 và Engram trước khi khởi động vLLM, hãy chạy:

./download.sh

Tính năng tự động tải xuống được bật theo mặc định. Đặt AUTO_DOWNLOAD=0 trong cấu hình nếu bạn muốn tự quản lý các tệp.

Khởi động server

Từ repository trên nút head, hãy chạy:

./start.sh

Trong lần chạy đầu tiên, trình khởi chạy thực hiện các thao tác tổng quát sau:

  1. Kiểm tra biên bộ nhớ khả dụng và cấu hình nút.
  2. Tải xuống mọi tệp EXL3 và Engram còn thiếu.
  3. Pull image công khai ghcr.io/miaai-lab/deepseek-v4.1-flash-exl3-2x-dgx-sparks:2.9bpw.
  4. Chỉ build cục bộ nếu dấu phiên bản recipe đã thay đổi hoặc registry không khả dụng.
  5. Cung cấp trọng số cho worker.
  6. Chạy các kiểm tra GPU đối với GEMM EXL3 và giải lượng tử hóa Engram.
  7. Khởi chạy hai rank tensor-parallel và cung cấp API trên cổng 8888.

Một checkout sạch phải khớp với dấu phiên bản recipe trong image đã phát hành, vì vậy thông thường không cần biên dịch.

Kiểm soát việc build image và truyền dữ liệu

  • BUILD=1 ./start.sh luôn xây dựng image cục bộ.
  • SKIP_BUILD=1 ./start.sh giữ lại image đã pull ngay cả khi dấu phiên bản recipe khác.
  • SKIP_PULL=1 ./start.sh không kết nối đến registry container.
  • PULL=1 ./start.sh pull lại image ngay cả khi dấu phiên bản cục bộ khớp.
  • SKIP_SHIP=1 ./start.sh ngăn head sao chép image sang worker.

Theo mặc định, việc chuyển image sử dụng tệp tar trung gian được truyền qua rsync có khả năng tiếp tục. Đặt IMAGE_SHIP=stream để pipe docker save vào docker load của worker. Streaming không cần dung lượng tạm, nhưng quá trình truyền bị gián đoạn sẽ phải bắt đầu lại từ đầu. Chế độ auto ưu tiên truyền có khả năng tiếp tục và chuyển sang streaming khi dung lượng đĩa của worker bị hạn chế.

Kiểm tra và quản lý quá trình triển khai

Sử dụng các lệnh quản lý của launcher thay vì vận hành riêng từng container:

./start.sh status
./start.sh logs
./start.sh logs worker
./start.sh stop
./start.sh restart

Nếu worker đã có các trọng số được đồng bộ, hãy khởi động lại mà không lặp lại bước đồng bộ:

SKIP_SYNC=1 ./start.sh restart

Trong quá trình khởi động, bộ phát hiện im lặng log trong 420 giây sẽ thu thập stack trace Python từ cả hai rank. Các tệp chẩn đoán được ghi vào logs/.

Gửi yêu cầu API đầu tiên

Sau khi máy chủ hoạt động ổn định, hãy gửi yêu cầu hoàn tất trò chuyện đến node head. Yêu cầu kiểm tra nhanh này tắt tính năng suy nghĩ để dễ kiểm tra câu trả lời trực tiếp:

curl -s http://127.0.0.1:8888/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"DeepSeek-v4.1-Flash-EXL3","messages":[{"role":"user","content":"What is 17*19? Reply with the integer only."}],"max_tokens":32,"temperature":0,"chat_template_kwargs":{"enable_thinking":false}}'

Câu trả lời dự kiến là 323. Bất kỳ client tương thích với OpenAI nào cũng có thể sử dụng cùng base URL và mã định danh model.

Sử dụng thiết lập sampling được khuyến nghị

Đối với việc sử dụng model thông thường, thiết lập sampling chính thức là:

  • temperature=1.0
  • top_p=0.95
  • Đã bật tính năng suy nghĩ

Chat template mặc định sử dụng mức độ suy luận cao, được biểu thị bằng 75. Template cũng chấp nhận low cho 50, max cho 100 hoặc một số nguyên từ 1 đến 100.

Một yêu cầu sử dụng cấu hình sampling được khuyến nghị có thể như sau:

curl -s http://127.0.0.1:8888/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"DeepSeek-v4.1-Flash-EXL3","messages":[{"role":"user","content":"Explain how tensor parallel inference works."}],"max_tokens":512,"temperature":1.0,"top_p":0.95,"reasoning_effort":"high"}'

Tìm hiểu về giải mã suy đoán DSpark

Quá trình triển khai sử dụng giải mã suy đoán DSpark với các draft expert được tích hợp trong checkpoint. Không có drafter riêng cần tải xuống, nạp hoặc cung cấp.

Cấu hình vLLM bên dưới tương đương với:

--speculative-config '{"method":"dspark","num_speculative_tokens":3}'

Checkpoint có giới hạn block size của draft là năm, nhưng thiết lập được cung cấp sử dụng ba token suy đoán vì cho tốc độ cao hơn trong văn bản xuôi. Các kích thước capture gồm 1 2 3 4 6 8 12 18 24, trong đó có kích thước sáu cho hai chuỗi, mỗi chuỗi tạo ra ba token suy đoán.

DSpark cải thiện tốc độ giải mã một luồng trong các phép đo được báo cáo. Với các batch lớn hơn, hãy tắt bằng SPEC_METHOD=none. Việc này cũng giải phóng khoảng 3.5 GiB. README báo cáo khoảng 31.6 token mỗi giây cho một luồng suy đoán, trong khi chế độ phục vụ không suy đoán đạt thông lượng tổng hợp cao hơn ở bốn luồng.

Quản lý bộ nhớ an toàn

Phân bổ gần đúng cho mỗi node gồm 99.5 GiB trọng số EXL3 trên mỗi rank, pool KV 2.5 GiB, khoảng 5 đến 7 GiB cho context, NCCL, workspace và CUDA graph, cùng khoảng 9 GiB cho các tiến trình vLLM, Docker, hệ điều hành và desktop.

Các giới hạn được cung cấp là:

  • MAX_MODEL_LEN=600000
  • MAX_NUM_SEQS=2
  • MAX_NUM_BATCHED_TOKENS=1024
  • Pool bộ nhớ đệm KV 2.5 GiB

Bố cục KV được tự động chọn là fp8_ds_mla của DeepSeek. Không truyền --kv-cache-dtype. Pool được đo có thể chứa 774,400 token ở giới hạn context 614,400, với chi phí khoảng 3.4 KiB mỗi token.

Không ước tính lượng tử hóa của checkpoint này bằng cách chuyển 2.9 thành số nguyên 2. Tham số lượng tử hóa được chọn theo từng tensor, và các routed expert, shared expert, ma trận attention, tensor Engram, tensor MTP và head của mô hình ngôn ngữ sử dụng các giá trị khác nhau.

Sau khi làm nóng, head giữ lại khoảng 4.07 đến 4.21 GiB bộ nhớ khả dụng trong các bài kiểm tra được báo cáo. Prefill 601,000 token làm giảm khoảng trống đó xuống khoảng 2.1 GiB. Vì vậy, hãy kiểm tra các thay đổi sau một prefill dài thay vì ngay sau khi khởi động.

Chỉ tăng một thiết lập liên quan đến bộ nhớ mỗi lần. README cảnh báo rằng thêm 0.5 GiB vào pool KV có thể làm mất khoảng 1 GiB khoảng trống prefill. Pool 3 GiB vượt qua bài kiểm tra nhanh nhưng thất bại ở khoảng 470,000 token trong prompt 600,000 token.

Sử dụng bộ bảo vệ bộ nhớ tùy chọn một cách thận trọng

scripts/memguard.sh kiểm tra /proc/meminfo mỗi giây một lần và dừng container phục vụ cục bộ sau hai mẫu liên tiếp thấp hơn DSV41_MEM_GUARD_GIB, với ngưỡng mặc định là 1.5 GiB.

Guard bị tắt theo mặc định vì không thể xác định tiến trình nào đã sử dụng bộ nhớ. Guard luôn dừng container vLLM của chính nó, ngay cả khi một workload không liên quan trên host gây ra tình trạng áp lực bộ nhớ. Để bật guard một cách rõ ràng, hãy đặt:

DSV41_MEM_GUARD=1

Đối với các tác vụ nặng phía host, README khuyến nghị giới hạn tác vụ gây ra vấn đề:

systemd-run --scope -p MemoryMax=2G <job>

Việc triển khai vẫn sử dụng --oom-score-adj 1000 cho cả hai container, cùng với bước kiểm tra trước khi khởi động về dung lượng dự phòng, giải phóng allocator sau mỗi lần prefill và loại bỏ page cache sau khi tải.

Chọn chiến lược đồng bộ trọng số

NFS: mặc định

NFS là cấu hình phù hợp với bộ thiết bị đã được kiểm thử. Cấu hình này sử dụng lại exporter vllm-fn-nfs, cung cấp các thư mục EXL3 và slim Engram, rồi mount chúng trong các container worker dưới dạng /model và /engram-src.

./start.sh share
./start.sh

start.sh tự động thực hiện bước chia sẻ, vì vậy lệnh share rõ ràng chủ yếu hữu ích khi chuẩn bị hoặc khắc phục sự cố cho export.

ZFS: bản sao cục bộ trên node

Đặt WEIGHT_SYNC=zfs trong .env để duy trì một dataset cục bộ trên mỗi node. ZFS có thể cải thiện khả năng chống chịu và chỉ truyền các block đã thay đổi sau bản sao ban đầu. ZFS cũng cho phép khôi phục dựa trên snapshot.

Chi phí là đáng kể: mỗi node cần khoảng 385 GiB dung lượng trống trong pool. README lưu ý rằng Spark thứ hai được kiểm thử chỉ còn khoảng 41 GiB trống, nên không thể sử dụng ZFS trên node đó.

Thiết lập điển hình trên mỗi node:

sudo apt-get install -y zfsutils-linux
sudo zpool create -f models <vdev>
sudo zfs create -o recordsize=1M -o atime=off -o compression=lz4 \
  -o mountpoint=/path/to/model models/dsv41-exl3
sudo zfs create -o recordsize=1M -o atime=off -o compression=lz4 \
  -o mountpoint=/path/to/engram models/dsv41-engram
sudo zfs allow -u "$WORKER_USER" create,destroy,mount,receive,snapshot,rollback models

Chỉ nạp các shard 47 và 48 cùng với index vào dataset Engram. Không sao chép toàn bộ cây model native 476 GiB. Nếu pool hoặc zfs recv của worker không khả dụng, launcher sẽ chuyển sang streaming.

Rsync: bản sao cục bộ đơn giản hơn

WEIGHT_SYNC=rsync tạo một bản sao thông thường trên node mà không yêu cầu pool ZFS. Cấu hình này không cung cấp cơ chế cập nhật gia tăng ở cấp block như ZFS.

Đóng gói dữ liệu Engram để prefill nhanh hơn

Các bảng Engram vẫn ở định dạng FP8 native và không thuộc cây EXL3 đã lượng tử hóa. Theo mặc định, cơ chế embedding dựa trên file truy xuất các hàng mà không ghim khoảng 47 GiB cho mỗi layer vào bộ nhớ.

Để chuyển các file hàng Engram theo rank sang NVMe cục bộ của mỗi node, hãy chạy:

./start.sh pack

Việc đóng gói ghi khoảng 94 GiB trên mỗi node khi kích thước tensor parallel là hai. Đây là bước tùy chọn và không bắt buộc để khởi động. Trong các thử nghiệm được báo cáo, các file Engram đã đóng gói cục bộ cải thiện hiệu năng prefill khoảng 25 đến 50 phần trăm vì các lần thiếu hàng sử dụng I/O trực tiếp cục bộ thay vì NFS.

Các hàng Engram phải tiếp tục được định kiểu là fp8_e4m3fn khi truyền vào kernel giải lượng tử. Xử lý chúng dưới dạng uint8 có thể tạo ra kết quả có vẻ hợp lý nhưng không chính xác.

Chạy các bài kiểm tra xác thực

Kho mã bao gồm các kiểm tra ở cấp source không yêu cầu PyTorch hoặc vLLM:

python3 tests/test_numeric_config.py
python3 tests/test_engram_src.py
python3 tests/test_engram_secondary.py
python3 tests/test_k_map.py
python3 tests/test_memory_log.py
python3 tests/test_exl3_lm_head.py
python3 tests/test_sm120_block64.py
python3 tests/test_h2d_stage.py
python3 scripts/weight_budget.py --tp 2

Bài kiểm tra tính tương đương của chat template yêu cầu transformers và encoding/encoding.py của model gốc:

python3 tests/test_chat_template.py --src /path/to/DeepSeek-V4.1-Flash

Sau khi API đang chạy, hãy thực hiện bài kiểm tra smoke end-to-end:

bash tests/test_smoke.sh

Bài kiểm tra smoke yêu cầu model tính 17*19 và mong đợi 323. Trước khi khởi chạy dịch vụ, start.sh cũng chạy các kiểm tra tương đương GPU và giải lượng tử Engram trong một container tạm thời. Kết quả được ghi vào logs/overlay-verify.log.

Mẹo vận hành nâng cao

  • Ưu tiên DSpark cho một hoặc hai luồng: Tốc độ giải mã một luồng được báo cáo là 31.6 token mỗi giây, với tổng tốc độ 42.5 token mỗi giây trên hai luồng.
  • Tắt suy đoán khi chạy các batch rộng hơn: Với SPEC_METHOD=none, thông lượng một luồng giảm, nhưng tổng thông lượng bốn luồng đạt 53.7 token mỗi giây trong thử nghiệm được báo cáo.
  • Đóng gói Engram cho các prompt dài lặp lại: Các tệp hàng NVMe cục bộ giảm sự phụ thuộc vào bản xuất NFS của nút đầu và có thể cải thiện đáng kể tốc độ prefill.
  • Giữ bản triển khai chỉ xử lý văn bản: Phạm vi kernel SM120 không cung cấp kernel sparse-MLA cần thiết cho độ rộng vision-token của mô hình.
  • Không tùy tiện bật lại các luồng phụ EXL3: Bản triển khai tuần tự hóa các luồng EXL3 vì hai kernel trên các luồng CUDA khác nhau có thể gây deadlock với bộ đệm khóa duy nhất trên mỗi thiết bị của runtime.
  • Theo dõi đúng các giao diện mạng: NCCL không thể sử dụng các bí danh loopback 10.0.0.x. Việc chọn GID theo từng NIC không chính xác có thể gây lỗi sau khoảng một phút với ibv_modify_qp errno 61.
  • Kiểm tra bộ nhớ khi prefill dài: Khởi động thành công và các bài kiểm tra smoke ngắn không chứng minh rằng pool KV lớn hơn hoặc thiết lập context là an toàn.
  • Sử dụng launcher của repository: Launcher này điều phối đồng bộ hóa qua NFS hoặc cục bộ, các bước kiểm tra trước khi chạy, xác minh image, khởi động hai rank và chẩn đoán.

Hiệu năng dự kiến

Các phép đo được công bố ghi nhận 31.6 token mỗi giây cho một luồng giải mã và tổng cộng 42.5 token mỗi giây cho hai luồng. Prefill prompt ngắn đạt khoảng 965 đến 1,041 token mỗi giây với dữ liệu Engram được đóng gói và các kernel expert được nhóm.

Prefill prompt dài đương nhiên chậm hơn. Các ví dụ được báo cáo gồm khoảng 979 token mỗi giây cho prompt 100,000 token, 802 token mỗi giây ở 455,000 token và 810 token mỗi giây ở 601,000 token. Kết quả thực tế phụ thuộc vào cấu trúc prompt, cấu hình lưu trữ, suy đoán, độ rộng batch và kích thước chunk prefill được cấu hình.

Kết luận

Dự án này đóng gói các kernel chuyên biệt, trình tải EXL3, triển khai Engram dựa trên tệp, cấu hình mạng, các cơ chế kiểm soát bộ nhớ và launcher cần thiết để phục vụ DeepSeek V4.1 Flash trên hai GB10 DGX Spark. Hãy bắt đầu với các giá trị mặc định đi kèm, xác minh API bằng bài kiểm tra smoke, sau đó tinh chỉnh từng thiết lập một. Để vận hành an toàn nhất, hãy đánh giá cả thông lượng và bộ nhớ khả dụng trong các tải prefill dài thực tế.