التحكم في إصدار البيانات والنماذج باستخدام DVC

قابلية إعادة إنتاج التجارب

4 دقيقة للقراءة

القوة الحقيقية لـ DVC تكمن في خطوط الأنابيب—تعريف سير عمل ML بالكامل ككود. شغّل dvc repro وأعد إنشاء أي تجربة بدقة.

مشكلة إعادة الإنتاج

بدون خطوط الأنابيب:

# "كيف درّبت هذا النموذج؟"
python preprocess.py --input raw.csv --output clean.csv  # أي معاملات؟
python train.py --data clean.csv --model rf  # أي معلمات فائقة؟
python evaluate.py --model model.pkl  # أي مقاييس؟
# الترتيب؟ التبعيات؟ الإصدارات؟

مع خطوط أنابيب DVC:

dvc repro  # يُعيد إنشاء كل شيء، تلقائياً

أساسيات خطوط أنابيب DVC

ملف dvc.yaml

خطوط الأنابيب تُعرف في dvc.yaml:

# dvc.yaml
stages:
  preprocess:
    cmd: python src/preprocess.py
    deps:
      - src/preprocess.py
      - data/raw.csv
    outs:
      - data/processed.csv

  train:
    cmd: python src/train.py
    deps:
      - src/train.py
      - data/processed.csv
    params:
      - train.epochs
      - train.learning_rate
    outs:
      - models/model.pkl

  evaluate:
    cmd: python src/evaluate.py
    deps:
      - src/evaluate.py
      - models/model.pkl
      - data/test.csv
    metrics:
      - metrics.json:
          cache: false

المكونات الرئيسية

المكون الغرض مثال
cmd الأمر للتشغيل python train.py
deps تبعيات المدخلات ملفات المصدر، البيانات
outs مخرجات المخرجات النماذج، البيانات المُعالجة
params المعلمات الفائقة معدل التعلم، الحقب
metrics مقاييس التقييم الدقة، درجة F1

ملف المعلمات

خزّن المعلمات الفائقة في params.yaml:

# params.yaml
preprocess:
  test_split: 0.2
  random_seed: 42

train:
  epochs: 100
  learning_rate: 0.001
  batch_size: 32
  model_type: random_forest

evaluate:
  threshold: 0.5

الإشارة في كودك:

# src/train.py
import yaml

with open('params.yaml') as f:
    params = yaml.safe_load(f)

epochs = params['train']['epochs']
lr = params['train']['learning_rate']

تشغيل خطوط الأنابيب

التنفيذ الأساسي

# شغّل خط الأنابيب بالكامل
dvc repro

# شغّل مرحلة محددة
dvc repro train

# أجبر إعادة التشغيل (تجاهل الكاش)
dvc repro --force

التخزين المؤقت الذكي

DVC يُعيد تشغيل المراحل فقط عندما يتغير شيء:

$ dvc repro
Stage 'preprocess' didn't change, skipping
Stage 'train' didn't change, skipping
Stage 'evaluate' didn't change, skipping
Data and calculation are already up to date.

# الآن عدّل learning_rate في params.yaml
$ dvc repro
Stage 'preprocess' didn't change, skipping
Running stage 'train'...  # يُعاد التشغيل لأن المعلمة تغيرت
Running stage 'evaluate'...  # يُعاد التشغيل لأن التبعية تغيرت

تتبع المقاييس

ملف المقاييس

# src/evaluate.py
import json

metrics = {
    "accuracy": 0.87,
    "precision": 0.85,
    "recall": 0.89,
    "f1_score": 0.87
}

with open('metrics.json', 'w') as f:
    json.dump(metrics, f, indent=2)

عرض المقاييس

# اعرض المقاييس الحالية
dvc metrics show

# الناتج:
# Path          accuracy    precision    recall    f1_score
# metrics.json  0.87        0.85         0.89      0.87

مقارنة التجارب

# قارن المقاييس عبر commits Git
dvc metrics diff HEAD~1

# الناتج:
# Path          Metric     HEAD      HEAD~1    Change
# metrics.json  accuracy   0.87      0.82      +0.05
# metrics.json  f1_score   0.87      0.81      +0.06

الرسوم والتصورات

تعريف الرسوم

# dvc.yaml
stages:
  train:
    cmd: python src/train.py
    plots:
      - training_history.csv:
          x: epoch
          y: loss

ملف سجل التدريب

# src/train.py
import csv

history = []
for epoch in range(epochs):
    loss = train_epoch(model, data)
    history.append({'epoch': epoch, 'loss': loss})

with open('training_history.csv', 'w', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=['epoch', 'loss'])
    writer.writeheader()
    writer.writerows(history)

عرض الرسوم

# أنشئ رسوم HTML
dvc plots show

# قارن الرسوم عبر التجارب
dvc plots diff HEAD~1

مثال خط أنابيب كامل

هيكل المشروع

my-project/
├── data/
│   ├── raw.csv
│   └── processed.csv
├── src/
│   ├── preprocess.py
│   ├── train.py
│   └── evaluate.py
├── models/
│   └── model.pkl
├── dvc.yaml
├── params.yaml
├── metrics.json
└── dvc.lock

dvc.yaml كامل

stages:
  preprocess:
    cmd: python src/preprocess.py
    deps:
      - src/preprocess.py
      - data/raw.csv
    params:
      - preprocess.test_split
      - preprocess.random_seed
    outs:
      - data/processed.csv
      - data/test.csv

  train:
    cmd: python src/train.py
    deps:
      - src/train.py
      - data/processed.csv
    params:
      - train.epochs
      - train.learning_rate
      - train.model_type
    outs:
      - models/model.pkl
    plots:
      - training_history.csv:
          x: epoch
          y: loss

  evaluate:
    cmd: python src/evaluate.py
    deps:
      - src/evaluate.py
      - models/model.pkl
      - data/test.csv
    metrics:
      - metrics.json:
          cache: false

ملف القفل

dvc.lock يُسجل الحالة الدقيقة لكل تشغيل:

# dvc.lock (يُنشأ تلقائياً)
schema: '2.0'
stages:
  preprocess:
    cmd: python src/preprocess.py
    deps:
      - path: data/raw.csv
        hash: md5
        md5: a1b2c3d4e5...
      - path: src/preprocess.py
        hash: md5
        md5: f6g7h8i9j0...
    outs:
      - path: data/processed.csv
        hash: md5
        md5: k1l2m3n4o5...

سير عمل التجارب

# 1. أنشئ فرع تجربة
git checkout -b experiment/new-features

# 2. عدّل المعلمات
vim params.yaml

# 3. شغّل خط الأنابيب
dvc repro

# 4. قارن النتائج
dvc metrics diff main

# 5. إذا أفضل، ثبّت وادمج
git add dvc.yaml dvc.lock params.yaml metrics.json
git commit -m "Experiment: added feature engineering, acc +5%"
git checkout main
git merge experiment/new-features

أفضل الممارسات

الممارسة لماذا
أبقِ المراحل دقيقة تخزين مؤقت وتصحيح أسهل
استخدم params.yaml افصل الكود عن التكوين
ثبّت dvc.lock يضمن إعادة الإنتاج الدقيقة
وثّق التجارب رسائل commit Git مهمة
استخدم الفروع تجربة واحدة لكل فرع

الرؤية الرئيسية: مع dvc.yaml و dvc.lock في Git، يمكن لأي شخص تشغيل dvc repro والحصول على نفس النتائج بالضبط—بعد أشهر أو سنوات.

الوحدة التالية: سنستكشف تنسيق سير عمل ML مع Kubeflow وAirflow وPrefect. :::

اختبار

الوحدة 2: التحكم في إصدار البيانات والنماذج باستخدام DVC

خذ الاختبار