أساسيات MLOps: الخطوات التسع الرئيسية للانتقال من الصفر إلى التشغيل

أساسيات MLOps: الأساسيات في مقال واحد — كود حقيقي، مخططات وخطوات ملموسة، مقتطفات من دورة تتكون من 72 درسًا.

أساسيات MLOps: الخطوات التسع الرئيسية للانتقال من الصفر إلى التشغيل

يمكن للجميع تعلم MLOps Fundamentals — بشرط اتباع الخطوات بالترتيب الصحيح. لقد لخصنا دورة كاملة من 72 درسًا في مسار واضح، مع أكثر مقتطفات الكود فائدة.

tl;dr
  • تثبيت بيئة MLOps
  • اكتشاف MLOps
  • إصدار البيانات والنماذج
  • خطوط أنابيب ML باستخدام MLflow
  • حاويات النماذج ML
~$ cat ./parcours.md # MLOps Fundamentals — 12 فصول
01
تثبيت بيئة MLOps
→ تثبيت Python، conda والأدوات الأساسية→ تهيئة Git، GitHub والممارسات الجيدة+ 1 دروس أخرى
02
اكتشاف MLOps
→ ما هو MLOps ولماذا هو ضروري؟→ دورة حياة نموذج ML في الإنتاج+ 1 دروس أخرى
03
إصدار البيانات والنماذج
→ لماذا إصدار البيانات والنماذج؟→ DVC — التثبيت والخطوات الأولى+ 1 دروس أخرى
04
خطوط أنابيب ML باستخدام MLflow
→ MLflow Tracking: تسجيل تجارب ML→ MLflow Model Registry: إدارة دورة حياة النماذج+ 1 دروس أخرى
05
تحويل نماذج ML إلى حاويات
→ لماذا Docker ضروري لـ MLOps→ Dockerfile لنموذج ML: البناء والتشغيل+ 1 دروس أخرى
06
النشر باستخدام FastAPI
→ FastAPI لخدمة نماذج ML→ بناء API تنبؤ كامل+ 1 دروس أخرى
07
CI-CD لـ ML
→ ما هو CI/CD لـ ML؟→ GitHub Actions: أتمتة اختبارات ML+ 2 دروس أخرى
08
مراقبة النماذج في الإنتاج
→ لماذا تتدهور النماذج في الإنتاج؟→ كشف Data Drift باستخدام Evidently+ 1 دروس أخرى
🏁
المشروع النهائي (+ 4 فصول في الطريق)
→ ستحصل على مشروع ملموس وقابل للعرض

دليل المشروع النهائي الكامل – خطوة بخطوة

Cours MLOps Fundamentals • كشف الاحتيال ببطاقة الائتمان • خط أنابيب MLOps شامل

NOTE📚 حول هذا الدليل
يرافقك هذا الدليل التفصيلي خطوة بخطوة في تنفيذ المشروع النهائي. يتوافق كل قسم مع خطوة رئيسية في خط أنابيب MLOps. اتبع الخطوات بالترتيب. كل الكود مقدم ومشروح. المدة التقريبية: 8–12 ساعة إجمالاً.

① الخطوة 1 – تهيئة المشروع والبيئة

أنشئ هيكل المشروع وبيئة Conda:

bash
mkdir fraud-detection-mlops
cd fraud-detection-mlops
git init
git config user.email "vous@email.com"
git config user.name "Votre Nom"

conda create -n fraud-mlops python=3.10 -y
conda activate fraud-mlops

pip install scikit-learn xgboost pandas numpy mlflow dvc \
            fastapi uvicorn pydantic evidently \
            pytest pytest-cov httpx joblib matplotlib \
            seaborn imbalanced-learn flake8 black

conda env export > environment.yml

أنشئ الملف requirements.txt :

output
scikit-learn==1.4.0
xgboost==2.0.3
pandas==2.1.4
numpy==1.26.3
mlflow==2.10.0
dvc==3.38.1
fastapi==0.109.0
uvicorn==0.27.0
pydantic==2.5.3
evidently==0.4.16
pytest==7.4.4
pytest-cov==4.1.0
httpx==0.26.0
joblib==1.3.2
matplotlib==3.8.2
seaborn==0.13.1
imbalanced-learn==0.11.0

أنشئ هيكل المجلدات:

bash
mkdir -p data/raw data/processed src api monitoring/reports tests models .github/workflows
output
__pycache__/
*.pyc
*.pyo
.env
models/
mlruns/
*.pkl
data/raw/
data/processed/
monitoring/reports/*.html
monitoring/reports/*.json
.coverage
htmlcov/
bash
dvc init
git add .
git commit -m "feat: init project structure and DVC"

② الخطوة 2 – توليد وإصدار البيانات

أنشئ src/generate_synthetic_data.py (إذا لم يكن لديك وصول إلى مجموعة بيانات Kaggle) :

output
"""يُنشئ مجموعة بيانات اصطناعية لاحتيال بطاقة الائتمان."""
import pandas as pd
import numpy as np

def generate_fraud_dataset(n_samples=50000, fraud_ratio=0.002, random_state=42):
    np.random.seed(random_state)
    n_fraud = int(n_samples * fraud_ratio)
    n_legit = n_samples - n_fraud

    legit = pd.DataFrame({
        f'V{i}': np.random.normal(0, 1, n_legit) for i in range(1, 29)
    })
    legit['Amount'] = np.abs(np.random.exponential(scale=88, size=n_legit))
    legit['Time'] = np.sort(np.random.uniform(0, 172800, n_legit))
    legit['Class'] = 0

    fraud = pd.DataFrame({
        f'V{i}': np.random.normal(np.random.uniform(-3, 3), 2, n_fraud) for i in range(1, 29)
    })
    fraud['Amount'] = np.abs(np.random.exponential(scale=122, size=n_fraud))
    fraud['Time'] = np.random.uniform(0, 172800, n_fraud)
    fraud['Class'] = 1

    df = pd.concat([legit, fraud], ignore_index=True)
    df = df.sample(frac=1, random_state=random_state).reset_index(drop=True)
    return df

if __name__ == "__main__":
    df = generate_fraud_dataset()
    df.to_csv("data/raw/creditcard.csv", index=False)
    print(f"Dataset généré : {len(df)} lignes")
    print(f"Fraudes : {df['Class'].sum()} ({df['Class'].mean()*100:.3f}%)")

Dockerfile لنموذج ML : البناء والتشغيل

Cours MLOps Fundamentals — الفصل 04 — حاويات نماذج ML

NOTE🎯 أهداف التعلم
  • فهم كل تعليمة في Dockerfile لنموذج ML
  • كتابة Dockerfile كامل لواجهة API scikit-learn مع FastAPI
  • بناء صورة Docker باستخدام docker build
  • تشغيل واختبار حاوية ML باستخدام docker run
  • تصحيح مشكلات حاويات ML الشائعة

1. هيكل مشروع ML المراد تحويله إلى حاوية

قبل كتابة Dockerfile، دعنا نرى هيكل المشروع الذي سنحوله إلى حاوية. إنه واجهة API لتصنيف النبيذ مبنية على نموذج Random Forest مدرب باستخدام scikit-learn، ويُقدم عبر FastAPI.

output
wine-classifier/
â└â─â─ Dockerfile              # Notre fichier de configuration Docker
â└â─â─ .dockerignore           # Fichiers à exclure de l'image
â└â─â─ requirements.txt        # Dépendances Python
â└â─â─ src/
â├â─â─   app.py               # Application FastAPI (point d'entrée)
â├â─â─   predict.py           # Logique de prédiction
â├â─â─   preprocessing.py     # Pré-traitement des features
â└â─â─ models/
    â└â─â─ rf_classifier.pkl   # Modèle Random Forest sérialisé
    â└â─â─ scaler.pkl           # StandardScaler sauvegardé

ملف requirements.txt

يسرد requirements.txt جميع التبعيات Python مع إصداراتها المثبتة. هذا أمر حاسم لإعادة الإنتاج: بدون تثبيت الإصدار، قد يقوم pip install بتنزيل إصدار أحدث وغير متوافق.

output
# requirements.txt
# Framework web
fastapi==0.110.0
uvicorn[standard]==0.27.1

# Machine Learning
scikit-learn==1.4.1
numpy==1.26.4
pandas==2.2.1

# Sérialisation du modèle
joblib==1.3.2

# Validation des données
pydantic==2.6.3
WARNING⚠ ثبت الإصدارات دائمًا!
تجنب scikit-learn>=1.0 أو scikit-learn بدون إصدار. إذا أصدر scikit-learn الإصدار 2.0 مع تغييرات في API، ستتعطل الحاوية عند إعادة البناء التالية. استخدم دائمًا scikit-learn==1.4.1.

تطبيق FastAPI (src/app.py)

output
# src/app.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import joblib
import numpy as np
import os

app = FastAPI(
    title="Wine Quality Classifier API",
    description="API de classification de la qualité du vin basée sur Random Forest",
    version="1.0.0"
)

# Chargement du modèle au démarrage (pas à chaque requête)
MODEL_PATH = os.getenv("MODEL_PATH", "/app/models/rf_classifier.pkl")
SCALER_PATH = os.getenv("SCALER_PATH", "/app/models/scaler.pkl")

model = joblib.load(MODEL_PATH)
scaler = joblib.load(SCALER_PATH)

class WineFeatures(BaseModel):
    fixed_acidity: float
    volatile_acidity: float
    citric_acid: float
    residual_sugar: float
    chlorides: float
    free_sulfur_dioxide: float
    total_sulfur_dioxide: float
    density: float
    pH: float
    sulphates: float
    alcohol: float

@app.get("/health")
def health_check():
    return {"status": "ok", "model": "rf_classifier", "version": "1.0.0"}

@app.post("/predict")
def predict(features: WineFeatures):
    try:
        X = np.array([[
            features.fixed_acidity, features.volatile_acidity,
            features.citric_acid, features.residual_sugar,
            features.chlorides, features.free_sulfur_dioxide,
            features.total_sulfur_dioxide, features.density,
            features.pH, features.sulphates, features.alcohol
        ]])
        X_scaled = scaler.transform(X)
        prediction = model.predict(X_scaled)[0]
        probability = model.predict_proba(X_scaled)[0].max()
        return {
            "quality": int(prediction),
            "confidence": round(float(probability), 4),
            "label": "bon vin" if prediction >= 6 else "vin moyen"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

2. Dockerfile الكامل والمشروح

إليك Dockerfile الكامل. سنحلل كل تعليمة بالتفصيل فورًا بعد ذلك.

output
# ============================================================
# Dockerfile pour un modèle scikit-learn servi avec FastAPI
# ============================================================

# Étape 1 : Image de base
FROM python:3.11-slim

# Étape 2 : Métadonnées de l'image
LABEL maintainer="mlops-team@exemple.com"
LABEL version="1.0.0"
LABEL description="Wine Quality Classifier - Random Forest API"

# Étape 3 : Variables d'environnement système
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# Étape 4 : Répertoire de travail dans le conteneur
WORKDIR /app

# Étape 5 : Dépendances système (si nécessaire)
RUN apt-get update && apt-get install -y --no-install-recommends \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*

# Étape 6 : Copier ET installer les dépendances Python (couche cachée)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Étape 7 : Copier le code source
COPY src/ ./src/

# Étape 8 : Copier le modèle ML
COPY models/ ./models/

# Étape 9 : Exposer le port de l'API
EXPOSE 8000

# Étape 10 : Utilisateur non-root pour la sécurité
RUN useradd --create-home --shell /bin/bash appuser
USER appuser

# Étape 11 : Commande de démarrage
CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "8000"]

3. تحليل مفصل لكل تعليمة

3.1 FROM python:3.11-slim

يحدد FROM الصورة الأساسية. python:3.11-slim هي النسخة الخفيفة من صورة Python الرسمية:

بالنسبة لـ scikit-learn، يُعد slim الحل الوسط الجيد: صغير بما يكفي، لكنه متوافق مع امتدادات C الخاصة بـ numpy/scipy.

WARNING⚠ تجنب :latest
قد ينتقل FROM python:latest من Python 3.11 إلى 3.12 بين عشية وضحاها ويكسر الكود. حدد الإصدار دائمًا: python:3.11-slim أو حتى python:3.11.8-slim.
TIP💡 لـ GPU
إذا كان نموذجك يستخدم GPU (TensorFlow، PyTorch)، استخدم:
FROM nvidia/cuda:12.1-cudnn8-runtime-ubuntu22.04

3.2 متغيرات البيئة ENV

output
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1
المتغير التأثير لماذا تفعّله؟
PYTHONDONTWRITEBYTECODE=1 لا ملفات .pyc يقلل حجم الصورة، يتجنب الملفات غير الضرورية
PYTHONUNBUFFERED=1 سجلات فورية ضروري لرؤية السجلات في docker logs فورًا
PIP_NO_CACHE_DIR=1 لا ذاكرة تخزين مؤقت لـ pip في الصورة يقلل الحجم النهائي للصورة (قد يوفر 100-200 ميجابايت)
PIP_DISABLE_PIP_VERSION_CHECK=1 لا فحص لتحديثات pip يسرّع البناء، يتجنب التحذيرات غير الضرورية

3.3 WORKDIR /app

يحدد WORKDIR دليل العمل داخل الحاوية. تنفذ جميع التعليمات التالية (COPY، RUN، CMD) من هذا الدليل. إذا لم يكن الدليل موجودًا، ينشئه Docker تلقائيًا.

TIP💡 أفضل ممارسة : استخدم دائمًا /app كدليل عمل لتطبيقات Python. تجنب العمل مباشرة في / أو /root.

تحسين صور Docker ML

Cours MLOps Fundamentals — الفصل 04 — حاويات نماذج ML

NOTE🎯 أهداف التعلم
  • فهم لماذا يُعد حجم صور Docker ML أمرًا حاسمًا في الإنتاج
  • إتقان البناء متعدد المراحل لفصل البناء عن التنفيذ
  • تهيئة .dockerignore فعال لمشاريع ML
  • استغلال ذاكرة التخزين المؤقت للطبقات في Docker إلى أقصى حد
  • تطبيق أفضل الممارسات لصور ML الإنتاجية

1. لماذا حجم صور ML حرج

على عكس تطبيق ويب تقليدي (بضع عشرات الميجابايت)، قد تصل صورة Docker ML بسهولة إلى 2 إلى 8 جيجابايت. لهذا الحجم عواقب مباشرة على سير عمل MLOps:

🚬 نشر بطيء

تنزيل صورة بحجم 5 جيجابايت على عنقود Kubernetes يستغرق 10 إلى 20 دقيقة. في حالة التوسع التلقائي تحت الحمل، هذا غير مقبول.

💸 تكلفة التخزين

تخزين 50 نسخة من صورة بحجم 4 جيجابايت في AWS ECR = 200 جيجابايت × 0.10$/جيجابايت/شهر = 20$/شهر فقط للتخزين.

🔐 سطح الهجوم

كل أداة إضافية (مترجمات، أدوات نظام) هي ثغرة أمنية محتملة. الصورة الدنيا أكثر أمانًا.

2. مقارنة: صور كبيرة مقابل صور خفيفة

النهج الصورة الأساسية الحجم النموذجي وقت السحب (100 ميجابت/ث) الثغرات
🔴 صورة كبيرة (ساذجة) python:3.11 + جميع الأدوات ~2.1 جيجابايت ~170 ث كثيرة جدًا
🟡 صورة خفيفة (ممارسة جيدة) python:3.11-slim ~700 ميجابايت ~56 ث متوسطة
🟢 متعددة المراحل خفيفة البناء: python:3.11-slim
التشغيل: python:3.11-slim
~350 ميجابايت ~28 ث قليلة
🆕 Distroless gcr.io/distroless/python3 ~180 ميجابايت ~14 ث قليلة جدًا
🟢 Alpine (بحذر) python:3.11-alpine ~100 ميجابايت ~8 ث دنيا
WARNING⚠ Alpine ومكتبات ML
يستخدم Alpine musl libc بدلاً من glibc. Numpy وscikit-learn وpandas وPyTorch مبنية لـ glibc. استخدام Alpine يجبر على الترجمة من المصدر، مما يستغرق 30 إلى 60 دقيقة وقد يفشل. احتفظ بـ Alpine للخدمات المصغرة بدون تبعيات C. لـ ML، فضّل python:3.11-slim.

3. .dockerignore: خط دفاعك الأول

حتى قبل البناء متعدد المراحل، يُعد .dockerignore أبسط وأسرع تحسين. يمنع Docker من إرسال ملفات غير ضرورية (أو خطيرة) إلى daemon البناء.

TIP💡 سياق بناء Docker
عند تنفيذ docker build .، يرسل Docker كامل الدليل الحالي إلى Docker daemon (يُسمى «سياق البناء»). بدون .dockerignore، يشمل ذلك مجموعة بياناتك بحجم 10 جيجابايت، ودفاتر Jupyter، وبيئات Python الافتراضية، وملفات الإعدادات السرية...
output
# .dockerignore pour un projet ML

# Contrôle de version
.git/
.gitignore
.github/

# Environnements Python virtuels (critique ! peut faire des centaines de Mo)
.venv/
venv/
env/
ENV/
__pycache__/
*.py[cod]
*.pyo
.pytest_cache/
.mypy_cache/

# Données ML (ne jamais embarquer dans l'image !)
data/
datasets/
*.csv
*.parquet
*.arrow
*.feather

# Notebooks Jupyter (pas nécessaires en production)
*.ipynb
.ipynb_checkpoints/

# Fichiers de configuration locale
.env
.env.local
.env.development
*.env

# Documentation et tests (inutiles en production)
docs/
tests/
README.md
CHANGELOG.md

# Artefacts de build
dist/
build/
*.egg-info/
htmlcov/
.coverage

# IDE et éditeurs
.vscode/
.idea/
*.swp
*.swo
.DS_Store
Thumbs.db

# Logs et fichiers temporaires
logs/
*.log
tmp/
temp/

# Fichiers MLflow et expériences
mlruns/
mlflow.db

# Modèles de test (garder seulement le modèle de production)
models/experiments/
models/checkpoints/

قياس تأثير .dockerignore

bash
# Vérifier la taille du contexte de build AVANT .dockerignore
docker build -t test-before . 2>&1 | head -5
# Sending build context to Docker daemon  4.521GB  <-- sans .dockerignore !

# Vérifier la taille du contexte de build APRES .dockerignore
docker build -t test-after . 2>&1 | head -5
# Sending build context to Docker daemon  12.34MB  <-- avec .dockerignore

4. البناء متعدد المراحل لـ ML

يستخدم البناء متعدد المراحل عدة تعليمات FROM في Dockerfile واحد. تنتج كل مرحلة طبقة وسيطة، ولا يُحتفظ إلا بالمرحلة الأخيرة في الصورة النهائية. يسمح ذلك بـ:

4.1 بناء متعدد المراحل لنموذج scikit-learn

output
# ============================================================
# Multi-stage Dockerfile pour Wine Classifier (production)
# ============================================================

# ---- ÉTAPE 1 : Builder ----
# Cette étape installe tout, y compris les outils de compilation
FROM python:3.11-slim AS builder

# Variables d'environnement pour le build
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1

WORKDIR /build

# Installer les outils de build système (ne seront PAS dans l'image finale)
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    g++ \
    build-essential \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*

# Copier et installer dans un répertoire local isolé (--prefix)
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt


# ---- ÉTAPE 2 : Image de production (finale) ----
# Image finale ultra-légère : ne contient PAS gcc, build-essential, etc.
FROM python:3.11-slim AS production

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

WORKDIR /app

# Copier uniquement les bibliothèques installées depuis le builder
COPY --from=builder /install /usr/local

# Dépendances système runtime (minimales)
RUN apt-get update && apt-get install -y --no-install-recommends \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*

# Copier le code source et le modèle
COPY src/ ./src/
COPY models/ ./models/

# Sécurité : utilisateur non-root
RUN useradd --create-home --shell /bin/bash appuser && \
    chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
TIP💡 توفير حجم نموذجي مع البناء متعدد المراحل
بإزالة gcc، g++، build-essential والملفات الوسيطة من الصورة النهائية، توفر عادة بين 200 و600 ميجابايت حسب تبعيات C/C++ المستخدمة.

4.2 بناء متعدد المراحل مع مرحلة اختبار

output
# ============================================================
# Multi-stage Dockerfile avec étape de test intégrée
# ============================================================

FROM python:3.11-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt


# ---- Étape de test ----
FROM base AS test
COPY requirements-dev.txt .
RUN pip install --no-cache-dir -r requirements-dev.txt
COPY src/ ./src/
COPY models/ ./models/
COPY tests/ ./tests/
RUN pytest tests/ -v --tb=short


# ---- Étape de production ----
FROM base AS production
COPY src/ ./src/
COPY models/ ./models/
RUN useradd --create-home appuser && chown -R appuser /app
USER appuser
EXPOSE 8000
CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "8000"]
va-plus-loin

يغطي هذا المقال المقتطفات الأكثر فائدة — الدورة الكاملة MLOps Fundamentals (13 فصلاً، 72 درسًا، تمارين مصححة ومشروع نهائي) تأخذك إلى النهاية.

./acceder-au-cours-complet cours gratuit : Maîtriser Claude Code

الأسئلة الشائعة

كم من الوقت لتعلم MLOps Fundamentals؟
مع تقدم منظم (13 فصلاً، 72 درسًا قصيرًا وعمليًا)، يمكن الوصول إلى مستوى تشغيلي في بضعة أسابيع بمعدل 30 إلى 60 دقيقة يوميًا. المهم هو تطبيق كل مفهوم فورًا.
هل هناك متطلبات مسبقة؟
يفضل الإلمام بأساسيات المجال: هذا المحتوى يتعمق، مع حالات حقيقية.
من أين نبدأ عمليًا؟
نفذ أوامر هذا المقال، ثم تابع دورة MLOps Fundamentals الكاملة: تربط الـ72 درسًا بالترتيب، مع تمارين ومشروع نهائي.

📬 هل تريد تلقي هذا النوع من الأدلة أسبوعيًا؟ اشترك مجانًا — كود حقيقي، بدون كلام زائد.