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

الوحدة 4 — إصدار البيانات والنماذج

استعادة الشفرة عند الالتزام رقم a3f8e21 سهلة: git checkout a3f8e21. لكن استعادة البيانات التي شغّلت التدريب في تلك اللحظة أصعب بكثير. البيانات تُحدَّث كلّ ليلة، والملفّ data/train.csv الذي وجدَتْه شفرة التدريب في يناير ليس نفس الملفّ الذي ستجده في مارس. إن لم نعالج هذا، فإنّ إعادة إنتاج تشغيل قديم مستحيلة، وأيّ ادّعاء «هذا النموذج مصدر أفضل» لا يمكن التحقّق منه.

لماذا لا نضع البيانات في Git مباشرةً

Git صُمّم للملفّات النصّية الصغيرة. عندما نلتزم بملفّ CSV بحجم 500 ميغابايت، يُنشئ كتلة (blob) بنفس الحجم في مستودعنا، وعند كلّ تحديث للبيانات يُخزَّن CSV آخر بأكمله. المستودع ينمو أُسّيًا، الاستنساخ يصبح مستحيلًا، والدفع إلى المستودع البعيد يستغرق ساعات. GitHub يرفض الدفع فوق 100 ميغابايت للملفّ الواحد، فهذا حاجز مادّي.

Git LFS يحلّ جزءًا من المشكلة (يحفظ محتوى الملفّ في تخزين منفصل ويلتزم بمؤشّر فقط)، لكنّه ليس مصمّمًا لدورة حياة البيانات في التعلّم الآلي: لا يعرف مفهوم خطوط الأنابيب (pipelines)، ولا يتتبّع أنّ features.parquet نُتِج من raw.csv بشفرة معيّنة.

DVC: مقدّمة عملية

DVC (Data Version Control) أداة مفتوحة المصدر تُكمل Git. المبدأ: بدلًا من الالتزام بالبيانات، نلتزم بـملفّ وصفي صغير يحتوي على بصمة MD5 للبيانات وموقعها في التخزين البعيد.

dvc add data/train.csv
git add data/train.csv.dvc .gitignore
git commit -m "بيانات تدريب: نسخة يناير 2026"

الأمر الأول ينشئ ملفّ data/train.csv.dvc صغير (بضعة بايتات) يحوي البصمة، ويضيف data/train.csv إلى .gitignore. الأمر الثاني يلتزم بالملفّ الوصفي فقط.

بعد ذلك، dvc push يرفع البيانات إلى التخزين البعيد المُهيّأ (S3، GCS، Azure، Google Drive، خادم SSH). وdvc pull يجلبها من هناك. النتيجة: نمتلك مستودع Git صغير الحجم، ومع ذلك كلّ التزام يشير إلى النسخة الدقيقة من البيانات المستعملة في تلك اللحظة.

ربط الالتزام بالبيانات والنموذج

في تدفّق العمل الكامل، كلّ التزام Git يُعرِّف ثلاثيًا: (شفرة، بيانات، نموذج). في مثال التسرّب لدينا:

git commit a3f8e21
├── src/train.py (شفرة)
├── data/train.csv.dvc (بصمة البيانات، النسخة الحقيقية في S3)
└── models/churn.pkl.dvc (بصمة النموذج، النسخة الحقيقية في S3)

للعودة إلى نموذج قديم:

git checkout a3f8e21
dvc pull

الأمران معًا يستعيدان كلّ الحالة: الشفرة من Git، والبيانات والنموذج من التخزين البعيد. يمكننا حينها إعادة تشغيل التدريب والتحقّق من أنّه يُنتج نفس النتيجة، أو تشخيص خطأ ظهر في الإنتاج.

اللِنَاج: من أين جاء هذا النموذج؟

اللِنَاج (lineage) هو سلسلة العمليات التي أنتجت أصلًا معيّنًا. في مثالنا، نموذج churn.pkl نتج من:

  1. data/raw/customers.csv (استخراج من قاعدة البيانات في يناير)
  2. src/preprocess.py أنتج data/features/train.parquet
  3. src/train.py استخدم train.parquet وأنتج models/churn.pkl

DVC يُمثّل هذه السلسلة في ملفّ dvc.yaml كخطوط أنابيب:

stages:
preprocess:
cmd: python src/preprocess.py
deps:
- data/raw/customers.csv
- src/preprocess.py
outs:
- data/features/train.parquet
train:
cmd: python src/train.py
deps:
- data/features/train.parquet
- src/train.py
outs:
- models/churn.pkl

الآن dvc repro يُعيد تنفيذ الخطوات التي تغيّرت اعتمادياتها فقط. إن غيّرنا preprocess.py، يُعاد تنفيذ الخطوتين. إن غيّرنا train.py فقط، يُعاد تنفيذ التدريب فقط. وهذا يوفّر ساعات على مشاريع تدرّب لعدّة ساعات.

متى نستغني عن DVC

DVC ليس ضروريًا دائمًا. في حالتين ملموستين، قاعدة بيانات مُصدَّرة تكفي:

  • بياناتنا في مستودع علائقي مركزي (PostgreSQL مثلًا)، والتدريب يقرأ باستعلام SQL. نستطيع «تصوير» البيانات بلقطة زمنية SELECT * FROM customers WHERE snapshot_date = '2026-01-15' ونحفظ التاريخ في MLflow. اللقطات الزمنية تُلعب دور DVC، وهذا سليم إذا كانت قاعدة البيانات لا تحذف السجلّات القديمة.
  • بياناتنا في بحيرة (data lake) بصيغة Delta أو Iceberg. هذه الصِيَغ تحتفظ بتاريخ التعديلات وتُتيح استعلامًا زمنيًا (AS OF TIMESTAMP). كذلك تُغني عن DVC للجزء المتعلّق بلقطات القراءة.

في هذه الحالات، DVC مفيد أساسًا للنموذج والمخرجات، لا للبيانات المصدرية.

البيانات الشخصية والامتثال

إصدار البيانات جميل من الناحية التقنية، لكنّه يُنشئ التزامًا قانونيًّا صريحًا: كلّ نسخة تُخزَّن قد تحتوي بيانات شخصية خاضعة للائحة العامّة لحماية البيانات. عند حذف بيانات مستخدم، يجب حذفها من كلّ اللقطات، لا من النسخة الحالية فقط. صمّم منذ اليوم الأول سياسة للاحتفاظ (retention policy) وأداة لحذف الأثر عبر النسخ.

في الخلاصة

  • Git وحده لا يكفي للبيانات لأسباب مادّية (الحجم) وتصميمية (لا يعرف الأنابيب)؛ نستعمل DVC أو مكافئه.
  • كلّ التزام Git يُعرِّف ثلاثيًا (شفرة، بيانات، نموذج) بفضل ملفّات .dvc الصغيرة الملتزمَ بها.
  • خطوط الأنابيب في dvc.yaml تُتيح إعادة تنفيذ الخطوات المتغيّرة فقط وتوفّر ساعات من الحساب.
  • قاعدة بيانات علائقية مُصدَّرة أو بحيرة Delta/Iceberg قد تُغني جزئيًا عن DVC، خصوصًا لبيانات الإدخال.