الدرس 8 من 8

المراقبة بعد النشر

النشر مش النهاية. ده بداية حياة الموديل.

ليه الموديل «بيبوظ»؟

الكود مابيتغيرش، بس الدنيا بتتغير. الموديل اتدرب على أسعار السنة اللي فاتت، والأسعار زادت. أو شركة فتحت كمبوند جديد والشقق كلها بقت أكبر. الموديل هيفضل يرد بثقة، بس ردوده هتبقى أغلط وأغلط. ده اسمه Data Drift، ومش هيطلع error ولا حاجة، فلازم انت تدور عليه.

سجّل كل توقع

logging_predictions.py
import json
import logging
import time

pred_log = logging.getLogger("predictions")
pred_log.addHandler(logging.FileHandler("predictions.jsonl"))
pred_log.setLevel(logging.INFO)

def log_prediction(features: dict, price_k: float, started: float, version: str):
    pred_log.info(json.dumps({
        "time": time.time(),
        "latency_ms": round((time.time() - started) * 1000, 1),
        "model_version": version,
        "input": features,
        "output": price_k,
    }))

# inside your /predict endpoint:
#   started = time.time()
#   ... predict ...
#   log_prediction(apt.model_dump(), price, started, MODEL_VERSION)

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

اكتشف الـ Drift

هنقارن توزيع المساحات في داتا التدريب بتوزيعها في الطلبات اللي جت الشهر ده. واختبار إحصائي اسمه Kolmogorov–Smirnov بيقولك هل التوزيعين مختلفين فعلاً ولا الفرق صدفة:

drift.py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import ks_2samp

train_area = pd.read_csv("apartments.csv")["area"]

rng = np.random.default_rng(3)
# Simulated production requests: a new compound with bigger flats opened
live_area = np.concatenate([rng.integers(60, 221, 300), rng.integers(200, 320, 150)])

stat, p_value = ks_2samp(train_area, live_area)
print(f"train mean area: {train_area.mean():.0f} m2")
print(f"live mean area:  {live_area.mean():.0f} m2")
print(f"KS p-value: {p_value:.2e}")
print("DRIFT DETECTED" if p_value < 0.01 else "no significant drift")
print(f"requests outside training range: {(live_area > train_area.max()).mean():.0%}")

plt.figure(figsize=(7, 3.4))
plt.hist(train_area, bins=30, alpha=0.6, density=True, label="training data", color="#3776AB")
plt.hist(live_area, bins=30, alpha=0.6, density=True, label="this month", color="#E0A800")
plt.xlabel("area (m²)"); plt.title("Input drift: area")
plt.legend()
plt.show()

الاختبار أكد إن فيه drift، والأخطر إن جزء كبير من الطلبات فيه مساحات أكبر من أي شقة الموديل شافها. الموديل هنا بيخمّن برا المنطقة اللي اتعلمها، زي الـ LSTM في المرحلة ٦ بالظبط. ده وقت إنك تجمع داتا جديدة وتدرّب نسخة جديدة.

تراقب إيه؟

المؤشربيقولك إيه
latency وعدد الطلبات والـ errorsالـ API نفسه صحته كويسة؟
توزيع المدخلاتالداتا الجديدة شبه داتا التدريب؟
توزيع التوقعاتالموديل فجأة بقى يطلع أرقام غريبة؟
الأداء الحقيقيلما السعر الحقيقي يبان بعدين، التوقع كان قريب؟
🔄 الدورة الكاملةداتا ← تدريب ← تقييم ← نشر ← مراقبة ← اكتشاف drift ← داتا جديدة ← تدريب نسخة جديدة. الموديل الناجح في الإنتاج مش اللي دقته عالية يوم النشر، ده اللي فيه نظام بيحافظ على دقته مع الوقت.
الدرس اللي فات