Logo
تعلم الآلة (2026-2027) - أشجار القرار والغابة العشوائية

في نافذة الطباعة اختر «حفظ كملف PDF» كوجهة.

تعلم الآلة، الأسبوع 6

أشجار القرار والغابة العشوائية

صنف المقترض بطرح الأسئلة، ثم دع أشجارا كثيرة تصوت: قروض Lending Club.

الأهداف

  • شرح كيف تصنف شجرة القرار (decision tree): سلسلة من أسئلة العتبة (threshold) تنتهي عند ورقة (leaf)
  • حساب عدم نقاء جيني (Gini impurity) لعقدة (node) وجيني الموزون (weighted Gini) لتقسيم (split) يدويا
  • شرح لماذا تعاني الشجرة التي تنمو حتى تصبح كل ورقة نقية فرط الملاءمة (overfitting)
  • شرح الغابة العشوائية (random forest): عينات bootstrap، وخصائص (features) عشوائية عند كل تقسيم، ثم التصويت
  • تدريب DecisionTreeClassifier وRandomForestClassifier على بيانات Lending Club
  • إدراك متى لا تعكس الدقة (accuracy) العالية إلا هدفا (target) غير متوازن، وتطبيق ذلك على مشروعك

الأسبوع 6 من الخطة

موقع الدرس من المقرر

  • دفتر (notebook) واحد: DecisionTrees_RandomForest_Classification، شجرة وغابة على بيانات إقراض عامة
  • سيناريو من الواقع: تربط Lending Club بين المقترضين والمستثمرين؛ ونتنبأ بما إذا كان المقترض قد سدد قرضه كاملا
  • مرحلة المشروع (project milestone): تنفيذ نموذج تعلم الآلة (Machine Learning Model Implementation)، وتستمر على بياناتك الخاصة

خطة الساعتين

الجزءما نفعلهالزمن
1فكرة الشجرة، وشجرة ننميها يدويا30 دقيقة
2دفتر Lending Club: البيانات والاستكشاف15 دقيقة
3شجرة قرار واحدة على القروض10 دقائق
4الغابة العشوائية: يدويا، ثم على القروض25 دقيقة
5الأخطاء، والمشروع، والتمارين مع الإجابات، والخلاصة40 دقيقة

الجزء 1

فكرة شجرة القرار

اطرح الأسئلة حتى تتضح الإجابة

ما شجرة القرار؟

شجرة القرار (decision tree)
مصنف (classifier) يطرح سلسلة من الأسئلة التي يجاب عنها بنعم أو لا، ويقارن كل سؤال خاصية واحدة بـعتبة، مثل fico ≤ 677.
  • الإجابة نعم ترسل العينة إلى الفرع الأيسر، والإجابة لا إلى الفرع الأيمن
  • ينتظر السؤال التالي في نهاية كل فرع
  • العقدة التي لا سؤال فيها ورقة: وصنفها هو التنبؤ

المصطلحات على بيانات القروض

المصطلحفي بيانات Lending Club
الخصائص Xالبيانات الائتمانية، مثل fico (درجة الائتمان) وint.rate (سعر الفائدة)
التسمية (label) ynot.fully.paid: 1 لم يسدد كاملا، و0 سدد كاملا
عقدة الجذرالسؤال الأول، ويطرح على كل مقترض
الورقةعقدة بلا سؤال: تتنبأ بصنف الأغلبية فيها
العمق (depth)عدد الأسئلة على أطول مسار إلى ورقة

ما مدى اختلاط العقدة؟ عدم نقاء جيني

يدرب الدفتر باستخدام criterion='gini'

G = 1 − (p02 + p12)
  • p_0 هي نسبة العقدة في الصنف 0 (سدد)، وp_1 نسبتها في الصنف 1 (لم يسدد)
p0 = 1: G = 0 p0 = p1 = 0.5: G = 0.5

اختيار السؤال: جيني الموزون

Gw = nLn GL + nRn GR
  • يقسم السؤال n من الصفوف إلى n_L على اليسار وn_R على اليمين
  • G_L وG_R هما جيني كل جانب؛ ويحسب كل جانب بحسب حجمه
  • العتبات المرشحة هي نقاط المنتصف (midpoints) بين القيم المتجاورة

يدويا

مثال محلول: عشرة مقترضين حقيقيين

صفوف من loan_data.csv، مرتبة حسب fico. y = 1: لم يسدد كاملا. أربعة من عشرة لم يسددوا.

المقترضficoint.ratey
B16470.14821
B26620.15070
B36670.12991
B46720.13471
B56820.11030
المقترضficoint.ratey
B66870.13870
B76920.12840
B87070.10911
B97270.10590
B107570.11890

الخطوة 1: جيني الجذر

المقترضون العشرة كلهم: 6 سددوا، و4 لم يسددوا

G = 1 − (0.62 + 0.42) = 1 − (0.36 + 0.16) = 0.48

الخطوة 2: جرب السؤال fico ≤ 677

677 هو نقطة المنتصف بين B4 (672) وB5 (682)

الجانبالمقترضونلم يسددسدد
نعم، ≤ 677من B1 إلى B431
لا، > 677من B5 إلى B101 (B8)5

الخطوة 3: جيني كل جانب

GL = 1 − (34)2 − (14)2 = 1 − 0.5625 − 0.0625 = 0.375
GR = 1 − (16)2 − (56)2 = 1036 ≈ 0.2778

الخطوة 4: جيني الموزون للتقسيم

Gw = 410 × 0.375 + 610 × 0.2778 = 0.15 + 0.1667 = 0.3167
ΔG = 0.48 − 0.3167 = 0.1633

الخطوة 5: الخاصية الأخرى

أفضل سؤال على int.rate هو int.rate ≤ 0.12915

الجانبالمقترضونلم يسددسدد
نعم، أسعار أقلB5، B7، B8، B9، B1014
لا، أسعار أعلىB1، B2، B3، B4، B632
Gw = 510 × 0.32 + 510 × 0.48 = 0.40 > 0.3167

الخطوة 6: يجرب البحث كل عتبة

تسع نقاط منتصف لـfico وتسع لـint.rate: ثمانية عشر سؤالا مرشحا

عتبة ficoاليسار: لم يسدد، سدداليمين: لم يسدد، سددجيني الموزون
654.51, 03, 60.4000
669.52, 12, 50.4190
6773, 11, 50.3167
684.53, 21, 40.4000
7174, 40, 20.4000

الخطوة 7: تنمية العقدة الابنة (child) اليسرى

من B1 إلى B4: 3 لم يسددوا، و1 سدد، G = 0.375

Gw = 34 × 0 + 14 × 0 = 0

الخطوة 8: تنمية العقدة الابنة اليمنى

من B5 إلى B10: 1 لم يسدد (B8)، و5 سددوا، G = 0.2778

Gw = 26 × 0.5 + 46 × 0 ≈ 0.1667

الشجرة التي بنيناها

text
fico <= 677 ?
|-- yes: int.rate <= 0.14945 ?
|        |-- yes: leaf [3 not paid, 0 paid] -> not paid
|        |-- no:  leaf [0 not paid, 1 paid] -> paid
|-- no:  int.rate <= 0.1097 ?
         |-- yes: leaf [1 not paid, 1 paid] -> paid
         |-- no:  leaf [0 not paid, 4 paid] -> paid

التنبؤ بمقترضين جديدين

اتبع الأسئلة من الجذر

  • A: 670 ≤ 677 نعم، ثم 0.14 ≤ 0.14945 نعم، إذن تقول الورقة لم يسدد
  • B: 700 ≤ 677 لا، ثم 0.12 ≤ 0.1097 لا، إذن تقول الورقة سدد

جرب بنفسك: ابن الشجرة يدويا

الجزء 2

دفتر Lending Club

9578 قرضا حقيقيا، من 2007 إلى 2010

افتح الدفتر في Colab

text
https://colab.research.google.com/github/
tirthajyoti/Machine-Learning-with-Python/blob/
master/Classification/
DecisionTrees_RandomForest_Classification.ipynb
اجمع الأسطر في عنوان واحد، أو اضغط رابط "افتح DecisionTrees_RandomForest_Classification في Colab" في صفحة الدرس.

تحميل البيانات وفحصها

python
import pandas as pd

url = ("https://raw.githubusercontent.com/"
       "tirthajyoti/Machine-Learning-with-Python/"
       "master/Datasets/loan_data.csv")
df = pd.read_csv(url)
df.info()
  • 9578 صفا، و14 عمودا، ولا قيم مفقودة
  • fico: درجة الائتمان؛ وint.rate: سعر الفائدة في صورة نسبة
  • purpose: الغرض من القرض، في صورة نص
  • التسمية not.fully.paid: 1 إذا لم يسدد المقترض كاملا

العدد المهم

python
print(df['not.fully.paid'].value_counts())
not.fully.paidالمقترضونالنسبة
0، سدد804584%
1، لم يسدد153316%

استكشف قبل أن تبني النموذج

مدرج تكراري (histogram) لدرجات FICO للمقترضين الذين يستوفون سياسة الائتمان (بالأزرق) والذين لا يستوفونها (بالأحمر): تحت 660 لا يستوفيها أحد تقريبا
عدد القروض حسب الغرض، المسددة كاملة (بالأحمر) وغير المسددة كاملة (بالأزرق)؛ وتوحيد الديون هو الغرض الأكثر شيوعا
  • تحت fico 660: لا يستوفي 487 مقترضا سياسة الائتمان، ويستوفيها 2 فقط. فعتبة واحدة تفصل مجموعة
  • لم تسدد كاملة: 27.8% من قروض small_business، و11.6% من قروض credit_card

تحويل عمود النص إلى أرقام

python
df_final = pd.get_dummies(df, ['purpose'],
                          drop_first=True)
  • تصبح الأغراض السبعة ستة أعمدة قيمها 0 أو 1، مثل purpose_credit_card
  • يحذف drop_first=True الغرض all_other: فالقيمة 0 في الأعمدة الستة كلها تعني هذا الغرض
  • في df_final 19 عمودا

التقسيم إلى مجموعة تدريب ومجموعة اختبار

python
X = df_final.drop('not.fully.paid', axis=1)
y = df_final['not.fully.paid']
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.30, random_state=101)

الجزء 3

شجرة قرار واحدة على القروض

دع الشجرة تنمو حتى تصبح كل ورقة نقية

تدريب الشجرة والتنبؤ

python
from sklearn.tree import DecisionTreeClassifier

dtree = DecisionTreeClassifier(criterion='gini', max_depth=None,
                               random_state=101)
dtree.fit(X_train, y_train)
predictions = dtree.predict(X_test)
  • criterion='gini': عدم النقاء الذي رأيناه في الجزء 1
  • max_depth=None: استمر في التقسيم حتى تصبح كل ورقة نقية
  • random_state=101: تكسر حالات التعادل بين التقسيمات المتساوية في الجودة بالطريقة نفسها في كل تشغيل

مصفوفة الالتباس للشجرة

A: الدقة. وR_1 وP_1: الاستدعاء (recall) والضبط (precision) للصنف 1

التنبؤ 0 (سدد)التنبؤ 1 (لم يسدد)
الفعلي 01975456
الفعلي 1336107
A = 1975 + 1072874 ≈ 0.724

الشجرة حفظت مجموعة تدريبها

python
print(dtree.get_depth(), dtree.get_n_leaves())
print(dtree.score(X_train, y_train))
المقياسالقيمة
العمق27
الأوراق1005
الدقة على التدريب1.0

الجزء 4

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

أشجار كثيرة مختلفة، وتصويت واحد

الفكرة: أشجار كثيرة مختلفة

  1. Bootstrap: تحصل كل شجرة على n من الصفوف مسحوبة عشوائيا مع الإرجاع (with replacement) من صفوف التدريب البالغ عددها n
  2. خصائص عشوائية: عند كل تقسيم لا تبحث الشجرة إلا في m من الخصائص البالغ عددها p، حيث m = ⌊√p⌋
  3. التصويت: تصنف كل شجرة العينة الجديدة؛ ويفوز صنف الأغلبية

يدويا

مثال محلول: عينة bootstrap واحدة

RandomForestClassifier(n_estimators=5, random_state=0) على المقترضين العشرة. ومع p = 2 يبحث كل تقسيم في خاصية عشوائية واحدة، m = 1

  • سحبت الشجرة 1: B4، B3، B1، B1، B3، B7، B10، B8، B4، B10
  • مرتين: B1، B3، B4، B10. ومرة واحدة: B7، B8
  • لم يسحبوا أبدا: B2، B5، B6، B9، فبقوا خارج هذه الشجرة

خمس أشجار تصوت على مقترض جديد

المقترض الجديد: fico 710، int.rate 0.11

الشجرةخارج عينتهاالصوت
1B2، B5، B6، B9لم يسدد
2B3، B6، B7سدد
3B1، B3، B7، B10سدد
4B2، B3، B7، B8، B10سدد
5B1، B3، B5، B9لم يسدد

جرب بنفسك: شاهد تصويت الغابة

تدريب الغابة

python
from sklearn.ensemble import RandomForestClassifier

rfc = RandomForestClassifier(n_estimators=600, random_state=101)
rfc.fit(X_train, y_train)
rfc_pred = rfc.predict(X_test)
print(confusion_matrix(y_test, rfc_pred))
print(classification_report(y_test, rfc_pred))
مع p = 18 يبحث كل تقسيم في m = 4 من الخصائص: فالأمر rfc.estimators_[0].max_features_ يطبع 4.

مصفوفة الالتباس للغابة

A: الدقة على مقترضي الاختبار الـ2874

التنبؤ 0 (سدد)التنبؤ 1 (لم يسدد)
الفعلي 0242011
الفعلي 143112
A = 2420 + 122874 ≈ 0.846

هل تجد شيئا غريبا؟

A_0: دقة الإجابة "سدد" عن الجميع. وR_1: استدعاء الصنف 1

A0 = 24312874 ≈ 0.846 R1 = 12443 ≈ 0.03
  • لا تصنف الغابة على أنهم لم يسددوا إلا 23 مقترضا، وتتفوق على "سدد للجميع" بمقترض واحد
  • لا يحصل أي مقترض اختبار على أكثر من 375 صوتا بـ"لم يسدد" من الأشجار الـ600

كم شجرة؟

دقة الغابات من 5 إلى 100 شجرة على الاختبار: 0.824 مع 5 أشجار، ثم بين 0.8455 و0.8473 من 30 شجرة فصاعدا، بجانب القيمة 0.8459 التي تحققها الإجابة سدد عن الجميع
  • يمر الدفتر في حلقة على 5 إلى 100 شجرة، مع min_samples_split=10، ونضيف random_state=101 لكل غابة
  • 0.8239 مع 5 أشجار، ثم 0.8455 إلى 0.8473 من 30 شجرة فصاعدا
  • زيادة الأشجار تجعل التصويت أكثر ثباتا، لكن المنحنى يستقر عند مستوى "سدد للجميع"

قبل التمارين

الأخطاء الشائعة

  • قراءة loan_data.csv في Colab من مجلد لا يحتويه
  • نسيان random_state، ثم مقارنة أرقام من تقسيمين مختلفين
  • الثقة بـالنتيجة المثالية على التدريب لشجرة نمت باستخدام max_depth=None
  • الحكم على نموذج بالدقة وحدها حين يكون أحد الصنفين نادرا
  • طباعة تقرير predictions وأنت تقصد rfc_pred
  • تمرير عمود نصي مثل purpose دون متغيرات وهمية (dummy variables)

مشروع فريقك

مرحلة المشروع: تنفيذ النموذج

  1. خذ الخصائص والهدف من الأسبوع الماضي؛ وحول الأعمدة النصية إلى متغيرات وهمية
  2. قسم البيانات مع قيمة ثابتة لـrandom_state
  3. درب DecisionTreeClassifier: وقارن نتيجته على التدريب بنتيجته على الاختبار
  4. درب RandomForestClassifier: واعرض مصفوفة الالتباس (confusion matrix) وclassification_report
  5. قارن الشجرة والغابة وKNN على مجموعة الاختبار نفسها، باستخدام استدعاء كل صنف

الجزء 5

التمارين: دورك

نحو 30 دقيقة، والإجابات تلي كل مهمة

نحو 10 دقائق

التمرين 1: اختيار السؤال الأول

ثمانية مقترضين حقيقيين آخرين، y = 1: لم يسدد

المقترضficoint.ratey
P16470.14821
P26670.13430
P36770.17341
P46820.11970
المقترضficoint.ratey
P56920.12870
P66970.13161
P77420.11140
P87470.07510
  • احسب جيني الجذر
  • احسب G_w للسؤال fico ≤ 679.5 وللسؤال int.rate ≤ 0.13015
  • أي السؤالين يفوز، وبكم تنخفض قيمة جيني؟

الإجابات

التمرين 1: الإجابة

G = 1 − (0.3752 + 0.6252) ≈ 0.4688
السؤالجيني الموزون
fico ≤ 679.5G_w = 0.375 × 0.4444 + 0.625 × 0.32 ≈ 0.3667
int.rate ≤ 0.13015G_w = 0.5 × 0 + 0.5 × 0.375 = 0.1875

نحو 5 دقائق

التمرين 2: قراءة مصفوفة الغابة

الغابة ذات الأشجار الـ600: [[2420, 11], [431, 12]]

P1 = 1223 ≈ 0.52 R1 = 12443 ≈ 0.03

نحو 5 دقائق

التمرين 3: إنتروبيا التقسيم الأول

يدرب الدفتر أيضا باستخدام criterion='entropy'، أي الإنتروبيا (entropy)

H = −(p0 log2 p0 + p1 log2 p1)
HL ≈ 0.8113 HR ≈ 0.6500 Hw ≈ 0.7145

نحو 10 دقائق

التمرين 4: في Colab، على القروض

  1. بعد التقسيم مع random_state=101، درب DecisionTreeClassifier(max_depth=5, random_state=101)
  2. اطبع مصفوفة الالتباس الخاصة به، ودقته على الاختبار وعلى التدريب؛ وقارنها مع max_depth=None
  3. أعد تشغيل الحلقة على عدد الأشجار مع max_depth=5 وrandom_state=101 في كل غابة
الشجرةمصفوفة الالتباسالاختبارالتدريب
max_depth=5[[2396, 35], [431, 12]]0.83790.845
max_depth=None[[1975, 456], [336, 107]]0.7241.0

الخلاصة

  1. تصنف الشجرة بسلسلة من أسئلة العتبة؛ وتتنبأ الورقة بصنف الأغلبية فيها
  2. كل سؤال هو صاحب أقل قيمة لجيني الموزون عبر كل خاصية وكل عتبة
  3. الشجرة التي تنمو حتى تصبح كل ورقة نقية تحفظ مجموعة تدريبها
  4. تجري الغابة العشوائية تصويتا بين أشجار نمت على عينات bootstrap مع خصائص عشوائية
  5. حين يكون أحد الصنفين نادرا، اقرأ استدعاءه، لا الدقة وحدها

افتح هذا الدرس

Mahmoud Abasأشجار القرار والغابة العشوائية