أذكرها وكأنها البارحة، ليلة شتاء قارسة، والساعة تجاوزت الثانية صباحاً. لم يكن البرد في الخارج هو ما يقلقني، بل “الحرارة” الصادرة من لوحة تحكم استهلاك الموارد السحابية (Cloud Dashboard). كانت الأرقام تتصاعد كأنها في سباق ماراثون، والتكاليف تحرق الميزانية المخصصة للمشروع دون رحمة. كل هذا بسبب نموذج جديد لتصنيف الصور أطلقناه في الإنتاج. النموذج كان ذكياً ودقيقاً في مرحلة التطوير، لكن في العالم الحقيقي، كان ثقيلاً، بطيئاً، وكل استدعاء له (API call) كان يكلفنا “شروة مصاري”.
وصلني بريد إلكتروني من مدير المنتج، عنوانه “URGENT: Latency issues”. في داخله، شكاوى من المستخدمين بأن الميزة الجديدة بطيئة جداً وتجعل التطبيق “يعلّق”. شعرت بالدم يغلي في عروقي، يا زلمة، كل هذا التعب والجهد وفي النهاoconut يشتكي المستخدم من البطء؟ جلست على مكتبي، أنظر إلى الكود وأرقام الأداء، وأتمتم: “شو هالحكي؟ لازم نلاقي حل!”. وبينما كنت أقلّب في أوراقي وأبحث في ذاكرتي المنهكة، لمعت في ذهني كلمة واحدة: Quantization.
المشكلة: وحوش جميلة لكنها ثقيلة وبطيئة
في عالم الذكاء الاصطناعي، نحن نحب النماذج الكبيرة والعميقة. نماذج مثل BERT و GPT و ResNet قوية جداً ودقيقة بشكل مذهل. لكن هذه القوة تأتي بثمن. هذه النماذج هي “وحوش” بالمعنى الحرفي للكلمة:
- حجم ضخم: يمكن أن تصل أحجامها لمئات الميغابايتات أو حتى عدة غيغابايتات. هذا يعني استهلاكاً عالياً للذاكرة (RAM) ومساحة التخزين.
- زمن استجابة طويل (High Latency): تحتاج إلى وقت طويل لإجراء عملية استدلال (Inference) واحدة. تخيل أن ينتظر المستخدم 3-4 ثوانٍ ليرى نتيجة بحث أو توصية! هذه كارثة لتجربة المستخدم.
- تكاليف تشغيل مرتفعة: لتشغيل هذه الوحوش بسرعة مقبولة، تحتاج إلى وحدات معالجة رسومية (GPUs) قوية ومكلفة. تشغيل هذه الأجهزة 24/7 يستهلك ميزانية أي شركة، كبيرة كانت أم صغيرة.
باختصار، النموذج الذي يبدو رائعاً على لابتوب المطور بمعالج i9 و GPU من فئة RTX، قد يتحول إلى كابوس في بيئة الإنتاج الحقيقية التي تخدم آلاف المستخدمين في نفس اللحظة.
الحل السحري: ما هو التحويل الكمي (Quantization)؟
تخيل أنك رسام تستخدم مجموعة كاملة من الألوان تحتوي على ملايين الدرجات المختلفة (مثل 3.14159265…). رسوماتك ستكون دقيقة جداً، لكنك ستحتاج إلى مرسم ضخم وكمية هائلة من الألوان. الآن، تخيل أنك قررت تبسيط عملك واستخدام مجموعة محدودة من الألوان الأساسية (مثل 3، 1، 4، 2…). قد تفقد بعض التفاصيل الدقيقة جداً التي لا يلاحظها معظم الناس، لكنك ستتمكن من الرسم بشكل أسرع بكثير وباستخدام أدوات أبسط وأرخص.
هذا هو جوهر التحويل الكمي. إنه عملية تقليل “دقة” الأرقام المستخدمة داخل نموذج الذكاء الاصطناعي.
بشكل تقني، معظم النماذج يتم تدريبها باستخدام أرقام الفاصلة العائمة ذات الدقة 32 بت (Floating-Point 32-bit أو FP32). التحويل الكمي يقوم بتحويل هذه الأوزان (weights) إلى أنواع بيانات أقل دقة، وأشهرها الأعداد الصحيحة ذات 8 بت (Integer 8-bit أو INT8).
لماذا هذا التحويل فعال جداً؟
- حجم أصغر: الانتقال من 32 بت إلى 8 بت يعني أن حجم النموذج سيقل بنسبة تصل إلى 75% (تقليل بمقدار 4 أضعاف). هذا يقلل من استهلاك الذاكرة بشكل كبير.
- سرعة أعلى: معظم المعالجات الحديثة (CPUs) وحتى الـ GPUs أسرع بكثير في إجراء العمليات الحسابية على الأعداد الصحيحة (INT8) مقارنة بأرقام الفاصلة العائمة (FP32). هذا يعني زمن استجابة أقل بكثير.
- استهلاك طاقة أقل: العمليات الأبسط تستهلك طاقة أقل، وهذا مهم جداً في الأجهزة المحمولة (Edge AI) وفي مراكز البيانات لتقليل التكاليف.
التحويل الكمي يشبه ضغط ملف MP3. قد تفقد بعض الترددات التي لا تسمعها الأذن البشرية، لكنك تحصل على ملف أصغر بكثير يعمل على أي جهاز.
يلا نشتغل: كيف نطبق التحويل الكمي عملياً؟
هناك طريقتان رئيسيتان لتطبيق التحويل الكمي، سأشرحهما مع مثال بسيط باستخدام مكتبة PyTorch الشهيرة.
الطريقة الأولى: التحويل الكمي بعد التدريب (Post-Training Quantization – PTQ)
هذه هي الطريقة الأسهل والأسرع. تأخذ نموذجاً مدرباً مسبقاً، وتقوم بتحويله إلى نسخة “مكمّاة” (Quantized) دون الحاجة لإعادة التدريب. إنها مثالية عندما لا يكون لديك وصول إلى بيانات التدريب الأصلية أو عندما تريد حلاً سريعاً.
لنفترض أن لدينا نموذج ResNet18 مدرب مسبقاً من مكتبة torchvision.
import torch
import torchvision.models as models
import os
# الخطوة 1: تحميل النموذج الأصلي المدرب مسبقاً
original_model = models.resnet18(pretrained=True)
original_model.eval() # مهم جداً: تحويل النموذج إلى وضع الاستدلال
# لنرى حجم النموذج الأصلي
def print_model_size(model, label):
torch.save(model.state_dict(), "temp.p")
size_mb = os.path.getsize("temp.p") / 1e6
print(f"حجم النموذج ({label}): {size_mb:.2f} MB")
os.remove("temp.p")
print_model_size(original_model, "الأصلي FP32")
# الخطوة 2: تطبيق التحويل الكمي الديناميكي (أبسط أنواع PTQ)
# يتم تحويل الأوزان إلى INT8، بينما تتم الحسابات ديناميكياً
quantized_model = torch.quantization.quantize_dynamic(
model=original_model, # النموذج الذي نريد تحويله
qconfig_spec={torch.nn.Linear}, # تحديد الطبقات التي سيتم تحويلها (هنا الطبقات الخطية فقط)
dtype=torch.qint8 # نوع البيانات المستهدف (8-bit integer)
)
# الخطوة 3: مقارنة حجم النموذج الجديد
print_model_size(quantized_model, "المكمّى INT8")
# يمكنك الآن استخدام quantized_model للاستدلال بنفس طريقة النموذج الأصلي
# input_tensor = torch.randn(1, 3, 224, 224)
# output = quantized_model(input_tensor)
النتيجة المتوقعة: ستلاحظ أن حجم النموذج قد انخفض من حوالي 44 ميغابايت إلى حوالي 15-20 ميغابايت. هذا انخفاض هائل بمجرد بضعة أسطر من الكود!
الطريقة الثانية: التدريب المدرك للتحويل الكمي (Quantization-Aware Training – QAT)
في بعض الأحيان، قد يؤدي PTQ إلى انخفاض ملحوظ في دقة النموذج. هنا يأتي دور QAT.
في هذه الطريقة، نقوم بمحاكاة أخطاء التقريب (quantization errors) أثناء عملية التدريب أو أثناء عمل fine-tuning إضافي. هذا يسمح للنموذج بأن “يتعلم” كيف يتأقلم مع الدقة المنخفضة، مما يحافظ على دقة عالية جداً بعد التحويل الفعلي.
هذه الطريقة أكثر تعقيداً وتتطلب وقتاً أطول، لكنها تعطي أفضل النتائج من حيث الموازنة بين الدقة والأداء.
تطبيقها في PyTorch يتطلب خطوات إضافية مثل إعداد `qconfig` وإدخال وحدات `QuantStub` و `DeQuantStub` في تعريف النموذج، ثم استخدام `torch.quantization.prepare_qat` و `torch.quantization.convert`. إنها عملية متقدمة لكنها تستحق العناء للنماذج الحساسة.
نصائح أبو عمر من أرض المعركة
بعد سنوات من التعامل مع هذه “الوحوش”، تعلمت بعض الدروس بالطريقة الصعبة. إليكم “الزبدة”:
نصيحة 1: قِس، ثم قِس، ثم قِس مرة أخرى!
لا تفترض أبداً أن التحويل الكمي سيعمل بشكل مثالي. قبل وبعد تطبيق التحويل، يجب أن تقيس ثلاثة أشياء أساسية:
- حجم النموذج (Model Size): أسهل مقياس.
- الأداء (Latency/Throughput): كم من الوقت يستغرق الاستدلال؟ كم عدد الطلبات التي يمكن معالجتها في الثانية؟
- الدقة (Accuracy): هل انخفضت دقة النموذج على مجموعة بيانات التحقق (validation set)؟ إذا كان الانخفاض كبيراً (مثلاً أكثر من 1-2%)، فقد تحتاج إلى استخدام QAT.
لا تثق على العمياني، الأرقام لا تكذب.
نصيحة 2: ابدأ بالبسيط ثم تعقّد
دائماً ابدأ بأسهل طريقة: Post-Training Dynamic Quantization. إنها سريعة وسهلة التطبيق. إذا كانت النتائج (الدقة والأداء) مرضية، فمبارك! لقد وفرت على نفسك الكثير من الوقت.
إذا لم تكن النتائج جيدة، انتقل إلى Post-Training Static Quantization (التي تتطلب “معايرة” النموذج بمجموعة صغيرة من البيانات). إذا استمرت مشكلة الدقة، عندها فقط استثمر وقتك وجهدك في Quantization-Aware Training (QAT).
نصيحة 3: الأداة المناسبة للمهمة المناسبة
ليست كل أدوات التحويل الكمي متشابهة. كل منصة لها أدواتها ونقاط قوتها:
- PyTorch/TensorFlow: يوفران أدوات مدمجة قوية (PTQ و QAT) ممتازة للتطوير والبحث.
- TensorRT (من NVIDIA): متخصص في تحسين النماذج للعمل بأقصى سرعة على وحدات معالجة NVIDIA.
- ONNX Runtime: أداة رائعة ومحايدة. يمكنك تحويل نموذجك إلى صيغة ONNX ثم استخدام أدوات التحويل الكمي الخاصة بها لنشره على منصات مختلفة (CPU, GPU, …).
- TensorFlow Lite: الحل الأمثل إذا كنت تستهدف أجهزة أندرويد أو المتحكمات الدقيقة (Microcontrollers).
اختر الأداة التي تناسب بيئة النشر (Deployment Target) النهائية لنموذجك.
الخلاصة… والزبدة 🏁
في تلك الليلة، وبعد تطبيق التحويل الكمي على نموذجنا، كانت النتائج مذهلة. انخفض زمن الاستجابة بنسبة 70%، وانخفض حجم النموذج بمقدار 4 أضعاف، والأهم من ذلك، انخفضت فاتورة السحابة بشكل كبير. لقد “روّضنا” الوحش بنجاح.
التحويل الكمي (Quantization) ليس مجرد تقنية متقدمة للمختصين، بل هو أداة أساسية في صندوق أدوات كل مهندس تعلم آلة و MLOps. إنه الخط الفاصل بين نموذج يبقى حبيس الأبحاث، ونموذج يرى النور في منتج حقيقي يخدم ملايين المستخدمين بكفاءة وفعالية.
فلا تخف من نماذجك الكبيرة، بل تعلم كيف تروّضها. 😉