دوز مباشرة للمحتوى الرئيسي
دروس فالذكاء الاصطناعي

شغّل DeepSeek V4.1 Flash محلياً على جوج ديال DGX Spark باستعمال EXL3 وvLLM

تعلّم كيفاش تنزّل، تهيّأ، تطلق، تختبر وتحسّن DeepSeek V4.1 Flash EXL3 على جوج أنظمة NVIDIA GB10 DGX Spark. هاد الدليل كيغطي المشغّل الآلي، تخزين Engram، فكّ الترميز التخميني DSpark، حماية الذاكرة، اختيارات المزامنة، طلبات API، الاختبار، وضبط الأداء.

شغّل DeepSeek V4.1 Flash محلياً على جوج DGX Spark باستعمال EXL3 وvLLM

نظرة عامة

هاد المشروع كيشغّل DeepSeek V4.1 Flash محلياً على جوج ديال أنظمة NVIDIA GB10 DGX Spark. كيستعمل checkpoint ديال EXL3 بـ 2.9 bits-per-weight، وtensor parallelism بين الجوج ديال العقد، وkernels أصلية ديال sm_121a، وواجهة vLLM متوافقة مع OpenAI على البورت 8888.

المعرّف ديال الموديل اللي كيتخدم هو DeepSeek-v4.1-Flash-EXL3. النشر ديالو نصّي فقط، ومصمّم بالضبط لkit فيه جوج ديال GB10 مربوطين عبر CX7.

الكونتينر كيزيد دعم EXL3 فوق vllm/vllm-openai:deepseekv41-flash-0909، وهي صورة vLLM ARM64 فيها architecture المطلوبة DeepseekV41.

شنو كيوفّر المشروع

  • خدمة محلية متوافقة مع OpenAI: التطبيقات تقدر تستعمل endpoint العادي /v1/chat/completions.
  • Tensor parallelism بين جوج ديال العقد: الموديل كيتشغّل بحجم tensor-parallel قدره 2 عبر اتصال CX7.
  • Quantization ديال EXL3: الـcheckpoint عندو متوسط 2.9 bpw وكيستعمل codebook ديال mul1 مع إعدادات quantization لكل tensor.
  • Speculation أصلية ديال DSpark: draft experts مخزّنين من الأصل تحت mtp.* فالـcheckpoint، وبالتالي ما كايناش حاجة لموديل draft مستقل.
  • دعم السياق الطويل: الإعداد اللي جاي مع المشروع كيحدد MAX_MODEL_LEN=600000 وكيستعمل pool ديال KV cache بحجم 2.5 GiB.
  • جداول Engram مدعومة بالملفات: بيانات Engram غير المكمّمة كتتقرا من shards الأصليين 47 و48 ديال الموديل، بلا ما تتثبت فذاكرة المضيف.
  • تشغيل آلي: start.sh كيحمّل الأوزان، وكيجيب الصورة ولا كيبنيها، وكيشارك الملفات مع worker، وكيدير فحوصات GPU، وكيشغّل الـAPI.
  • حمايات وقت التشغيل: الـlauncher كيدير فحوصات هامش الإقلاع، وكيحرر allocators بين مراحل prefill، وكيحيد page cache من بعد التحميل، وكيعدّل OOM scores ديال الكونتينرات.

متطلبات العتاد والتخزين

هاد repository موجّه بالضبط لجوج ديال عقد NVIDIA GB10 DGX Spark. العنوان الافتراضي ديال head node هو 10.0.0.1، والعقد كيتواصلو عبر CX7. الـREADME كيعرف الواجهات اللي تجرّبات على أنها enp1s0f1np1/rocep1s0f1 فالعقدة الأولى وenp1s0f0np0/rocep1s0f0 فالعقدة الثانية.

حسب حسابك تقريباً 387 GiB ديال بيانات الموديل:

  • حوالي 197 GiB لـ39 shard ديال EXL3 من Mia-AiLab/DeepSeek-V4.1-Flash-EXL3-2.9bpw.
  • حوالي 190 GiB للشاردات الأصليين 47 و48 ديال DeepSeek، زائد الـindex. هادو فيهم جداول Engram الأصلية.
  • حوالي 9 GiB لصورة الكونتينر المنشورة إلا كان ممكن تسحب صورة مطابقة عوض ما تبنيها.

إلا كان عندك من قبل directory كامل ديال DeepSeek-V4.1-Flash، وجّه ENGRAM_DIR ليه باش ما تعاودش تحمّل مصدر Engram. غير shard 47 و48 والـindex اللي كيتقراو لهاد الغرض.

فـDGX Spark، RAM ديال المضيف هي حتى هي ذاكرة GPU. كل تخصيص CUDA كياكل مباشرة من الذاكرة المشتركة، لذلك خاص إعدادات التخزين والذاكرة يتبدلو بشكل مستقل ويتجرّبو مع prompt طويل.

وجد الـHead Node

1. نسخ الـrepository وإنشاء ملف البيئة

شغّل النشر من directory ديال الـrepository فـhead Spark:

cp .env.example .env

الـlauncher حتى هو كينشئ .env فأول تشغيل إلا ما كانش موجود. راجع الملف وحدّث قيم بحال WORKER_USER وGID إلا كانت الأنظمة ديالك محتاجة لهاد الشي.

القيم اللي مسمّاة بشكل صريح فـ.env عندها الأولوية على prefixes ديال environment فسطّر الأوامر، حيث start.sh كيقرا الملف فوق environment الموروثة. مثلاً، بدّل WEIGHT_SYNC داخل .env عوض ما تشغّل WEIGHT_SYNC=zfs ./start.sh.

2. تثبيت أدوات التحميل ديال Hugging Face

التحميل الآلي ديال الموديلات كيحتاج Hugging Face CLI مع دعم النقل:

pip install -U 'huggingface_hub[hf_transfer]'

التحميلات قابلة للاستئناف. إلا خلّى تشغيل تقطع والموديل ناقص، عاود شغّل الـlauncher ولا downloader.

3. تحميل اختياري بلا تشغيل السيرفر

باش تجهّز ملفات EXL3 وEngram قبل ما تقلّع vLLM، شغّل:

./download.sh

التحميل الآلي مفعّل افتراضياً. حدّد AUTO_DOWNLOAD=0 فالإعدادات إلا بغيتي تسيّر الملفات بيدك.

شغّل السيرفر

من الـrepository فـhead node، شغّل:

./start.sh

فأول تشغيل، الـlauncher كيدير العمليات العامة التالية:

  1. كيتأكد من هامش الذاكرة المتوفرة وإعدادات العقد.
  2. كيحمّل ملفات EXL3 وEngram الناقصة.
  3. كيسحب الصورة العمومية ghcr.io/miaai-lab/deepseek-v4.1-flash-exl3-2x-dgx-sparks:2.9bpw.
  4. كيبني محلياً غير إلا تبدّل recipe stamp ولا كان الـregistry غير متاح.
  5. كيخلي الأوزان متاحة للـworker.
  6. كيدير فحوصات GPU ديال EXL3 GEMM وEngram dequantization.
  7. كيطلق جوج ranks ديال tensor parallel وكيكشف الـAPI على البورت 8888.

خاص checkout نقي يطابق recipe stamp اللي فالصورة المنشورة، لذلك ما كيكونش compilation ضروري فالعادي.

التحكم فبناء الصورة والنقل

  • BUILD=1 ./start.sh ديما كيبني الصورة محلياً.
  • SKIP_BUILD=1 ./start.sh كيخلي الصورة اللي تجرّات حتى إلا كان ختم الوصفة ديالها مختلف.
  • SKIP_PULL=1 ./start.sh كيتفادى الاتصال بسجلّ الحاويات.
  • PULL=1 ./start.sh كيعيد جرّ الصورة حتى إلا كان الختم المحلي مطابق.
  • SKIP_SHIP=1 ./start.sh كيمنع الـhead من نسخ الصورة للـworker.

افتراضياً، إرسال الصورة كيستعمل ملف tar مرحلي كيتنقل عبر rsync قابل للاستئناف. عيّن IMAGE_SHIP=stream باش تدوز مخرجات docker save مباشرة لـdocker load ديال الـworker. البث ما كيحتاجش مساحة مؤقتة، ولكن إلا تقطع النقل كيبدا من الأول. الوضع auto كيفضّل النقل القابل للاستئناف، وكيرجع للبث ملي كتكون مساحة القرص فالـworker محدودة.

تفقد ودبّر النشر

استعمل أوامر التدبير ديال المُطلِق بلا ما تشغّل الحاويتين بوحدهم:

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

إلا كان الـworker عندو الأوزان متزامنة من قبل، عاود التشغيل بلا ما تكرر خطوة المزامنة:

SKIP_SYNC=1 ./start.sh restart

أثناء الإقلاع، كيجمع كاشف سكوت السجلات لمدة 420 ثانية مكدسات الاستدعاء ديال Python من الرتبتين بجوج. الملفات التشخيصية كتتكتب تحت logs/.

صيفط أول طلب ديالك للـAPI

منين كيولي السيرفر خدام مزيان، صيفط طلب إكمال محادثة للعقدة head. هاد الطلب التجريبي كيعطّل التفكير باش يكون الجواب المباشر ساهل للفحص:

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}}'

الجواب المنتظر هو 323. أي عميل متوافق مع OpenAI يقدر يستعمل نفس عنوان الأساس ومعرّف النموذج.

استعمل إعدادات أخذ العينات الموصى بها

للاستعمال العادي ديال النموذج، إعدادات أخذ العينات الرسمية هي:

  • temperature=1.0
  • top_p=0.95
  • التفكير مفعّل

قالب المحادثة كيبدا افتراضياً بمستوى عالٍ ديال الاستدلال، ممثل بـ75. وكيتقبل حتى low لـ50، وmax لـ100، ولا عدداً صحيحاً من 1 حتى 100.

الطلب اللي كيستعمل إعدادات أخذ العينات الموصى بها يقدر يكون هكذا:

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"}'

فهم فك الترميز التخميني DSpark

النشر كيستعمل فك الترميز التخميني DSpark، مع خبراء المسودة مدموجين فـcheckpoint. ما كاين حتى drafter مستقل خاصك تجرّو أو تحمّلو أو تخدمو.

إعداد vLLM الأساسي مكافئ لـ:

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

الـcheckpoint عندو سقف لحجم كتلة المسودة قدره خمسة، ولكن الإعداد المشحون كيستعمل ثلاثة ديال التوكنات التخمينية حيث تبيّن أنه أسرع فالنصوص النثرية. أحجام الالتقاط هي 1 2 3 4 6 8 12 18 24، ومن بينها الحجم ستة لجوج ديال السلاسل، كل وحدة كتنتج ثلاثة توكنات تخمينية.

DSpark كيحسّن فك الترميز ديال التدفق الواحد فالقِياسات المبلّغ عليها. مع الدفعات الكبيرة، عطّلو بـSPEC_METHOD=none. هاد الشي حتى هو كيفرّغ تقريباً 3.5 GiB. الـREADME كيذكر حوالي 31.6 توكن فالثانية لتدفق تخميني واحد، بينما الخدمة بلا تخمين وصلات لمعدل إجمالي أعلى مع أربعة ديال التدفقات.

دبّر الذاكرة بأمان

التخصيص التقريبي لكل عقدة كيشمل 99.5 GiB ديال أوزان EXL3 لكل رتبة، ومسبح KV بحجم 2.5 GiB، وحوالي 5 حتى 7 GiB للسياقات وNCCL ومساحات العمل ورسومات CUDA، وحوالي 9 GiB لعمليات vLLM وDocker ونظام التشغيل وسطح المكتب.

الحدود المشحونة هي:

  • MAX_MODEL_LEN=600000
  • MAX_NUM_SEQS=2
  • MAX_NUM_BATCHED_TOKENS=1024
  • مسبح cache ديال KV بحجم 2.5 GiB

تخطيط KV كيتختار أوتوماتيكياً على أنه fp8_ds_mla ديال DeepSeek. ما تدوزش --kv-cache-dtype. المسبح المقاس كان كيسع 774,400 توكن مع حد سياق قدره 614,400، وبكلفة تقريباً 3.4 KiB لكل توكن.

ما تقدّرش quantization ديال هاد الـcheckpoint بتحويل 2.9 للعدد الصحيح 2. معامل quantization ديالو كيتختار لكل tensor، والخبراء الموجّهون المختلفون، والخبراء المشتركون، ومصفوفات الانتباه، وEngram tensors، وMTP tensors، وhead ديال نموذج اللغة كيستعملو قيماً مختلفة.

من بعد الإحماء، الـhead خلّى تقريباً بين 4.07 و4.21 GiB من الذاكرة المتاحة فاختبارات التقرير. وتهيئة مسبقة من 601,000 توكن نقصات هاد الهامش لحوالي 2.1 GiB. لذلك، تحقّق من التغييرات من بعد تهيئة مسبقة طويلة، ماشي مباشرة من بعد الإقلاع.

زِد غير إعداد واحد متعلق بالذاكرة فكل مرة. الـREADME كينبّه أن زيادة 0.5 GiB فمسبح KV تقدر تكلّف تقريباً 1 GiB من هامش التهيئة المسبقة. مسبح بحجم 3 GiB داز اختباراً تجريبياً قصيراً، ولكن فشل تقريباً عند 470,000 توكن فـprompt بحجم 600,000 توكن.

استعمل حارس الذاكرة الاختياري بحذر

scripts/memguard.sh كيفحص /proc/meminfo مرة كل ثانية، وكيقتل حاوية الخدمة المحلية من بعد جوج عينات متتابعة تحت DSV41_MEM_GUARD_GIB، والعتبة الافتراضية ديالو هي 1.5 GiB.

الحارس معطّل افتراضياً حيث ما كيقدرش يحدّد شنو هي العملية اللي استهلكات الذاكرة. ديما كيقتل الكونتينر ديال vLLM ديالو، حتى إلا كان الضغط جاي من خدمة أخرى خدامة فالمضيف. باش تفعّلو بشكل صريح، عيّن:

DSV41_MEM_GUARD=1

بالنسبة للمهام الثقيلة فالجهة ديال المضيف، الـREADME كيوصي تحدّ من المهمة اللي مسبّبة المشكل عوض ذلك:

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

النشر باقي كيستعمل --oom-score-adj 1000 للكونتينرين بجوج، مع فحص هامش الإقلاع، وتحرير المخصّص ديال الذاكرة من بعد كل prefill، وحذف page cache من بعد التحميل.

اختار استراتيجية مزامنة الأوزان

NFS: الإعداد الافتراضي

NFS هو الإعداد اللي كيناسب العتاد اللي تجرّب. كيعيد استعمال المصدّر vllm-fn-nfs، وكيعرّض مجلدات EXL3 وslim Engram، وكيركبهم فكونتينرات العمال على شكل /model و/engram-src.

./start.sh share
./start.sh

start.sh كيدير مرحلة المشاركة أوتوماتيكياً، لذلك الأمر الصريح share كينفع بالأساس ملي كتوجد التصدير ولا كتقلب على المشكل ديالو.

ZFS: نسخ محلية لكل عقدة

عيّن WEIGHT_SYNC=zfs فـ.env باش تحافظ على dataset محلي فكل عقدة. ZFS يقدر يحسّن المرونة ويرسل غير البلوكات اللي تبدلات من بعد النسخة الأولى. وكيخلي حتى الرجوع بالاعتماد على snapshots ممكن.

الثمن كبير: كل عقدة محتاجة تقريباً 385 GiB ديال المساحة الخاوية فـpool. الـREADME كيشير بلي Spark الثاني اللي تجرّب كان فيه غير حوالي 41 GiB خاوية، وبالتالي ما قدرناش نستعملو ZFS تما.

إعداد نموذجي فكل عقدة هو:

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

عمّر dataset ديال Engram غير بالـshards 47 و48 ومعاهم الـindex. ما تنسخش شجرة الموديل native الكاملة اللي حجمها 476 GiB. إلا ما كانش الـpool ولا zfs recv ديال العامل متاح، الـlauncher كيرجع للبث المباشر.

Rsync: نسخة محلية أبسط

WEIGHT_SYNC=rsync كينشئ نسخة عادية محلية فكل عقدة بلا ما يحتاج ZFS pool. وما كيوفّرش التحديثات التدريجية على مستوى بلوكات ZFS.

جمّع بيانات Engram باش تسرّع الـprefill

جداول Engram كتبقى بصيغة FP8 الأصلية، وماشي جزء من شجرة EXL3 المكمّمة. افتراضياً، تطبيق embedding المعتمد على الملفات كيجبد الصفوف بلا ما يثبّت تقريباً 47 GiB لكل طبقة فالذاكرة.

باش تنقل ملفات صفوف Engram الخاصة بكل rank للـNVMe المحلي ديال كل عقدة، شغّل:

./start.sh pack

التجميع كيكتب تقريباً 94 GiB فكل عقدة ملي كيكون حجم التوازي tensor هو جوج. هاد العملية اختيارية وماشي ضرورية للإقلاع. فالتجارب المذكورة، ملفات Engram المجمّعة محلياً حسّنت أداء prefill بحوالي 25 حتى 50 فالمائة، حيث الصفوف اللي ما تلقاوش فالكاش استعملات direct I/O محلي عوض NFS.

خص صفوف Engram تبقى مْعنونة بالنوع fp8_e4m3fn ملي كتدوز لـkernel ديال نزع التكميم. التعامل معاها على أساس uint8 يقدر يعطي مخرجات باينة معقولة ولكن غالطة.

دير اختبارات التحقق

المستودع فيه فحوصات على مستوى المصدر وما كتحتاجش PyTorch ولا 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

اختبار تطابق قالب الدردشة كيحتاج transformers وencoding/encoding.py ديال الموديل الأصلي:

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

من بعد ما تكون الـAPI خدامة، نفّذ اختبار smoke من البداية للنهاية:

bash tests/test_smoke.sh

اختبار smoke كيطلب من الموديل يحسب 17*19 وكيستنى 323. قبل ما يطلق الخدمة، start.sh كيدير حتى هو فحوصات تطابق GPU ونزع تكميم Engram فكونتينر مؤقت. النتائج كتكتب فـlogs/overlay-verify.log.

نصائح تشغيلية متقدمة

  • فضّل DSpark مع ستريم ولا جوج: معدل فكّ الترميز المبلّغ عليه لستريم واحد كان 31.6 توكن فالثانية، وبمجموع 42.5 توكن فالثانية عبر جوج ستريمات.
  • عطّل التوقّع مع الدفعات الواسعة: مع SPEC_METHOD=none، هبط الصبيب ديال ستريم واحد، ولكن الصبيب الإجمالي ديال أربعة ستريمات وصل لـ53.7 توكن فالثانية فالاختبار المبلّغ عليه.
  • استعمل Engram مع البرومبتات الطويلة اللي كتعاود: ملفات الصفوف المحلية فـNVMe كتنقص الاعتماد على تصدير NFS ديال العقدة الرئيسية، وقد تحسّن سرعة الـprefill بشكل واضح.
  • خلّي النشر نصّي فقط: نطاق نواة SM120 ما كيوفّرش نواة sparse-MLA المطلوبة لعرض vision-token ديال الموديل.
  • ما تعاودش تفعّل ستريمات EXL3 الإضافية بلا تروّي: النشر كيسيرياليز ستريمات EXL3 حيث جوج نوى خدامين فستريمات CUDA مختلفين يقدرو يديرو deadlock مع القفل الواحد فكل جهاز اللي كيستعملو الـruntime.
  • راقب واجهات الشبكة الصحيحة: NCCL ما يقدرش يستعمل aliases ديال loopback من نوع 10.0.0.x. الاختيار الغلط ديال GID لكل NIC يقدر يسبب الفشل من بعد تقريباً دقيقة، مع ibv_modify_qp errno 61.
  • فتّش فذاكرة الـprefill الطويل: نجاح الإقلاع والاختبارات القصيرة ما كيثبتوش باللي pool أكبر ديال KV ولا إعداد context آمن.
  • استعمل المشغّل ديال المستودع: كينسّق المزامنة عبر NFS ولا محلياً، وفحوصات ما قبل التشغيل، والتحقق من الصورة، والإقلاع بجوج الرتب، والتشخيصات.

الأداء المتوقّع

القياسات المنشورة كتشير لـ31.6 توكن فالثانية فستريم واحد ديال فكّ الترميز، و42.5 توكن فالثانية كمجموع جوج ستريمات. الـprefill ديال البرومبتات القصيرة كان تقريباً بين 965 و1,041 توكن فالثانية مع بيانات Engram مكدّسة ونوى الخبراء المجمّعة.

الـprefill ديال البرومبتات الطويلة كيولي أبطأ بطبيعة الحال. من الأمثلة المبلّغ عليها تقريباً 979 توكن فالثانية مع برومبت فيه 100,000 توكن، و802 توكن فالثانية مع 455,000 توكن، و810 توكن فالثانية مع 601,000 توكن. النتائج الفعلية كتتبدّل حسب شكل البرومبت، وإعداد التخزين، والتوقّع، وعرض الدفعة، وحجم قطعة الـprefill المهيّأ.

الخلاصة

هاد المشروع كيجمع النوى المتخصصة، ومحمّل EXL3، وتنفيذ Engram المعتمد على الملفات، وإعداد الشبكة، وضوابط الذاكرة، والمشغّل اللازمين باش يتخدم DeepSeek V4.1 Flash على جوج GB10 DGX Sparks. بدا بالإعدادات اللي جاية مع المشروع، وتحقّق من الـAPI باستعمال اختبار الدخان، ومن بعد بدّل إعداد واحد فكل مرة. ولأكثر تشغيل آمن، قيّم الصبيب والذاكرة المتاحة بجوج خلال أحمال واقعية ديال الـprefill الطويل.