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

الوحدة 8 — التحويل بالدفعات للأحجام الكبيرة

الوحدة السابقة اختصّت بالتنبّؤ في الوقت الحقيقيّ. لكن نصف مشاريع تعلّم الآلة لا تحتاج ميلي‌ثوانٍ، بل تحتاج إلى معالجة مليون سطر مرّة في الشهر. لخيطنا الأحمر: التسويق يريد فاتورة تسرّب شهريّة لكلّ عميل، مسبَّقة الحساب في ملفّ Parquet يقرأه CRM. هذه هي الحالة الطبيعيّة لـBatch Transform.

متى نفضّل الدفعة على نقطة النهاية

قاعدة القرار بسيطة، لكنّها تُنسى كثيرًا:

المعيارنقطة نهاية دائمةBatch Transform
زمن الاستجابة المطلوبميلي‌ثوانٍدقائق أو ساعات
تكرار الاستعمالمستمرّدفعة كلّ فترة (يوم، أسبوع، شهر)
الحجم لكلّ استدعاءسطر أو بضعة سطورآلاف إلى ملايين السطور
المستهلكتطبيق ويب، جوّالجدول قاعدة بيانات، CRM
الفاتورة الشهريّة النمطيّة74 دولارًا لآلة صغيرة1-5 دولارات لتشغيل واحد

الفرق العمليّ: نقطة النهاية تدفع فيها ساعات الفراغ، أمّا الدفعة فتدفع فقط الدقائق التي عمل فيها العتاد فعلًا.

لخيطنا الأحمر: 500 ألف عميل، تسجيل شهريّ، لا حاجة لبيانات فوريّة قبل الحملة → دفعة.

أوّل Batch Transform

الفكرة نفسها: Transformer بدل Predictor. من نموذج مدرَّب:

transformer = sk_estimator.transformer(
instance_count=1,
instance_type="ml.m5.xlarge",
output_path=f"s3://{bucket}/churn/predictions/2024-07/",
strategy="MultiRecord", # نرسل عدّة سطور في نداء واحد
max_payload=6, # ميغابايت لكلّ نداء داخليّ
accept="text/csv",
)

transformer.transform(
data=f"s3://{bucket}/churn/scoring-input/2024-07/customers.csv",
content_type="text/csv",
split_type="Line",
join_source="Input", # الأصل مع المُخرَج (مفتاح للربط)
input_filter="$[1:]", # نتجاهل العمود الأوّل (المعرّف) عند الحساب
output_filter="$[0,-1]", # في المُخرَج: المعرّف والتوقّع فقط
)
transformer.wait()

النتيجة على S3: customers.csv.out يحتوي المعرّف والتوقّع، وشيء لا شيء غير ذلك.

SplitType وJoinSource: مفتاحان لا ثالث لهما

نداءان تقنيّان يصعبان على المبتدئين، ويستحقّان توضيحًا:

SplitType: كيف يقسم SageMaker ملفّ الدخل عند إرساله للنموذج.

  • None: يرسل الملفّ كلّه كمصفوفة واحدة. مقبول للملفّ الصغير.
  • Line: يقسم بناءً على السطور، ويرسل حزمًا صغيرة. الأصحّ في 90٪ من الحالات.
  • RecordIO: صيغة ثنائيّة خاصّة، للأداء العالي في بعض الحاويات المدمجة.

JoinSource="Input": يعيد إلحاق كلّ سطر دخل بمُخرَجه. بدونه، يحصل الفريق على ملفّ توقّعات بدون مفتاح، فيصير التوصيل بCRM مستحيلًا. مع input_filter وoutput_filter (صيغة JMESPath)، تتحكّم في أعمدة الدخل التي تدخل النموذج والأعمدة الأخيرة الظاهرة في المُخرَج.

الفخّ الأشيع: نسيان JoinSource، ثمّ إعادة الحساب أسبوعًا بعد فترة لإدراك أنّ الملفّ الناتج بلا مفتاح ربط.

توزيع المهمّة على عدّة نُسخ

Batch Transform يوزّع الملفّ تلقائيًّا. instance_count=4 يعني أنّ SageMaker يقسم الدخل إلى أربع قطع، ويشغّل النموذج على كلّ قطعة على آلة منفصلة، ثمّ يجمع الملفّات الناتجة تحت مسار الخرج نفسه. للملفّ الواحد بحجم غيغابايت، هذه المضاعفة تخفّض الزمن أربع مرّات تقريبًا.

لكن الأداة ليست ذكيّة إلى حدّ الاعتبار الاقتصاديّ:

  • 4 × ml.m5.xlarge لعشر دقائق ≈ 0.15 دولار.
  • 1 × ml.m5.xlarge لأربعين دقيقة ≈ 0.15 دولار.

الفاتورة نفسها، لكنّ الوقت مختلف. اختر الوقت الذي يناسب موعدك، لا الأداء الأقصى بلا سبب.

معالجة أوّليّة داخل نفس المهمّة

كثيرًا ما يحتاج مُدخَل التسجيل إلى معالجة (تصفية، حساب متغيّرات مشتقّة). لا داعي لتشغيل مهمّة معالجة منفصلة: يمكن أن يحدث ذلك داخل سكربت inference.py الذي يرافق النموذج:

# code/inference.py
def input_fn(input_data, content_type):
df = pd.read_csv(io.StringIO(input_data))
df["tenure_months"] = df["tenure_days"] // 30
df = df.drop(columns=["tenure_days"])
return df

def predict_fn(input_data, model):
return model.predict_proba(input_data)[:, 1]

def output_fn(prediction, accept):
return ",".join(f"{p:.4f}" for p in prediction), accept

يُلحق هذا السكربت بالنموذج عبر SKLearnModel(source_dir="code/"). قاعدة ذهبيّة: نفس منطق المعالجة الذي يعمل داخل نقطة نهاية عاديّة يعمل أيضًا داخل Batch Transform. سيرمي هذا نصف الشيفرة المكرَّرة.

الفخّ: تسجيل يوميّ بلا داعٍ

اختبار حقيقيّ للحُكم: يقول شخص «سنشغّل التسجيل كلّ يوم في الرابعة صباحًا احتياطًا». اسأل: ما التغيير الفعليّ في تنبّؤ التسرّب من يوم إلى يوم؟ إن كان الجواب «قد يتغيّر»، فذلك ليس مبرّرًا كافيًا. لخيطنا الأحمر:

  • بيانات العميل تتحدّث شهريًّا مع الفاتورة → تسجيل شهريّ يكفي.
  • حملة تسويق تنطلق شهريًّا → لا حاجة لتوقّع أحدث.

30 تسجيلًا يوميًّا في السنة تكلّف نحو 55 دولارًا؛ تسجيل شهريّ تكلّف 2 دولارًا. الفرق: 53 دولارًا دون أيّ قيمة عمل مضافة. لا تُقرن التكرار بالإخلاص.

تحقّق دائمًا من output_filter

اختبار بسيط قبل الإنتاج: شغّل الدفعة على 100 سطر، افتح output.csv، تأكّد يدويًّا أنّ العمود الأوّل معرّف العميل والأخير التوقّع. اكتشاف الترتيب المقلوب في شهر التسجيل الأوّل يعني إعادة الحساب — ودَينًا للفريق.

الخلاصة

  • Batch Transform يخدم حالات لا تحتاج زمن استجابة قصير: تسجيل شهريّ، تقارير، توليد ميزات.
  • SplitType="Line" وJoinSource="Input" هما الإعدادان اللذان يُعيدان المعرّف مع التوقّع في المُخرَج.
  • التوزيع على عدّة نُسخ يخفض الزمن دون تعديل الفاتورة الإجماليّة؛ اختر ما يناسب موعدك.
  • تسجيل يوميّ لبيانات تتحدّث شهريًّا هدر خالص: اضبط التكرار على وتيرة التغيير الفعليّة.

الوحدة التالية: نُؤتمت كلّ الخطوات — تحضير، تدريب، ضبط، تسجيل، نشر — داخل SageMaker Pipelines.