تحسين أداء نماذج الترانسفورمر باستخدام Hugging Face Optimum و ONNX Runtime والكمية

يُقدم هذا المقال شرحًا عمليًا لكيفية استخدام مكتبة Hugging Face Optimum لتحسين أداء نماذج الترانسفورمر وزيادة سرعتها مع الحفاظ على دقة النتائج. سنتناول في هذا الشرح خطوات عملية تبدأ من تهيئة نموذج DistilBERT على مجموعة بيانات SST-2، ثم مقارنة محركات التنفيذ المختلفة، بما في ذلك PyTorch العادي و torch.compile و ONNX Runtime، بالإضافة إلى تقنية الكمية (Quantization) لـ ONNX. سنتعلم خلال هذا المقال كيفية تصدير النموذج، وتحسينه، وكميته، وقياس أدائه، كل ذلك داخل بيئة Google Colab. (يمكنكم الاطلاع على الشفرة الكاملة هنا).

1. الإعداد والتهيئة:

  • تثبيت المكتبات: نبدأ بتثبيت المكتبات اللازمة باستخدام الأمر التالي:
pip -q install "transformers>=4.49" "optimum[onnxruntime]>=1.20.0" "datasets>=2.20" "evaluate>=0.4" accelerate
  • تهيئة البيئة: نقوم بتحديد المسارات، وحجم الدفعات (Batch Size)، وإعدادات التكرار، والتأكد من استخدام وحدة المعالجة المركزية (CPU) أو وحدة معالجة الرسومات (GPU):
from pathlib import Path
import os, time, numpy as np, torch
# ... باقي الكود ...

2. تحميل البيانات ومعالجة النصوص:

  • تحميل مجموعة البيانات: نقوم بتحميل جزء من بيانات SST-2 للتحقق:
ds = load_dataset("glue", "sst2", split="validation[:20%]")
texts, labels = ds["sentence"], ds["label"]
metric = evaluate.load("accuracy")
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
  • إنشاء الدفعات: يتم تعريف دالة make_batches لإنشاء دفعات من البيانات لمعالجتها بكفاءة:
def make_batches(texts, max_len=MAXLEN, batch=BATCH):
    # ...
  • قياس الدقة ووقت التنفيذ: يتم تعريف دالتين run_eval و bench لقياس دقة النموذج ووقت تنفيذه على التوالي:
def run_eval(predict_fn, texts, labels):
    # ...

def bench(predict_fn, texts, n_warm=N_WARM, n_iters=N_ITERS):
    # ...

3. قياس أداء PyTorch:

  • النموذج الأساسي: نقوم بتحميل نموذج PyTorch الأساسي وتحديد دالة pt_predict للتنبؤ:
torch_model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID).to(DEVICE).eval()

@torch.no_grad()
def pt_predict(toks):
    # ...
  • تحسين الأداء باستخدام torch.compile: نحاول استخدام torch.compile لتحسين الأداء، وقياس الأداء قبل وبعد استخدام هذه الخاصية:
compiled_model = torch_model
compile_ok = False
try:
    compiled_model = torch.compile(torch_model, mode="reduce-overhead", fullgraph=False)
    compile_ok = True
except Exception as e:
    print("torch.compile unavailable or failed -> skipping:", repr(e))

@torch.no_grad()
def ptc_predict(toks):
    # ...

4. استخدام ONNX Runtime:

  • تصدير النموذج إلى ONNX: نقوم بتصدير النموذج إلى صيغة ONNX باستخدام ORTModelForSequenceClassification:
ort_model = ORTModelForSequenceClassification.from_pretrained(MODEL_ID, export=True, provider=provider, cache_dir=ORT_DIR)
  • الكمية الديناميكية: نقوم بتطبيق الكمية الديناميكية باستخدام ORTQuantizer لتحسين الأداء:
Q_DIR.mkdir(parents=True, exist_ok=True)
quantizer = ORTQuantizer.from_pretrained(ORT_DIR)
qconfig = QuantizationConfig(approach="dynamic", per_channel=False, reduce_range=True)
quantizer.quantize(model_input=ORT_DIR, quantization_config=qconfig, save_dir=Q_DIR)
ort_quant = ORTModelForSequenceClassification.from_pretrained(Q_DIR, provider=provider)
  • قياس الأداء: نقوم بقياس أداء النموذج بعد تصديره إلى ONNX وقبل وبعد تطبيق الكمية:
@torch.no_grad()
def ort_predict(toks):
    # ...

@torch.no_grad()
def ortq_predict(toks):
    # ...

5. مقارنة النتائج:

  • التنبؤات: نقوم بعرض بعض الأمثلة للتنبؤات من نماذج PyTorch و ONNX للتأكد من صحة النتائج.

  • جدول المقارنة: نقدم جدولًا يوضح مقارنة الأداء بين المحركات المختلفة من حيث السرعة والدقة:

import pandas as pd
rows = [["PyTorch eager", pt_ms, pt_sd, pt_acc], ["ONNX Runtime", ort_ms, ort_sd, ort_acc], ["ORT Quantized", oq_ms, oq_sd, oq_acc]]
if compile_ok:
    rows.insert(1, ["torch.compile", ptc_ms, ptc_sd, ptc_acc])
df = pd.DataFrame(rows, columns=["Engine", "Mean ms (↓)", "Std ms", "Accuracy"])
display(df)

6. ملاحظات إضافية:

  • نقوم بتقديم بعض الملاحظات حول كيفية تحسين الأداء أكثر، مثل استخدام نماذج FlashAttention2 أو FP8 مع TensorRT-LLM، أو ضبط عدد الخيوط على وحدة المعالجة المركزية.

في الختام، يوضح هذا المقال كيف تساعدنا مكتبة Hugging Face Optimum على تحسين أداء نماذج الترانسفورمر وجعلها جاهزة للإنتاج، من خلال استخدام ONNX Runtime وتقنية الكمية، مع الحفاظ على دقة النتائج. كما نوضح كيفية استخدام torch.compile لتحسين الأداء مباشرةً ضمن بيئة PyTorch. هذا النهج العملي يوفر توازنًا بين الأداء والكفاءة لنماذج الترانسفورمر، ويوفر أساسًا يمكن توسيعه باستخدام محركات خلفية متقدمة مثل OpenVINO أو TensorRT. (يمكنكم الاطلاع على الشفرة الكاملة هنا).

المصدر: MarkTechPost