Loading
Loading
Decision Trees هي خوارزمية تعلم آلة تقلّد طريقة التفكير البشري — تطرح سلسلة من الأسئلة الثنائية للوصول إلى قرار نهائي.
هل المساحة > 150م²؟
├── نعم → هل الغرف > 3؟
│ ├── نعم → سعر عالٍ ✅
│ └── لا → سعر متوسط
└── لا → هل قرب المركز؟
├── نعم → سعر متوسط
└── لا → سعر منخفض ✅
| المصطلح | المعنى | |---------|--------| | Root Node | السؤال الأول (الأهم) | | Internal Node | أسئلة وسطى | | Branch | النتيجة (نعم/لا) | | Leaf Node | القرار النهائي | | Depth | عمق الشجرة (عدد الأسئلة) |
الهدف: كل سؤال يجب أن يُقلل الاضطراب (Impurity) بأكبر قدر ممكن.
Gini Impurity:
Gini = 1 - Σ(pᵢ²)
Information Gain (Entropy):
Entropy = -Σ(pᵢ × log₂(pᵢ))
IG = Entropy(parent) - Σ(weighted Entropy(children))
النموذج يختار السؤال الذي يُعطي أعلى Information Gain.
المشكلة: إذا تركنا الشجرة تنمو بحرية، ستحفظ كل مثال في بيانات التدريب.
بيانات التدريب: Accuracy = 100% 🎉
بيانات الاختبار: Accuracy = 65% 😱
الحل — Pruning (التقليم):
model = DecisionTreeClassifier(
max_depth=5, # أقصى عمق
min_samples_leaf=10, # أقل عدد عناصر في الورقة
min_samples_split=20, # أقل عدد للتقسيم
)
المزايا:
العيوب:
import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, classification_report
from sklearn.datasets import load_breast_cancer
print("=" * 55)
print("Decision Trees: كشف سرطان الثدي")
print("=" * 55)
# بيانات طبية حقيقية (مدمجة في sklearn)
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = data.target
print(f"عدد العينات: {len(y)}")
print(f"الفئات: {dict(zip(data.target_names, np.bincount(y)))}")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42
)
# ─────────────────────────────────────────
# 1. شجرة بدون قيود — Overfitting
# ─────────────────────────────────────────
tree_full = DecisionTreeClassifier(random_state=42)
tree_full.fit(X_train, y_train)
train_acc = accuracy_score(y_train, tree_full.predict(X_train))
test_acc = accuracy_score(y_test, tree_full.predict(X_test))
print("\n1. شجرة كاملة (بدون قيود):")
print(f" Training Accuracy = {train_acc:.3f}")
print(f" Test Accuracy = {test_acc:.3f}")
print(f" عمق الشجرة = {tree_full.get_depth()}")
print(f" Overfitting gap = {train_acc - test_acc:.3f}")
# ─────────────────────────────────────────
# 2. شجرة مقلّمة — Pruning
# ─────────────────────────────────────────
tree_pruned = DecisionTreeClassifier(
max_depth=5,
min_samples_leaf=10,
min_samples_split=20,
random_state=42
)
tree_pruned.fit(X_train, y_train)
train_acc_p = accuracy_score(y_train, tree_pruned.predict(X_train))
test_acc_p = accuracy_score(y_test, tree_pruned.predict(X_test))
print("\n2. شجرة مقلّمة (max_depth=5):")
print(f" Training Accuracy = {train_acc_p:.3f}")
print(f" Test Accuracy = {test_acc_p:.3f}")
print(f" عمق الشجرة = {tree_pruned.get_depth()}")
print(f" Overfitting gap = {train_acc_p - test_acc_p:.3f} ✅ أفضل!")
# ─────────────────────────────────────────
# 3. Cross-Validation
# ─────────────────────────────────────────
cv_scores = cross_val_score(tree_pruned, X, y, cv=5, scoring="accuracy")
print(f"\n3. Cross-Validation (5 folds):")
print(f" Scores: {cv_scores.round(3)}")
print(f" Mean: {cv_scores.mean():.3f} ± {cv_scores.std():.3f}")
# ─────────────────────────────────────────
# 4. أهمية الميزات
# ─────────────────────────────────────────
importances = pd.Series(
tree_pruned.feature_importances_,
index=data.feature_names
).sort_values(ascending=False)
print("\n4. أهم الميزات الطبية:")
for feat, imp in importances.head(5).items():
bar = "█" * int(imp * 50)
print(f" {feat[:30]:30s}: {imp:.3f} {bar}")
# ─────────────────────────────────────────
# 5. قراءة الشجرة
# ─────────────────────────────────────────
print("\n5. هيكل الشجرة (أول 3 مستويات):")
tree_text = export_text(tree_pruned,
feature_names=list(data.feature_names),
max_depth=3)
print(tree_text[:800])