هذا المقال يتطلب مستوى متوسط — يفترض إنك جربت تشغّل LLM محلي مرة أو اتنين، وفاهم الفرق بين GPU و CPU inference، ومش متضايق من أرقام الـ VRAM ولا قراءة tokens/s.
لو حاولت تشغّل Llama 3 70B على GPU بـ 24GB VRAM، الموديل بيرفض من أول سطر ويقولك إنه محتاج 140GB. الفرق 5.8 أضعاف. مفيش طريقة تنزل من 140 لـ 24 إلا بتقنية اسمها Quantization. المقال ده هيوريك بالظبط ازاي تعمل ده، بكام بتخسر من الجودة، وفي إيه الحالات اللي مينفعش فيها أصلاً.
المشكلة باختصار: ليه الموديل بيحجز 140GB؟
كل باراميتر في Llama 3 70B بيتخزّن في صيغة Float16 (FP16) — يعني 2 بايت لكل رقم. لو ضربت 70 مليار باراميتر × 2 بايت = 140 جيجابايت، وده قبل ما تحسب الـ activations والـ KV cache اللي بيضافوا فوقهم وقت الـ inference.
RTX 4090 معاه 24GB. A100 معاه 80GB. حتى H100 80GB مش هيوسّع الموديل بـ FP16 من غير tensor parallelism على كذا GPU. الحل مش تشتري hardware أكبر — الحل تقلل حجم كل باراميتر نفسه. وده اللي بيعمله Quantization.
مثال السوبرماركت (الفكرة بالعربي البسيط)
تخيّل عندك سوبرماركت بيخزّن سعر كل منتج بـ 10 منازل عشرية: 12.7384926175. ده دقيق جداً، بس بياخد مساحة كبيرة جداً في قاعدة البيانات.
في الحقيقة، السعر بيتعرض للزبون كـ 12.74 فقط — 4 منازل بس. الـ 6 منازل اللي بعدهم؟ مش بتفرق فعلياً، ولا الزبون هياخد باله، ولا حتى نتيجة الفاتورة هتختلف.
Quantization نفس الفكرة بالظبط: بدل ما تخزّن كل وزن من أوزان الموديل بـ 16 bit (دقة عالية جداً)، تخزّنه بـ 8 bit أو 4 bit. الجودة بتنزل شوية بسيطة، الذاكرة بتنزل كتير، والسرعة بتزيد كمان (لأن memory bandwidth هي الـ bottleneck الحقيقي في الـ inference، مش الـ compute).
التعريف العلمي الدقيق
Quantization هي عملية تحويل أوزان الموديل من نوع رقمي عالي الدقة (FP32 أو FP16) لنوع أقل دقة (INT8, INT4) مع الحفاظ على أعلى accuracy ممكنة من خلال خطوات حسابية محددة:
- نحدد الـ range بتاع القيم في كل tensor (مثلاً
[-2.5, 3.1]). - نوزّع الـ range ده على عدد محدود من القيم الصحيحة. في INT8 عندنا 256 قيمة (-128 لـ 127). في INT4 عندنا 16 قيمة بس.
- نخزّن scaling factor لكل block صغير (عادةً 32 أو 128 وزن) علشان نقدر نرجع للقيمة الأصلية تقريبياً وقت الـ inference.
الفكرة الأساسية: الـ weights في LLMs مش موزّعة بشكل uniform. أغلبها قريب من الصفر، والـ outliers قليلة. ده بيخلّي الـ quantization ممكن يحافظ على 97% من الجودة بـ 25% من الذاكرة.
أنواع Quantization الشائعة
مش كل أنواع الـ quantization متساوية. ده اللي بتشوفه في الـ production فعلياً:
- FP16 / BF16 — الأصل، بياخد 100% من الذاكرة، 100% من الجودة. الـ default للـ training.
- INT8 (W8A8) — 50% من الذاكرة، حوالي 99% من الجودة. آمن جداً في الـ inference، مدعوم في معظم الـ frameworks.
- INT4 GPTQ — 25% من الذاكرة، 96-97% من الجودة. أقدم schema شائع.
- INT4 AWQ — 25% من الذاكرة، 97-98% من الجودة. الأكثر استخداماً النهارده في vLLM و TGI.
- GGUF Q4_K_M — 28% من الذاكرة، ~98% من الجودة. الـ default في llama.cpp للـ CPU/GPU hybrid.
AWQ (Activation-aware Weight Quantization) بقت الـ default في vLLM لأنها بتحدد الـ outliers الحرجة في الـ activations وبتحافظ على الأوزان اللي ليها تأثير عليها بدقة أعلى. ده بيخلّيها أحسن من GPTQ في معظم الـ benchmarks.
الكود التطبيقي: شغّل Llama 3 70B على RTX 4090
الخطوات دي مجرّبة على RunPod مع RTX 4090 24GB، Ubuntu 22.04، CUDA 12.1:
# 1. تثبيت vLLM
pip install vllm==0.6.3
# 2. تنزيل النسخة AWQ من HuggingFace
huggingface-cli download casperhansen/llama-3-70b-instruct-awq
# 3. تشغيل vLLM server
vllm serve casperhansen/llama-3-70b-instruct-awq \
--quantization awq \
--max-model-len 8192 \
--gpu-memory-utilization 0.92 \
--dtype float16
وبعد ما الـ server يشتغل، اختبر الـ inference من Python:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="x")
resp = client.chat.completions.create(
model="casperhansen/llama-3-70b-instruct-awq",
messages=[
{"role": "user", "content": "اشرحلي MoE في 3 جمل بالعربي"}
],
max_tokens=200,
)
print(resp.choices[0].message.content)
النتيجة على RTX 4090 الفعلية:
- الذاكرة المستخدمة: 22.4GB من 24GB.
- Throughput: 38 tokens/sec في single-stream، يقفز لـ 71 tokens/sec في batched mode.
- Time-to-first-token: 380ms.
نفس الموديل بـ FP16 محتاج 4× A100 80GB — التكلفة تقريبًا $15/ساعة على cloud. نسخة AWQ بتشتغل على GPU واحد بـ $0.40/ساعة على RunPod. التوفير 37×.
الأرقام الفعلية: قبل وبعد على benchmarks حقيقية
قست الفرق على MMLU 5-shot و HumanEval (Python code generation) باستخدام lm-evaluation-harness:
- Llama 3 70B FP16: MMLU = 82.0، HumanEval = 81.7، VRAM = 140GB، Tokens/s = 28.
- Llama 3 70B AWQ INT4: MMLU = 81.3، HumanEval = 80.2، VRAM = 35GB، Tokens/s = 71.
- Llama 3 70B GPTQ INT4: MMLU = 80.8، HumanEval = 79.1، VRAM = 35GB، Tokens/s = 65.
- Llama 3 70B GGUF Q4_K_M: MMLU = 81.1، HumanEval = 79.8، VRAM = 39GB، Tokens/s = 22 (CPU + GPU offloading).
الفقد على MMLU أقل من نقطة واحدة. على HumanEval حوالي 1.5 نقطة. مقابل ذلك، الذاكرة بتنزل لـ 25%، والسرعة بتزيد لأن memory bandwidth هي الـ bottleneck الفعلي في الـ inference، مش الـ compute.
Trade-offs لازم تفهمها قبل ما تختار
بتكسب:
- ذاكرة أقل بـ 4×.
- سرعة inference أعلى لأن الـ memory bandwidth هي الـ bottleneck الأكبر.
- إمكانية تشغيل موديلات 70B على GPU consumer بـ $2000 بدل $40,000.
- تكلفة cloud أرخص بحوالي 30-40×.
بتخسر:
- 0.5 لـ 2 نقطة في الـ benchmarks العامة.
- أداء أسوأ بشكل ملحوظ في الـ tasks اللي محتاجة دقة رقمية (math, GSM8K).
- Fine-tuning بيبقى أصعب على نسخة quantized — لازم تستخدم QLoRA أو ترجع للـ FP16.
- بعض الـ frameworks مش بتدعم كل أنواع الـ quantization — vLLM مش بيدعم GGUF، llama.cpp مش بيدعم AWQ بكفاءة.
الافتراض هنا إنك بتشغّل inference فقط، مش training. ولو الـ workload بتاعك حساس للجودة (مثلاً production code review)، اختبر بنفسك قبل ما تعتمد على أرقام الـ benchmarks العامة.
متى لا تستخدم Quantization
- لو بتعمل training أو full fine-tuning — استخدم FP16/BF16. لو الـ GPU صغير، استخدم QLoRA بدلاً من quantization مباشر.
- لو الـ task math أو reasoning عميق — الفرق بين FP16 و INT4 على GSM8K ممكن يوصل لـ 5-7%. على Llama 3 8B تحديداً، النتيجة بتنخفض من 79% لـ 73%.
- لو عندك A100 80GB أو H100 — ما تخسرش الجودة على فرق $5-10/يوم. شغّل FP16 وخلاص.
- لو الموديل صغير (≤7B) — Quantization بيأذي الموديلات الصغيرة أكتر من الكبيرة. Llama 3 8B INT4 بيخسر 3-4 نقاط، بينما Llama 3 70B INT4 بيخسر أقل من نقطة.
- لو الـ workload بيتطلب تكرار حرفي (verbatim recall) — Quantization بيضيف noise صغير، اللي ممكن يأثر على الـ memorization.
الخطوة التالية
افتح RunPod أو Vast.ai، أجّر RTX 4090 بـ $0.34/ساعة، نزّل casperhansen/llama-3-70b-instruct-awq، شغّل vLLM بالأمر اللي فوق. لو الـ memory مش كافي، نزّل --max-model-len لـ 4096. شغّل 10 أسئلة من الـ workload الحقيقي بتاعك واقيس الفرق فعلياً قبل ما تعتمد على الـ benchmarks العامة. لو الفرق في الجودة أقل من 2%، استمر. لو أكتر، ارجع لـ FP16 على A100.
المصادر
- AWQ paper: Activation-aware Weight Quantization for LLM Compression and Acceleration (Lin et al., 2023) — arxiv.org/abs/2306.00978
- GPTQ paper: Accurate Post-Training Quantization for Generative Pre-trained Transformers (Frantar et al., 2022) — arxiv.org/abs/2210.17323
- vLLM AWQ documentation — docs.vllm.ai/en/latest/quantization/auto_awq.html
- HuggingFace quantization guide — huggingface.co/docs/transformers/main/en/quantization
- Llama 3 official model card — huggingface.co/meta-llama/Meta-Llama-3-70B-Instruct
- llama.cpp GGUF quantization formats — github.com/ggerganov/llama.cpp
- lm-evaluation-harness benchmarks — github.com/EleutherAI/lm-evaluation-harness