Machine Learning: أشجار القرار والغابات العشوائية

آخر تحديث: 2026-08-26

شجرة القرار مثل شجرة من الأسئلة — كل عقدة تطرح سؤالًا واحدًا، وتتبع الإجابة لأسفل إلى ورقة، وهي تنبؤك.

1. ما ستتعلمه


2. قصة حقيقية من مدير منتج التجارة الإلكترونية

(1) نقطة الألم: التنبؤ بالمنتجات الرائجة اعتمد كليًا على الحدس، بمعدل نجاح أقل من 30%

كان Bob بحاجة لاختيار المنتجات الرائجة المحتملة من بين 1000 منتج جديد. في الماضي كان هذا حكمًا بحتًا، وكان معدل نجاحه 30% فقط. كل منتج رائج يحقق في المتوسط 500 ألف دولار من الإيرادات الشهرية، لذلك تفويت واحد يمثل خسارة ضخمة. الحدس لا يمكن تحديده كميًا أو إعادة استخدامه أو التكرار عليه.

(2) حل الغابة العشوائية

يمكن للغابة العشوائية اكتشاف أنماط المنتجات الرائجة تلقائيًا من بيانات المنتجات التاريخية — نطاق السعر، خصائص الفئة، اتجاهات مبيعات الأسبوع الأول — وتوفر بشكل طبيعي ترتيبًا لأهمية الميزات.

PYTHON
from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_estimators=100, oob_score=True, random_state=42)
rf.fit(X_train, y_train)
print(f"OOB Accuracy: {rf.oob_score_:.3f}")
print(f"Top feature: {feature_names[rf.feature_importances_.argmax()]}")

(3) النتيجة: ارتفعت دقة التنبؤ بالمنتجات الرائجة من 30% إلى 65%

استبدل Bob الحدس بالغابة العشوائية، مما رفع دقة التنبؤ بالمنتجات الرائجة من 30% إلى 65%. يحدد الآن 5 منتجات رائجة إضافية كل شهر، مما يضيف ما يقرب من 3 ملايين دولار في الإيرادات السنوية.


3. كيف تعمل أشجار القرار

(1) معايير التقسيم

في كل عقدة، تختار شجرة القرار أفضل ميزة وعتبة لتقسيم البيانات عليها. توجد ثلاثة معايير شائعة:

100%
graph TB
    ROOT[العقدة الجذرية<br/>جميع البيانات] --> SPLIT1{الميزة: السعر<br/>العتبة: 50 دولار}
    SPLIT1 -->|≤ 50| LEFT[الطفل الأيسر<br/>60% منتجات رائجة]
    SPLIT1 -->|> 50| RIGHT[الطفل الأيمن<br/>10% منتجات رائجة]
    LEFT --> SPLIT2{الميزة: مبيعات الأسبوع الأول}
    SPLIT2 -->|> 500| LEAF1[الورقة: رائج<br/>ثقة 90%]
    SPLIT2 -->|≤ 500| LEAF2[الورقة: ليس رائجًا<br/>ثقة 40%]
    RIGHT --> LEAF3[الورقة: ليس رائجًا<br/>ثقة 5%]
المعيار الصيغة الأساسية التفضيل عائلة الخوارزمية
كسب المعلومات $H(D) - H(D|A)$ يفضل الميزات متعددة القيم ID3
نسبة الكسب كسب المعلومات / القيمة الجوهرية تصحيح تحيز متعدد القيم C4.5
شائبة جيني $1 - \sum p_i^2$ يفضل نقاء الفئة الكبيرة CART

▶ مثال: تصنيف شجرة القرار على Iris

PYTHON
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
import matplotlib.pyplot as plt

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.3, random_state=42, stratify=iris.target
)

# التدريب مع التقليم المسبق
tree = DecisionTreeClassifier(max_depth=3, min_samples_leaf=5, random_state=42)
tree.fit(X_train, y_train)

y_pred = tree.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"Tree depth: {tree.get_depth()}")
print(f"Number of leaves: {tree.get_n_leaves()}")

# تصور الشجرة
fig, ax = plt.subplots(figsize=(14, 8))
plot_tree(tree, feature_names=iris.feature_names,
          class_names=iris.target_names, filled=True, rounded=True, ax=ax)
plt.title("Decision Tree (max_depth=3)")
plt.tight_layout()
plt.savefig("decision_tree.png", dpi=150)

Output:

TEXT 📖 للعرض فقط
# Runs successfully

(2) استراتيجيات التقليم

▶ مثال: مقارنة معاملات التقليم المسبق

PYTHON
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)

configs = {
    "No pruning": DecisionTreeClassifier(random_state=42),
    "max_depth=2": DecisionTreeClassifier(max_depth=2, random_state=42),
    "max_depth=3": DecisionTreeClassifier(max_depth=3, random_state=42),
    "min_samples_leaf=5": DecisionTreeClassifier(min_samples_leaf=5, random_state=42),
    "max_depth=3 + min_samples_leaf=5": DecisionTreeClassifier(
        max_depth=3, min_samples_leaf=5, random_state=42),
}

for name, model in configs.items():
    scores = cross_val_score(model, X, y, cv=5, scoring="accuracy")
    model.fit(X, y)
    print(f"{name:35s}: Accuracy={scores.mean():.3f}, Leaves={model.get_n_leaves()}")

Output:

TEXT 📖 للعرض فقط
# Runs successfully
نوع التقليم المعامل التأثير القيمة الموصى بها
التقليم المسبق max_depth يحد من عمق الشجرة 3-10
التقليم المسبق min_samples_leaf الحد الأدنى للعينات لكل ورقة 5-20
التقليم المسبق min_samples_split الحد الأدنى للعينات للتقسيم 10-40
التقليم المسبق max_features الميزات التي يتم النظر فيها لكل تقسيم sqrt(n_features)
التقليم اللاحق ccp_alpha تقليم تعقيد التكلفة اختر عبر GridSearch

4. الغابة العشوائية

(1) فكرة Bagging

الغابة العشوائية = Bagging (تجميع البوتستراب) + اختيار الميزات العشوائي.

100%
graph TB
    DATA[البيانات الأصلية] --> B1[عينة بوتستراب 1]
    DATA --> B2[عينة بوتستراب 2]
    DATA --> B3[عينة بوتستراب 3]
    DATA --> BN[عينة بوتستراب N]
    B1 --> T1[شجرة 1<br/>مجموعة ميزات عشوائية]
    B2 --> T2[شجرة 2<br/>مجموعة ميزات عشوائية]
    B3 --> T3[شجرة 3<br/>مجموعة ميزات عشوائية]
    BN --> TN[شجرة N<br/>مجموعة ميزات عشوائية]
    T1 --> VOTE[تصويت الأغلبية<br/>/ المتوسط]
    T2 --> VOTE
    T3 --> VOTE
    TN --> VOTE

▶ مثال: تصنيف الغابة العشوائية + تقييم OOB

PYTHON
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

rf = RandomForestClassifier(
    n_estimators=100,
    max_depth=None,
    oob_score=True,
    random_state=42,
)
rf.fit(X_train, y_train)

print(f"OOB Score: {rf.oob_score_:.4f}")
print(f"Test Accuracy: {accuracy_score(y_test, rf.predict(X_test)):.4f}")
print(f"Number of trees: {rf.n_estimators}")

Output:

TEXT 📖 للعرض فقط
# Runs successfully

(2) ضبط المعاملات

▶ مثال: انحدار الغابة العشوائية — التنبؤ بمبيعات SalesPredict

PYTHON
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import mean_absolute_error, r2_score
import numpy as np

rng = np.random.default_rng(42)
n = 500
X = rng.uniform(0, 100, (n, 6))
y = 50 + 0.8 * X[:, 0] + 1.2 * X[:, 1] - 0.5 * X[:, 2] + rng.normal(0, 5, n)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# مقارنة n_estimators المختلفة
for n_est in [10, 50, 100, 200]:
    rf = RandomForestRegressor(n_estimators=n_est, random_state=42, n_jobs=-1)
    rf.fit(X_train, y_train)
    score = rf.score(X_test, y_test)
    mae = mean_absolute_error(y_test, rf.predict(X_test))
    print(f"n_estimators={n_est:3d}: R²={score:.4f}, MAE={mae:.2f}")

Output:

TEXT 📖 للعرض فقط
# Runs successfully

5. تحليل أهمية الميزات

(1) متوسط تناقص الشوائب مقابل أهمية التبديل

▶ مثال: مقارنة طريقتين لأهمية الميزات

PYTHON
from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import numpy as np

X, y = load_iris(return_X_y=True)
feature_names = load_iris().feature_names

rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X, y)

# الطريقة 1: MDI (مدمجة، سريعة لكن متحيزة)
mdi_importance = rf.feature_importances_

# الطريقة 2: أهمية التبديل (أبطأ لكن أكثر موثوقية)
perm_result = permutation_importance(rf, X, y, n_repeats=30, random_state=42, n_jobs=-1)
perm_importance = perm_result.importances_mean

# المقارنة
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

y_pos = np.arange(len(feature_names))
axes[0].barh(y_pos, mdi_importance, color="#2196F3")
axes[0].set_yticks(y_pos)
axes[0].set_yticklabels(feature_names)
axes[0].set_title("MDI Feature Importance")

axes[1].barh(y_pos, perm_importance, color="#4CAF50")
axes[1].set_yticks(y_pos)
axes[1].set_yticklabels(feature_names)
axes[1].set_title("Permutation Feature Importance")

plt.tight_layout()
plt.savefig("feature_importance.png", dpi=150)

Output:

TEXT 📖 للعرض فقط
# Runs successfully
البُعد أهمية MDI أهمية التبديل
سرعة الحساب سريعة (محسوبة أثناء التدريب) بطيئة (تتطلب تنبؤات متكررة)
التحيز متحيزة نحو ميزات عالية الكاردينالية غير متحيزة
القابلية للتطبيق مدمجة في النماذج الشجرية تعمل مع أي نموذج
الموثوقية معتدلة أكثر موثوقية

▶ مثال: ترتيب ميزات المنتجات الرائجة لـ Bob

PYTHON
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
import numpy as np

rng = np.random.default_rng(42)
n = 1000
df = pd.DataFrame({
    "price_usd": rng.uniform(10, 200, n),
    "first_week_sales": rng.integers(10, 2000, n),
    "category_trend_score": rng.uniform(0, 1, n),
    "ad_budget_k": rng.uniform(1, 50, n),
    "review_score": rng.uniform(1, 5, n),
    "return_rate": rng.uniform(0, 0.3, n),
    "stock_depth": rng.integers(10, 500, n),
})

df["is_hit"] = (
    (df["first_week_sales"] > 500)
    & (df["category_trend_score"] > 0.6)
    & (df["price_usd"] < 100)
).astype(int) | (rng.random(n) < 0.05)  # أضف 5% ضوضاء

X = df.drop(columns=["is_hit"])
y = df["is_hit"]

rf = RandomForestClassifier(n_estimators=200, random_state=42)
rf.fit(X, y)

importance = pd.DataFrame({
    "feature": X.columns,
    "importance": rf.feature_importances_,
}).sort_values("importance", ascending=False)

print("Feature Importance for Hit Product Prediction:")
print(importance.to_string(index=False))

Output:

TEXT 📖 للعرض فقط
Feature Importance for Hit Product Prediction:

❓ أسئلة شائعة

س لماذا الغابة العشوائية أفضل من شجرة قرار واحدة؟
ج لسببين — 1) Bagging يقلل التباين (الأشجار المتعددة تصوت وتوسط)؛ 2) اختيار الميزات العشوائي يقلل الارتباط بين الأشجار، مما يجعل المجموعة أقوى. شجرة واحدة تميل إلى فرط التجهيز.
س هل n_estimators الأكبر دائمًا أفضل؟
ج ليس بالضرورة. بعد نقطة معينة، تتضاءل المكاسب وتضيف فقط وقت الحساب. عادة 100-500 كافٍ. راقب درجة OOB لمعرفة متى تستقر.
س ما هي درجة OOB؟
ج تقييم Out-of-Bag. تُدرب كل شجرة على حوالي 63% من البيانات، وتشكل الـ 37% المتبقية بشكل طبيعي مجموعة تحقق. درجة OOB هي متوسط التنبؤ عبر جميع الأشجار على عينات OOB الخاصة بها — مكافئة للتحقق المتقاطع، لكن مجانية.
س هل تحتاج أشجار القرار إلى التوحيد القياسي؟
ج لا. تنقسم أشجار القرار على عتبات الميزات، لذلك لا تتأثر بالمقياس. ولكن إذا تم دمج الغابة العشوائية مع نماذج أخرى (مثل Scaler في Pipeline)، فقد تحتاج إليه.
س ماذا لو كانت كل قيم أهمية الميزات قريبة من 0؟
ج هذا يشير إلى أن الميزات قد يكون لها بالفعل علاقة قليلة بالهدف. جرب تفاعلات الميزات أو التحويلات غير الخطية، أو استخدم أهمية التبديل للتحقق مما إذا كانت MDI متحيزة.
س هل يمكن للغابة العشوائية التعامل مع عدم توازن الفئات؟
ج نعم. اضبط class_weight="balanced" أو class_weight={0:1, 1:10}. يمكنك أيضًا دمجها مع الإفراط في العينات (SMOTE) أو تقليلها.

📖 ملخص

📝 تمارين

  1. أساسي (الصعوبة ⭐): استخدم DecisionTreeClassifier لتصنيف Iris، مع تنويع max_depth من 1 إلى 5، وارسم منحنى الدقة مقابل العمق. تلميح: حلقة على التدريب باستخدام cross_val_score.
  2. متوسط (الصعوبة ⭐⭐): استخدم RandomForestRegressor على California Housing، وقارن R² ودرجة OOB لـ n_estimators=[10,50,100,200]. تلميح: اضبط oob_score=True.
  3. تحدي (الصعوبة ⭐⭐⭐): نفّذ خط أنابيب التنبؤ بالمنتجات الرائجة الكامل لـ Bob — ولّد البيانات المحاكاة، ودرب RandomForestClassifier، وحلل الميزات باستخدام permutation_importance، وتعامل مع عدم التوازن باستخدام class_weight (المنتجات الرائجة < 10%)، وأخرج تقرير التصنيف. تلميح: راجع مثال المنتجات الرائجة في القسم 5.

← الدرس السابق: الانحدار اللوجستي | الدرس التالي: آلات المتجهات الداعمة →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%