انتقل إلى المحتوى الرئيسي

الوحدة 8 — التوقّع بالدفعات

نقطة النهاية في الوحدة 7 تُجيب على استدعاء واحد بتنبّؤ فوريّ. لكن ليست كلّ الحاجات تحتاج ذلك: في الفيل الأحمر، يُحلَّل ليلًا أرشيف اليوم كاملًا (نحو 12 مليون معاملة) لإعداد تقرير المخاطر الصباحيّ ولتحديث لوحات المراقبة. تشغيل ذلك على نقطة نهاية فوريّة ثقيل ومكلف بلا مبرِّر. التوقّع بالدفعات (Batch Prediction) هو الأداة الصحيحة: يقرأ ملفًّا أو جدول BigQuery، يُوزِّع الحمل على نُسخ مؤقّتة، ويكتب النتائج، ثمّ يتوقّف.

متى دفعات ومتى فوريّ

المقاربتان مكمِّلتان لا متنافستان:

  • الفوريّ (Endpoint): استجابة سريعة على طلب واحد أو بضعة. مثال: قرار السماح بمعاملة بطاقة أثناء الشراء.
  • الدفعات (Batch): معالجة مجموعة كبيرة دفعة واحدة، بلا قيد زمنيّ صارم. مثال: تصنيف كلّ معاملات الأمس لتغذية تقارير الغد؛ إعادة تسجيل كامل قاعدة العملاء بعد إعادة تدريب.

قاعدة القرار: إن كانت النتيجة تُلزَم لاتّخاذ قرار خلال ثوانٍ، فوريّ. وإلّا، دفعات. لأنّ الدفعات دائمًا أرخص للحجم نفسه بعامل من 5 إلى 20.

إطلاق وظيفة دفعات

نستعمل الإصدار المُسجَّل في الوحدة 6:

from google.cloud import aiplatform

aiplatform.init(project="fraud-detection-lab", location="europe-west1")
model = aiplatform.Model("fraud-xgb@production")

job = model.batch_predict(
job_display_name="fraud-batch-2026-09-05",
bigquery_source="bq://fraud-detection-lab.transactions.enriched_yesterday",
bigquery_destination_prefix="bq://fraud-detection-lab.monitoring.batch_predictions",
machine_type="n1-standard-4",
starting_replica_count=4,
max_replica_count=20,
service_account="sa-training@fraud-detection-lab.iam.gserviceaccount.com",
)
job.wait()

يحدث:

  • Vertex يقرأ الجدول المصدر، يُقسِّم البيانات على starting_replica_count نسخة.
  • كلّ نسخة تُشغِّل الحاوية المُخدِّمة نفسها المعرَّفة عند تسجيل النموذج.
  • النتائج تُكتب إلى جدول جديد fraud-detection-lab.monitoring.batch_predictions_<timestamp> مع الأعمدة الأصليّة زائد عمود predictions.
  • الآلات تُدمَّر عند الانتهاء. الفوترة على مدّة النسخ فقط.

يستطيع Vertex التوسّع تلقائيًّا بين starting وmax وفق حجم الحمل المتبقّي.

مقارنة الكلفة

لنقارن على مثال ملموس: 12 مليون تنبّؤ في اليوم.

  • نقطة نهاية دائمة: نُسخة n1-standard-2 واحدة دائمة، تكفي حسابيًّا لكن حاجزة على تسارع طارئ.
    • كلفة: نحو 2 دولار في اليوم × 30 = 60 دولار في الشهر (بلا احتساب الطلبات).
    • زمن استجابة: ممتاز (100 مللي ثانية).
  • دفعات ليليّة: 4 نسخ n1-standard-4 لـ20 دقيقة تكفي لـ12 مليون.
    • كلفة: نحو 0.15 دولار في اليوم × 30 = 4.5 دولار في الشهر.
    • زمن استجابة: النتائج جاهزة الساعة 3 صباحًا.

الفارق 13 ضعفًا لصالح الدفعات. لكن السيناريو ليس بديلًا: نحتاج الاثنَين. نقطة النهاية للقرارات الفوريّة أثناء الشراء، والدفعات لتقارير الصباح ولإعادة تسجيل المجموعة الكاملة عند نشر إصدار جديد.

قراءة النتائج من BigQuery

جدول المخرج له أعمدة الجدول المصدر زائد predictions (مصفوفة قِيَم لكلّ صفّ):

SELECT
transaction_id,
amount,
merchant_country,
y AS true_label, -- إن كان يتوفّر
predictions[OFFSET(0)].scores[OFFSET(1)] AS fraud_prob
FROM `fraud-detection-lab.monitoring.batch_predictions_20260905`
WHERE predictions[OFFSET(0)].scores[OFFSET(1)] > 0.8
ORDER BY fraud_prob DESC
LIMIT 100;

بنية predictions تعتمد على الحاوية المُخدِّمة. للحاوية XGBoost القياسيّة: قائمة قِيَم scores بحجم الفئات. للنموذج ثنائيّ، الفئة 1 (احتيال) هي الاحتمال الذي يهمّنا.

الجدولة

الجدولة اليوميّة تمرّ عبر Vertex AI Pipelines (الوحدة القادمة) أو عبر Cloud Scheduler يُطلق دالّة Cloud Functions تستدعي model.batch_predict(...). الحلّ الأنيق:

# cloud_function/main.py
import functions_framework
from google.cloud import aiplatform

@functions_framework.http
def run_batch(request):
aiplatform.init(project="fraud-detection-lab", location="europe-west1")
model = aiplatform.Model("fraud-xgb@production")
job = model.batch_predict(
job_display_name=f"fraud-batch-{request.args.get('date')}",
bigquery_source=f"bq://fraud-detection-lab.transactions.enriched_{request.args.get('date')}",
bigquery_destination_prefix="bq://fraud-detection-lab.monitoring.batch_predictions",
machine_type="n1-standard-4",
starting_replica_count=4,
max_replica_count=20,
sync=False, # لا نُحبس داخل الدالّة
)
return {"job": job.resource_name}

Cloud Scheduler يستدعي هذه الدالّة كلّ ليلة 2 صباحًا مع ?date=YYYYMMDD. تسجيل الوظيفة يستغرق ثوانٍ؛ الدفعة نفسها تعمل بعدها وتنتهي حين تنتهي، بلا حبس المُجدوِل.

ربط النتائج بالجدول الأصليّ

جدول التنبّؤ يحمل معرِّف كلّ معاملة (transaction_id)، فربطه بالجدول الخام لتغذية لوحة المراقبة:

CREATE OR REPLACE TABLE `fraud-detection-lab.monitoring.daily_review` AS
SELECT
t.transaction_id,
t.card_hash,
t.amount,
t.merchant_country,
p.predictions[OFFSET(0)].scores[OFFSET(1)] AS fraud_prob,
CASE
WHEN p.predictions[OFFSET(0)].scores[OFFSET(1)] > 0.9 THEN 'high'
WHEN p.predictions[OFFSET(0)].scores[OFFSET(1)] > 0.5 THEN 'medium'
ELSE 'low'
END AS risk_bucket
FROM `fraud-detection-lab.transactions.raw` t
JOIN `fraud-detection-lab.monitoring.batch_predictions_20260905` p
USING (transaction_id)
WHERE DATE(t.event_ts) = "2026-09-05";

هذا الجدول اليومي يُقرأ من Looker أو Metabase لبناء لوحة المخاطر التي يفحصها فريق التحكيم صباحًا. جدولته الكاملة (تنبّؤ، ربط، تحديث لوحة) تنتظم داخل خطّ الأنابيب في الوحدة التالية.

قيود يجب معرفتها

  • الحاوية المُخدِّمة نفسها: التوقّع بالدفعات يستعمل الحاوية التي عُرِّفت عند تسجيل النموذج. لا نقدر تعديلها لجرَعة استدلال أخفّ.
  • الحدّ الأدنى لأنشاء الوظيفة: بضع دقائق لتشغيل النُّسخ. لدفعة 1000 صفّ فقط، الوقت الإجماليّ (تشغيل + استدلال + إغلاق) قد يفوق ما ستفعله نقطة نهاية موجودة.
  • الاستدلال دون تحويل: التوقّع بالدفعات لا يُشغّل معالجة مسبقة لك. البيانات المصدر يجب أن تكون في نفس بنية ما دُرِّب عليه النموذج (نفس الأعمدة، نفس الترميز).

النقطة الأخيرة تفرض أن يكون تحويل الميزات (feature engineering) مُنجزًا مسبقًا في جدول enriched_yesterday. عادةً هذا يكون خطوة SQL مجدولة تُشغَّل قبل الدفعة الاستدلاليّة.

انحدار الميزات هو الفخّ الصامت

النموذج دُرِّب على amount / device_avg_amount كميزة نسبيّة. إذا نسي فريق البيانات تحديث حساب device_avg_amount قبل تشغيل الدفعة، النموذج يستلم قيمًا مغلوطة ويعطي تنبّؤات لا معنى لها، بلا أيّ رسالة خطأ. القاعدة: جدول الميزات يجب أن يمرّ اختبار جودة (لا قيم مفقودة أعلى من عتبة، توزيعات ضمن نطاق تاريخيّ) قبل استدعاء التنبّؤ.

دفعة تجريبيّة قبل الجدولة الكاملة

قبل تفعيل الجدولة الليليّة، شغّل دفعة على يوم واحد فحسب من البيانات التاريخيّة الموسومة، وقارن التنبّؤات بالتوسيم الحقيقيّ. يجب أن تبلغ نتيجة القياس تقريبًا نفس النتيجة على مجموعة اختبار الوحدة 5. فارق كبير يعني أنّ الأنبوب يفقد شيئًا في الطريق (ميزة، تحويل، ترميز).

في الخلاصة

  • الدفعات لِما ليس فوريًّا، نقطة النهاية للقرارات الآنيّة؛ الاثنان يعيشان معًا.
  • الدفعات على BigQuery تكتب النتائج في جدول تحت predictions[...]؛ الربط بالجدول الأصليّ يبني لوحة المراقبة.
  • الفارق الاقتصاديّ عشرة أضعاف أو أكثر لصالح الدفعات على الأحمال الكبيرة اليوميّة.
  • الميزات المسبَّقة الحساب يجب أن تصل بجودة موثَّقة قبل التنبّؤ، وإلّا يتلوّث المخرج بصمت.

الوحدة التالية: تنسيق كلّ ذلك — التحويل، التدريب، التقييم، النشر — في Vertex AI Pipelines بمكوّنات KFP.