مستوى المقال: محترف. الشرح موجّه لمن يشغّل نماذج LLM في الإنتاج ويعرف معنى forward pass و KV cache و batch size. لو لسه مبتدئ، في مثال مبسّط في الأول هيوصّلك الفكرة قبل الدخول في التفاصيل.
فك التشفير التخميني: تسريع استدلال LLM بدون أي تنازل عن الجودة
لو بتشغّل نموذج LLM كبير لمستخدم تفاعلي واحد، فك التشفير التخميني (Speculative Decoding) ممكن يديك توليد أسرع مرتين لتلاتة بنفس المخرجات بالظبط — من غير ما تغيّر النموذج ولا تدرّبه من جديد. المقال ده بيوريك إزاي بيشتغل، إعداد قابل للنسخ على vLLM، والرقم الوحيد اللي بيحدّد هتكسب قد إيه.
قبل التفاصيل، مثال يقرّب الفكرة: تخيّل محرّر سريع بيكتب مسودة جملة في ثانية، وخبير بطيء بس دقيق بيراجعها. بدل ما الخبير يكتب كلمة كلمة، المحرّر بيقترح خمس كلمات دفعة واحدة، والخبير بيراجعهم كلهم في نظرة واحدة. لو وافق على أول أربعة وغلّط الخامسة، بياخد الأربعة الصح ويصحّح الخامسة بنفسه. النتيجة: نفس الجودة اللي كان الخبير هيطلعها لو اشتغل لوحده، بس في وقت أقل بكتير. المحرّر هنا هو نموذج المسوّدة، والخبير هو النموذج الكبير.
المشكلة: التوليد التتابعي بيهدر الـ GPU
النموذج التوليدي بيطلّع توكن واحد في كل forward pass. وعشان يطلّع التوكن، لازم يقرأ أوزان النموذج كلها من ذاكرة الـ GPU. ده بيخلّي زمن التوليد محكومًا بعرض نطاق الذاكرة (memory-bound) مش بقدرة الحساب. عند batch size يساوي 1 — يعني مستخدم واحد بيستنى رد — وحدات الحساب في الـ GPU بتقعد فاضية معظم الوقت وانت بتدفع ثمن قراءة 70 مليار parameter لكل توكن.
الافتراض هنا إنك في سيناريو تفاعلي بـ batch صغير. لو بتخدم آلاف الطلبات بالتوازي، القصة مختلفة — وهنرجعلها في قسم "متى لا تستخدمه".
الحل: المسوّدة تقترح، والنموذج الكبير يتحقق
فك التشفير التخميني بيستغل حقيقة إن الـ GPU قادر يتحقق من عدة توكنات في تمريرة واحدة بنفس تكلفة تمريرة واحدة تقريبًا. الخطوات:
- نموذج مسوّدة صغير وسريع (مثلاً Llama-3.2-1B) يولّد K توكن تخمينية — عادة من 4 لـ 8.
- النموذج الكبير (مثلاً Llama-3.1-70B) يمرّر الـ K توكن كلها في forward pass واحد ويحسب توزيع الاحتمالات لكل موضع.
- نقبل أطول بادئة من اقتراحات المسوّدة متوافقة مع النموذج الكبير، عبر خطوة أخذ عيّنة بالرفض المعدّل (modified rejection sampling).
- أول توكن مرفوض نعيد أخذ عيّنته من التوزيع المصحّح. ولو اتقبلت الـ K كلها، ناخد توكن إضافي ببلاش من نفس تمريرة التحقق.
أهم نقطة: الخطوة دي مثبتة رياضيًا إنها بتنتج نفس توزيع النموذج الكبير. يعني المخرجات مطابقة إحصائيًا لو شغّلت النموذج الكبير لوحده. التسريع في الزمن بس، ومفيش أي تنازل عن الجودة (Leviathan وآخرون، ICML 2023).
إعداد قابل للنسخ على vLLM
vLLM بيدعم فك التشفير التخميني أصلًا. أبسط شكل: نموذج مسوّدة من نفس العائلة عبر speculative_config.
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3.1-70B-Instruct",
tensor_parallel_size=4,
speculative_config={
"model": "meta-llama/Llama-3.2-1B-Instruct",
"num_speculative_tokens": 5,
},
)
params = SamplingParams(temperature=0.0, max_tokens=256)
out = llm.generate(["اشرح معنى الـ KV cache في جملتين:"], params)
print(out[0].outputs[0].text)
لو مش عايز تشغّل نموذج مسوّدة تاني عشان توفّر ذاكرة، في طريقة n-gram مجانية بتدوّر على تكرارات داخل الـ prompt نفسه — مفيدة جدًا في التلخيص و RAG واستخراج البيانات اللي بيتكرّر فيها نص الإدخال:
speculative_config = {
"method": "ngram",
"num_speculative_tokens": 4,
"prompt_lookup_max": 4,
}
معدّل القبول هو الرقم اللي بيحدّد كل شيء
التسريع الفعلي بيعتمد على نسبة قبول النموذج الكبير لاقتراحات المسوّدة (acceptance rate). كل ما المسوّدة تشبه الكبير أكتر، التسريع يزيد:
- قبول حوالي 40% → تسريع ضعيف (~1.2×)، وأحيانًا أبطأ من العادي بسبب تكلفة تشغيل المسوّدة.
- قبول 60–80% (النطاق الواقعي العملي) → تسريع 2× لـ 3× (Leviathan 2023؛ توثيق vLLM).
- طرق متقدمة زي EAGLE-3 بتوصّل القبول لحوالي 80% وتسريع يتعدّى 3× (Li وآخرون، 2025). NVIDIA سجّلت حتى 3.6× على H200 بـ TensorRT-LLM.
سيناريو واقعي: شات بوت بـ Llama-3.1-70B على 4×A100، بـ batch=1، مع مسوّدة 1B ومعدّل قبول حوالي 70%. التوليد بينتقل تقريبًا من حوالي 18 توكن/ثانية لحوالي 42 توكن/ثانية. للمستخدم، ده الفرق بين رد بيتقطّع ورد بيسيل بسلاسة. (أرقام تقريبية تعتمد على العتاد وطبيعة الـ workload.)
المقايضات: مفيش حاجة ببلاش
- ذاكرة إضافية: نموذج المسوّدة بياخد VRAM. Llama-3.2-1B بـ bf16 حوالي 2.5GB. الـ trade-off هنا: بتكسب سرعة، بتخسر ذاكرة كانت ممكن تروح لـ KV cache أكبر أو batch أوسع.
- تكلفة لما القبول واطي: لو المسوّدة مش متوافقة مع مجال بياناتك (كود مقابل نص عربي مثلاً)، الـ overhead بتاع التحقق ممكن يخلّيك أبطأ من غير speculative أصلًا.
- تعقيد تشغيلي: بقى عندك نموذجين تحمّلهم وتراقبهم وتحدّثهم بدل واحد.
متى لا تستخدمه
مكسب فك التشفير التخميني الأكبر بيكون عند batch صغير. الحالات اللي ما ينفعش فيها:
- خدمة عالية الإنتاجية بـ batch كبير: عند batch كبير الـ GPU بيبقى مشبع بالحساب أصلًا، فتمريرة التحقق على توكنات زيادة بتضيف overhead من غير مكسب، وممكن تقلّل الـ throughput الكلي.
- ذاكرة ضيقة: لو مفيش VRAM فاضي لنموذج مسوّدة، استخدم طريقة n-gram أو سيبها.
- قبول منخفض ثابت: لو قِست معدّل القبول ولقيته أقل من ~40% ومش بيتحسّن، الإعداد بيضرّك مش بينفعك.
الخطوة التالية
شغّل بنشمارك واحد: قِس توكن/ثانية على إعدادك الحالي، بعدين أضف speculative_config بمسوّدة 1B وnum_speculative_tokens=5 وقِس تاني. لو معدّل القبول طلع فوق 60% والـ latency نزل، خلّيه. لو القبول تحت 40%، جرّب مسوّدة أقرب لمجالك أو حوّل لطريقة n-gram. قارن الرقمين قبل وبعد قبل ما تقرّر.
المصادر
- Leviathan, Kalman, Matias — "Fast Inference from Transformers via Speculative Decoding", ICML 2023 — arXiv:2211.17192.
- Chen et al., DeepMind — "Accelerating Large Language Model Decoding with Speculative Sampling", 2023 — arXiv:2302.01318.
- Li, Wei, Zhang, Zhang — "EAGLE-3: Scaling up Inference Acceleration of LLMs", 2025.
- توثيق vLLM الرسمي — Speculative Decoding — docs.vllm.ai.
- NVIDIA Developer Blog / TensorRT-LLM — Speculative Decoding على H200.