Machine Learning: تقييم النموذج والضبط
آخر تحديث: 2026-08-26
إذا كانت منهجية التقييم الخاصة بك خاطئة، فإن جميع التحسينات مبنية على الرمال — قس بدقة أولًا، ثم اضبط.
1. ما ستتعلمه
- منهجية التقييم: Hold-out / K-Fold / Stratified K-Fold / TimeSeriesSplit
- تقييم التصنيف: مصفوفة الارتباك، منحنيات Precision-Recall، F1-β، مقاييس متعددة الفئات
- تقييم الانحدار: MAE / MSE / RMSE / MAPE / R²، مواءمة المقاييس مع أهداف العمل
- ضبط المعاملات: GridSearchCV / RandomizedSearchCV / تحسين Optuna Bayesian
- تشخيص فرط التجهيز: منحنيات التعلم، منحنيات التحقق
2. قصة حقيقية من مهندس ML
(1) المشكلة: دقة 95% على مجموعة الاختبار، 75% بعد النشر
قيّم Bob نموذجه بـ train_test_split وحصل على دقة اختبار 95%. ولكن بعد أسبوع من النشر، كانت دقة العالم الحقيقي 75% فقط — لأنه مع بيانات السلاسل الزمنية، تداخلت توزيعات التدريب والاختبار، وكان النموذج "يتلصص" على معلومات المستقبل. أدت منهجية التقييم المعيبة إلى مقاييس متضخمة وإطلاق كارثي.
(2) الإصلاح: منهجية التقييم الصحيحة
يجب أن تستخدم بيانات السلاسل الزمنية TimeSeriesSplit — تحتوي مجموعة التدريب على البيانات الماضية فقط، ومجموعة الاختبار هي بيانات مستقبلية، مما يحاكي ظروف العالم الحقيقي.
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_idx, test_idx in tscv.split(X):
# train_idx دائمًا < test_idx (لا تسرب مستقبلي)
model.fit(X[train_idx], y[train_idx])
score = model.score(X[test_idx], y[test_idx])
(3) النتيجة: مقاييس تعكس الأداء الحقيقي
أعاد Bob التقييم باستخدام TimeSeriesSplit، وانخفضت الدقة من 95% "متضخمة" إلى 82% "واقعية". انخفض الرقم، لكن دقة ما بعد النشر كانت 82% أيضًا — التقييم والواقع تطابقا أخيرًا، دون مزيد من المفاجآت غير السارة.
3. منهجية التقييم
(1) أربع استراتيجيات للتحقق المتقاطع
graph TB
DATA[مجموعة البيانات] --> HO[Hold-out<br/>تقسيم تدريب/اختبار واحد]
DATA --> KF[K-Fold CV<br/>تقسيمات عشوائية]
DATA --> SKF[Stratified K-Fold<br/>تقسيمات متوازنة الفئة]
DATA --> TSS[TimeSeriesSplit<br/>تقسيمات زمنية]
HO --> HO_USE[خط أساس سريع<br/>مجموعة بيانات كبيرة]
KF --> KF_USE[للأغراض العامة<br/>تقدير مستقر]
SKF --> SKF_USE[التصنيف<br/>بيانات غير متوازنة]
TSS --> TSS_USE[سلسلة زمنية<br/>لا تسرب مستقبلي]
▶ مثال: مقارنة أربع استراتيجيات للتحقق المتقاطع
from sklearn.model_selection import (train_test_split, KFold, StratifiedKFold,
TimeSeriesSplit, cross_val_score)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
import numpy as np
X, y = load_iris(return_X_y=True)
model = RandomForestClassifier(n_estimators=50, random_state=42)
# 1. Hold-out
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
model.fit(X_tr, y_tr)
holdout_score = model.score(X_te, y_te)
# 2. K-Fold
kf_scores = cross_val_score(model, X, y, cv=KFold(n_splits=5, shuffle=True, random_state=42))
# 3. Stratified K-Fold
skf_scores = cross_val_score(model, X, y, cv=StratifiedKFold(n_splits=5, shuffle=True, random_state=42))
# 4. TimeSeriesSplit (للبيانات المرتبة زمنيًا)
tss_scores = cross_val_score(model, X, y, cv=TimeSeriesSplit(n_splits=5))
print(f"Hold-out: {holdout_score:.4f}")
print(f"K-Fold: {kf_scores.mean():.4f} +/- {kf_scores.std():.4f}")
print(f"Stratified K-Fold: {skf_scores.mean():.4f} +/- {skf_scores.std():.4f}")
print(f"TimeSeriesSplit: {tss_scores.mean():.4f} +/- {tss_scores.std():.4f}")
Output:
# Execution successful
| الطريقة | نوع البيانات | المزايا | العيوب |
|---|---|---|---|
| Hold-out | أي | سريع | النتيجة تعتمد على التقسيم |
| K-Fold | أي | مستقر | تقسيم عشوائي |
| Stratified K-Fold | التصنيف | يحافظ على نسب الفئة | التصنيف فقط |
| TimeSeriesSplit | سلسلة زمنية | لا تسرب مستقبلي | حجم تدريب متزايد |
4. مقاييس التصنيف والانحدار
(1) مقاييس التصنيف الكاملة
▶ مثال: تقييم تصنيف شامل
from sklearn.metrics import (accuracy_score, precision_score, recall_score,
f1_score, roc_auc_score, classification_report,
confusion_matrix)
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
pipe = Pipeline([("scaler", StandardScaler()), ("model", LogisticRegression(max_iter=500))])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
y_prob = pipe.predict_proba(X_test)[:, 1]
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"Precision: {precision_score(y_test, y_pred):.4f}")
print(f"Recall: {recall_score(y_test, y_pred):.4f}")
print(f"F1: {f1_score(y_test, y_pred):.4f}")
print(f"AUC-ROC: {roc_auc_score(y_test, y_prob):.4f}")
print(f"\nConfusion Matrix:\n{confusion_matrix(y_test, y_pred)}")
print(f"\nDetailed Report:\n{classification_report(y_test, y_pred)}")
Output:
# Execution successful
(2) مقاييس الانحدار المتوائمة مع أهداف العمل
▶ مثال: مقاييس الانحدار ومعناها التجاري
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np
y_true = np.array([100, 150, 200, 250, 300]) # ألف دولار
y_pred = np.array([95, 160, 190, 260, 310])
mae = mean_absolute_error(y_true, y_pred)
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
r2 = r2_score(y_true, y_pred)
mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
print(f"MAE: {mae:.2f} k USD")
print(f"RMSE: {rmse:.2f} k USD")
print(f"R²: {r2:.4f}")
print(f"MAPE: {mape:.1f}%")
# حساب التأثير التجاري
avg_monthly_revenue = 1000 # ألف دولار
mape_pct = mape / 100
inventory_cost_pct = 0.3 # 30% من المخزون الزائد هو هدر
annual_loss = avg_monthly_revenue * mape_pct * inventory_cost_pct * 12
print(f"\nAnnual inventory loss from prediction error: {annual_loss:.0f} k USD")
Output:
# Execution successful
| المقياس | الصيغة | المعنى التجاري |
|---|---|---|
| MAE | متوسط|y-ŷ| | متوسط الانحراف (ألف دولار) |
| RMSE | √(متوسط(y-ŷ)²) | يعاقب الأخطاء الكبيرة أكثر |
| R² | 1 - SS_res/SS_tot | نسبة التباين المفسّر |
| MAPE | متوسط|y-ŷ|/y × 100% | الخطأ النسبي (%) |
5. ضبط المعاملات
(1) ثلاث استراتيجيات بحث
▶ مثال: GridSearch مقابل RandomSearch
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
import time
X, y = fetch_california_housing(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
param_grid = {
"n_estimators": [50, 100, 200],
"max_depth": [5, 10, 15, None],
"min_samples_leaf": [1, 5, 10],
}
# GridSearch: بحث شامل
start = time.time()
grid = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=3, scoring="r2", n_jobs=-1)
grid.fit(X_train, y_train)
grid_time = time.time() - start
# RandomSearch: أخذ عينات عشوائية
start = time.time()
random = RandomizedSearchCV(RandomForestRegressor(random_state=42), param_grid,
n_iter=15, cv=3, scoring="r2", n_jobs=-1, random_state=42)
random.fit(X_train, y_train)
random_time = time.time() - start
print(f"GridSearch: R²={grid.best_score_:.4f}, Time={grid_time:.1f}s, Trials={len(grid.cv_results_['mean_test_score'])}")
print(f"RandomSearch: R²={random.best_score_:.4f}, Time={random_time:.1f}s, Trials=15")
Output:
# Execution successful
| الطريقة | استراتيجية البحث | التغطية | السرعة | الأفضل لـ |
|---|---|---|---|---|
| GridSearch | شامل لجميع التركيبات | 100% | بطيء | معاملات قليلة |
| RandomSearch | أخذ عينات عشوائية | جزئي | سريع | معاملات كثيرة |
| Optuna | تحسين Bayesian | بحث موجّه | فعّال | فضاءات بحث معقدة |
(2) تحسين Optuna Bayesian
▶ مثال: ضبط المعاملات باستخدام Optuna
# pip install optuna
import optuna
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
import numpy as np
X, y = fetch_california_housing(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
def objective(trial):
n_estimators = trial.suggest_int("n_estimators", 50, 500)
max_depth = trial.suggest_int("max_depth", 3, 20)
min_samples_leaf = trial.suggest_int("min_samples_leaf", 1, 20)
max_features = trial.suggest_float("max_features", 0.3, 1.0)
model = RandomForestRegressor(
n_estimators=n_estimators, max_depth=max_depth,
min_samples_leaf=min_samples_leaf, max_features=max_features,
random_state=42, n_jobs=-1,
)
scores = cross_val_score(model, X_train, y_train, cv=3, scoring="r2")
return scores.mean()
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=30, show_progress_bar=False)
print(f"Best R²: {study.best_value:.4f}")
print(f"Best params: {study.best_params}")
Output:
# Function defined successfully
6. تشخيص فرط التجهيز
(1) منحنيات التعلم ومنحنيات التحقق
▶ مثال: رسم منحنيات التعلم
from sklearn.model_selection import learning_curve, validation_curve
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import fetch_california_housing
import matplotlib.pyplot as plt
import numpy as np
X, y = fetch_california_housing(return_X_y=True)
# منحنى التعلم: الأداء مقابل حجم مجموعة التدريب
train_sizes, train_scores, val_scores = learning_curve(
RandomForestRegressor(n_estimators=50, random_state=42),
X, y, train_sizes=np.linspace(0.1, 1.0, 10),
cv=3, scoring="r2", n_jobs=-1,
)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# منحنى التعلم
axes[0].plot(train_sizes, train_scores.mean(axis=1), "o-", label="Training")
axes[0].plot(train_sizes, val_scores.mean(axis=1), "o-", label="Validation")
axes[0].fill_between(train_sizes, train_scores.mean(axis=1) - train_scores.std(axis=1),
train_scores.mean(axis=1) + train_scores.std(axis=1), alpha=0.1)
axes[0].fill_between(train_sizes, val_scores.mean(axis=1) - val_scores.std(axis=1),
val_scores.mean(axis=1) + val_scores.std(axis=1), alpha=0.1)
axes[0].set_xlabel("Training Size")
axes[0].set_ylabel("R² Score")
axes[0].set_title("Learning Curve")
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# منحنى التحقق: الأداء مقابل المعامل
param_range = [3, 5, 7, 10, 15, 20, None]
train_scores2, val_scores2 = validation_curve(
RandomForestRegressor(n_estimators=50, random_state=42),
X, y, param_name="max_depth", param_range=param_range,
cv=3, scoring="r2", n_jobs=-1,
)
axes[1].plot(range(len(param_range)), train_scores2.mean(axis=1), "o-", label="Training")
axes[1].plot(range(len(param_range)), val_scores2.mean(axis=1), "o-", label="Validation")
axes[1].set_xticks(range(len(param_range)))
axes[1].set_xticklabels([str(p) for p in param_range])
axes[1].set_xlabel("max_depth")
axes[1].set_ylabel("R² Score")
axes[1].set_title("Validation Curve")
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("learning_validation_curves.png", dpi=150)
Output:
# Execution successful
| التشخيص | نمط المنحنى | المعنى | الحل |
|---|---|---|---|
| فرط التجهيز المنخفض | كل من نتائج التدريب والتحقق منخفضة | النموذج بسيط جدًا | زيادة التعقيد / إضافة ميزات |
| فرط التجهيز | تدريب عالٍ، تحقق منخفض | النموذج معقد جدًا | تنظيم / تبسيط / إضافة بيانات |
| ملاءمة جيدة | كلاهما عالٍ ومتقارب | متوازن جيدًا | لا حاجة لتغييرات |
| يحتاج مزيدًا من البيانات | منحنى التحقق لا يزال يرتفع | بيانات غير كافية | جمع مزيد من البيانات |
❓ أسئلة شائعة
📖 ملخص
- منهجية التقييم هي أساس أي مشروع ML: استخدم StratifiedKFold للتصنيف، TimeSeriesSplit للسلاسل الزمنية
- اختيار مقياس التصنيف يعتمد على العمل: التخلي → Recall، كشف البريد المزعج → Precision، التوصيات → F1
- يجب أن تتوائم مقاييس الانحدار مع تكاليف العمل: MAPE بنسبة 10% على توقعات بمليون دولار يعني 100 ألف دولار في الخطأ
- تطور ضبط المعاملات: GridSearch (شامل) → RandomSearch (أخذ عينات) → Optuna (ذكي)
- تشخيص فرط التجهيز: تكشف منحنيات التعلم ما إذا كان لديك بيانات كافية؛ تكشف منحنيات التحقق ما إذا كانت المعاملات مثلى
- التقييم الصحيح أهم من الدرجات العالية — المقاييس المتضخمة تنهار بعد النشر
📝 تمارين
- أساسي (الصعوبة ⭐): قارن متوسط الدقة والانحراف المعياري لـ RandomForestClassifier على Iris باستخدام 5-fold و 10-fold تحقق متقاطع. تلميح: cross_val_score(cv=5/10).
- متوسط (الصعوبة ⭐⭐): استخدم GridSearchCV لإيجاد المعاملات المثلى لـ XGBoost (max_depth / learning_rate / n_estimators)، وأخرج أفضل المعاملات و R² للتحقق المتقاطع. تلميح: Pipeline + param_grid.
- تحدٍّ (الصعوبة ⭐⭐⭐): ارسم منحنى تعلم ومنحنى تحقق لتشخيص فرط التجهيز/فرط التجهيز المنخفض في نموذج، واقترح خطة تحسين ملموسة، وتحقق من التحسين. تلميح: learning_curve + validation_curve + مقارنة قبل/بعد.
← الدرس السابق: الشبكات العصبية التلافيفية | الدرس التالي: مقدمة في MLOps →