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

ما هي أشجار القرار؟

أشجار القرار تعد خوارزميات التعلم الآلي متعددة الاستخدامات التي يمكنها تنفيذ مهام التصنيف والانحدار. وهي خوارزميات قوية للغاية، وقادرة على استيعاب مجموعات البيانات المعقدة. علاوة على ذلك، تعد أشجار القرار مكونات أساسية للغابات العشوائية، والتي تعد من بين أقوى خوارزميات التعلم الآلي المتاحة اليوم.

التدريب وتصور أشجار القرار في R

لبناء شجرة القرار الأولى في مثال R، سنتبع ما يلي في هذا البرنامج التعليمي لشجرة القرار:

  • الخطوة 1: استيراد البيانات
  • الخطوة 2: تنظيف مجموعة البيانات
  • الخطوة 3: إنشاء مجموعة تدريب/اختبار
  • الخطوة 4: بناء النموذج
  • الخطوة 5: قم بالتنبؤ
  • الخطوة 6: قياس الأداء
  • الخطوة 7: ضبط المعلمات الفائقة

الخطوة 1) استيراد البيانات

إذا كنت مهتمًا بمصير التايتنك، يمكنك مشاهدة هذا الفيديو على يوتيوب . الغرض من مجموعة البيانات هذه هو التنبؤ بالأشخاص الذين من المرجح أن يبقوا على قيد الحياة بعد الاصطدام بالجبل الجليدي. تحتوي مجموعة البيانات على 13 متغيرًا و1309 ملاحظة. يتم ترتيب مجموعة البيانات بواسطة المتغير X.

set.seed(678)
path <- 'https://raw.githubusercontent.com/guru99-edu/R-Programming/master/titanic_data.csv'
titanic <-read.csv(path)
head(titanic)

الإخراج:

##   X pclass survived                                            name    sex
## 1 1      1        1                   Allen, Miss. Elisabeth Walton female
## 2 2      1        1                  Allison, Master. Hudson Trevor   male
## 3 3      1        0                    Allison, Miss. Helen Loraine female
## 4 4      1        0            Allison, Mr. Hudson Joshua Creighton   male
## 5 5      1        0 Allison, Mrs. Hudson J C (Bessie Waldo Daniels) female
## 6 6      1        1                             Anderson, Mr. Harry   male
##       age sibsp parch ticket     fare   cabin embarked
## 1 29.0000     0     0  24160 211.3375      B5        S
## 2  0.9167     1     2 113781 151.5500 C22 C26        S
## 3  2.0000     1     2 113781 151.5500 C22 C26        S
## 4 30.0000     1     2 113781 151.5500 C22 C26        S
## 5 25.0000     1     2 113781 151.5500 C22 C26        S
## 6 48.0000     0     0  19952  26.5500     E12        S
##                         home.dest
## 1                    St Louis, MO
## 2 Montreal, PQ / Chesterville, ON
## 3 Montreal, PQ / Chesterville, ON
## 4 Montreal, PQ / Chesterville, ON
## 5 Montreal, PQ / Chesterville, ON
## 6                    New York, NY
tail(titanic)

الإخراج:

##         X pclass survived                      name    sex  age sibsp
## 1304 1304      3        0     Yousseff, Mr. Gerious   male   NA     0
## 1305 1305      3        0      Zabour, Miss. Hileni female 14.5     1
## 1306 1306      3        0     Zabour, Miss. Thamine female   NA     1
## 1307 1307      3        0 Zakarian, Mr. Mapriededer   male 26.5     0
## 1308 1308      3        0       Zakarian, Mr. Ortin   male 27.0     0
## 1309 1309      3        0        Zimmerman, Mr. Leo   male 29.0     0
##      parch ticket    fare cabin embarked home.dest
## 1304     0   2627 14.4583              C          
## 1305     0   2665 14.4542              C          
## 1306     0   2665 14.4542              C          
## 1307     0   2656  7.2250              C          
## 1308     0   2670  7.2250              C          
## 1309     0 315082  7.8750              S

من مخرجات الرأس والذيل، يمكنك ملاحظة عدم خلط البيانات. هذه قضية كبيرة! عندما تقوم بتقسيم بياناتك بين مجموعة القطار ومجموعة الاختبار، ستختار فقط الراكب من الفئتين 1 و2 (لا يوجد راكب من الفئة 3 ضمن أعلى 80 بالمائة من الملاحظات)، مما يعني أن الخوارزمية لن ترى أبدًا ميزات راكب الفئة 3. سيؤدي هذا الخطأ إلى سوء التنبؤ.

للتغلب على هذه المشكلة، يمكنك استخدام الدالة Sample().

shuffle_index <- sample(1:nrow(titanic))
head(shuffle_index)

شرح رمز شجرة القرار R

  • Sample(1:nrow(titanic)): إنشاء قائمة عشوائية من الفهرس من 1 إلى 1309 (أي الحد الأقصى لعدد الصفوف).

الإخراج:

## [1]  288  874 1078  633  887  992

سوف تستخدم هذا الفهرس لخلط مجموعة البيانات العملاقة.

titanic <- titanic[shuffle_index, ]
head(titanic)

الإخراج:

##         X pclass survived
## 288   288      1        0
## 874   874      3        0
## 1078 1078      3        1
## 633   633      3        0
## 887   887      3        1
## 992   992      3        1
##                                                           name    sex age
## 288                                      Sutton, Mr. Frederick   male  61
## 874                   Humblen, Mr. Adolf Mathias Nicolai Olsen   male  42
## 1078                                 O'Driscoll, Miss. Bridget female  NA
## 633  Andersson, Mrs. Anders Johan (Alfrida Konstantia Brogren) female  39
## 887                                        Jermyn, Miss. Annie female  NA
## 992                                           Mamee, Mr. Hanna   male  NA
##      sibsp parch ticket    fare cabin embarked           home.dest## 288      0     0  36963 32.3208   D50        S     Haddenfield, NJ
## 874      0     0 348121  7.6500 F G63        S                    
## 1078     0     0  14311  7.7500              Q                    
## 633      1     5 347082 31.2750              S Sweden Winnipeg, MN
## 887      0     0  14313  7.7500              Q                    
## 992      0     0   2677  7.2292              C	

الخطوة 2) تنظيف مجموعة البيانات

يوضح هيكل البيانات أن بعض المتغيرات لها NA. يتم تنظيف البيانات على النحو التالي

  • قم بإسقاط المتغيرات home.dest وcabin وname وX والتذكرة
  • إنشاء متغيرات عامل لـ pclass ونجا
  • إسقاط NA
library(dplyr)
# Drop variables
clean_titanic <- titanic % > %
select(-c(home.dest, cabin, name, X, ticket)) % > % 
#Convert to factor level
	mutate(pclass = factor(pclass, levels = c(1, 2, 3), labels = c('Upper', 'Middle', 'Lower')),
	survived = factor(survived, levels = c(0, 1), labels = c('No', 'Yes'))) % > %
na.omit()
glimpse(clean_titanic)

شرح الكود

  • حدد(-c(home.dest, Cabin, name, X, Ticket)): قم بإسقاط المتغيرات غير الضرورية
  • pclass = عامل(pclass,levels = c(1,2,3), labels= c('Upper', 'Middle', 'Lower')): أضف تسمية إلى المتغير pclass. 1 يصبح علويًا، 2 يصبح متوسطًا و3 يصبح أقل
  • عامل(survived,levels = c(0,1), labels = c('No', 'Yes')): أضف تسمية للمتغير الذي نجا. 1 يصبح لا و 2 يصبح نعم
  • na.omit(): إزالة ملاحظات NA

الإخراج:

## Observations: 1,045
## Variables: 8
## $ pclass   <fctr> Upper, Lower, Lower, Upper, Middle, Upper, Middle, U...
## $ survived <fctr> No, No, No, Yes, No, Yes, Yes, No, No, No, No, No, Y...
## $ sex      <fctr> male, male, female, female, male, male, female, male...
## $ age      <dbl> 61.0, 42.0, 39.0, 49.0, 29.0, 37.0, 20.0, 54.0, 2.0, ...
## $ sibsp    <int> 0, 0, 1, 0, 0, 1, 0, 0, 4, 0, 0, 1, 1, 0, 0, 0, 1, 1,...
## $ parch    <int> 0, 0, 5, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 4, 0,...
## $ fare     <dbl> 32.3208, 7.6500, 31.2750, 25.9292, 10.5000, 52.5542, ...
## $ embarked <fctr> S, S, S, S, S, S, S, S, S, C, S, S, S, Q, C, S, S, C...		

الخطوة 3) إنشاء مجموعة تدريب/اختبار

قبل تدريب النموذج الخاص بك، تحتاج إلى تنفيذ خطوتين:

  • إنشاء قطار ومجموعة اختبار: يمكنك تدريب النموذج على مجموعة القطار واختبار التنبؤ على مجموعة الاختبار (أي البيانات غير المرئية)
  • قم بتثبيت rpart.plot من وحدة التحكم

الممارسة الشائعة هي تقسيم البيانات بنسبة 80/20، حيث يعمل 80 بالمائة من البيانات على تدريب النموذج، و20 بالمائة في عمل التنبؤات. تحتاج إلى إنشاء إطارين منفصلين للبيانات. لا تريد لمس مجموعة الاختبار حتى تنتهي من بناء النموذج الخاص بك. يمكنك إنشاء اسم دالة create_train_test() الذي يأخذ ثلاث وسائط.

create_train_test(df, size = 0.8, train = TRUE)
arguments:
-df: Dataset used to train the model.
-size: Size of the split. By default, 0.8. Numerical value
-train: If set to `TRUE`, the function creates the train set, otherwise the test set. Default value sets to `TRUE`. Boolean value.You need to add a Boolean parameter because R does not allow to return two data frames simultaneously.
create_train_test <- function(data, size = 0.8, train = TRUE) {
    n_row = nrow(data)
    total_row = size * n_row
    train_sample < - 1: total_row
    if (train == TRUE) {
        return (data[train_sample, ])
    } else {
        return (data[-train_sample, ])
    }
}

شرح الكود

  • الوظيفة (البيانات، الحجم = 0.8، القطار = TRUE): أضف الوسائط في الوظيفة
  • n_row = nrow(data): حساب عدد الصفوف في مجموعة البيانات
  • Total_row = size*n_row: قم بإرجاع الصف n لإنشاء مجموعة القطار
  • Train_sample <- 1:total_row: حدد الصف الأول من الصفوف n
  • if (train ==TRUE){ } else { }: إذا تم ضبط الشرط على true، قم بإرجاع مجموعة القطار، وإلا قم بإرجاع مجموعة الاختبار.

يمكنك اختبار وظيفتك والتحقق من البعد.

data_train <- create_train_test(clean_titanic, 0.8, train = TRUE)
data_test <- create_train_test(clean_titanic, 0.8, train = FALSE)
dim(data_train)

الإخراج:

## [1] 836   8
dim(data_test)

الإخراج:

## [1] 209   8

تحتوي مجموعة بيانات القطار على 1046 صفًا بينما تحتوي مجموعة بيانات الاختبار على 262 صفًا.

يمكنك استخدام الدالة Prop.table() مع table() للتحقق من صحة عملية التوزيع العشوائي.

prop.table(table(data_train$survived))

الإخراج:

##
##        No       Yes 
## 0.5944976 0.4055024
prop.table(table(data_test$survived))

الإخراج:

## 
##        No       Yes 
## 0.5789474 0.4210526

وفي كلتا مجموعتي البيانات، فإن عدد الناجين هو نفسه، حوالي 40 بالمائة.

قم بتثبيت rpart.plot

rpart.plot غير متوفر من مكتبات conda. يمكنك تثبيته من وحدة التحكم:

install.packages("rpart.plot")

الخطوة 4) بناء النموذج

أنت جاهز لبناء النموذج. بناء جملة وظيفة شجرة القرار Rpart هو:

rpart(formula, data=, method='')
arguments:			
- formula: The function to predict
- data: Specifies the data frame- method: 			
- "class" for a classification tree 			
- "anova" for a regression tree	

يمكنك استخدام طريقة الفصل لأنك تتنبأ بالفصل.

library(rpart)
library(rpart.plot)
fit <- rpart(survived~., data = data_train, method = 'class')
rpart.plot(fit, extra = 106

شرح الكود

  • rpart(): الدالة التي تناسب النموذج. الحجج هي:
    • نجا ~.: صيغة أشجار القرار
    • البيانات = data_train: مجموعة البيانات
    • الطريقة = 'الفئة': تناسب النموذج الثنائي
  • rpart.plot(fit, extra= 106): ارسم الشجرة. يتم تعيين الميزات الإضافية على 101 لعرض احتمالية الفئة الثانية (مفيدة للاستجابات الثنائية). يمكنك الرجوع إلى المقالة القصيرة لمزيد من المعلومات حول الاختيارات الأخرى.

الإخراج:

بناء نموذج لأشجار القرار في R

تبدأ من العقدة الجذرية (العمق 0 على 3، أعلى الرسم البياني):

  1. في الأعلى، هو الاحتمال العام للبقاء على قيد الحياة. ويبين نسبة الركاب الذين نجوا من الحادث. نجا 41 بالمائة من الركاب.
  2. تسأل هذه العقدة ما إذا كان جنس الراكب ذكرًا. إذا كانت الإجابة بنعم، فانتقل إلى العقدة الفرعية اليسرى للجذر (العمق 2). 63% منهم ذكور مع احتمال البقاء على قيد الحياة بنسبة 21%.
  3. في العقدة الثانية، تسأل ما إذا كان عمر الراكب الذكر أكبر من 3.5 سنة. إذا كانت الإجابة بنعم، فإن فرصة البقاء على قيد الحياة هي 19 بالمائة.
  4. عليك أن تستمر على هذا المنوال لفهم الميزات التي تؤثر على احتمالية البقاء على قيد الحياة.

لاحظ أن إحدى الخصائص العديدة لأشجار القرار هي أنها تتطلب القليل جدًا من إعداد البيانات. وعلى وجه الخصوص، فهي لا تتطلب تغيير حجم الميزة أو توسيطها.

بشكل افتراضي، تستخدم الدالة rpart() ‎ جيني قياس النجاسة لتقسيم المذكرة. كلما ارتفع معامل جيني، زادت الحالات المختلفة داخل العقدة.

الخطوة 5) قم بالتنبؤ

يمكنك التنبؤ بمجموعة بيانات الاختبار الخاصة بك. للقيام بالتنبؤ، يمكنك استخدام وظيفة التنبؤ (). بناء الجملة الأساسي للتنبؤ بشجرة قرار R هو:

predict(fitted_model, df, type = 'class')
arguments:
- fitted_model: This is the object stored after model estimation. 
- df: Data frame used to make the prediction
- type: Type of prediction			
    - 'class': for classification			
    - 'prob': to compute the probability of each class			
    - 'vector': Predict the mean response at the node level	

تريد التنبؤ بالركاب الذين من المرجح أن ينجووا بعد الاصطدام من مجموعة الاختبار. يعني أنك ستعرف من بين هؤلاء الركاب الـ 209 من منهم سينجو أم لا.

predict_unseen <-predict(fit, data_test, type = 'class')

شرح الكود

  • توقع (fit، data_test، type = 'class'): توقع الفئة (0/1) لمجموعة الاختبار

اختبار الراكب الذي لم ينجح ومن فعل.

table_mat <- table(data_test$survived, predict_unseen)
table_mat

شرح الكود

  • table(data_test$survived,predict_unseen): قم بإنشاء جدول لحساب عدد الركاب المصنفين كناجين ومتوفين مقارنة بتصنيف شجرة القرار الصحيح في R

الإخراج:

##      predict_unseen
##        No Yes
##   No  106  15
##   Yes  30  58

تنبأ النموذج بشكل صحيح بمقتل 106 راكبًا، لكنه صنف 15 ناجيًا على أنهم موتى. وعلى سبيل القياس، أخطأ النموذج في تصنيف 30 راكبًا على أنهم ناجين بينما تبين أنهم ماتوا.

الخطوة 6) قياس الأداء

يمكنك حساب مقياس الدقة لمهمة التصنيف باستخدام الارتباك مصفوفة:

تشير الارتباك مصفوفة هو خيار أفضل لتقييم أداء التصنيف. الفكرة العامة هي حساب عدد المرات التي يتم فيها تصنيف المثيلات الحقيقية على أنها خاطئة.

قياس أداء أشجار القرار في R

يمثل كل صف في مصفوفة الارتباك هدفًا فعليًا، بينما يمثل كل عمود هدفًا متوقعًا. يأخذ الصف الأول من هذه المصفوفة الركاب الموتى (الفئة الخاطئة): تم تصنيف 106 منهم بشكل صحيح على أنهم أموات (صحيح سلبي)، بينما تم تصنيف الباقي خطأً على أنه ناجٍ (إيجابية كاذبة). الصف الثاني يعتبر الناجين، الفئة الإيجابية كانت 58 (صحيح إيجابي)، في حين أن صحيح سلبي كان 30.

يمكنك حساب اختبار الدقة من مصفوفة الارتباك :

قياس أداء أشجار القرار في R

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

accuracy_Test <- sum(diag(table_mat)) / sum(table_mat)

شرح الكود

  • sum(diag(table_mat)): مجموع القطر
  • sum(table_mat): مجموع المصفوفة.

يمكنك طباعة دقة مجموعة الاختبار:

print(paste('Accuracy for test', accuracy_Test))

الإخراج:

## [1] "Accuracy for test 0.784688995215311"

لقد حصلت على 78 بالمائة في مجموعة الاختبار. يمكنك تكرار نفس التمرين مع مجموعة بيانات التدريب.

الخطوة 7) ضبط المعلمات الفائقة

تحتوي شجرة القرار في R على معلمات مختلفة تتحكم في جوانب الملاءمة. في مكتبة شجرة القرار rpart، يمكنك التحكم في المعلمات باستخدام الدالة rpart.control(). في الكود التالي، يمكنك تقديم المعلمات التي ستقوم بضبطها. يمكنك الرجوع إلى المقالة القصيرة لمعلمات أخرى.

rpart.control(minsplit = 20, minbucket = round(minsplit/3), maxdepth = 30)
Arguments:
-minsplit: Set the minimum number of observations in the node before the algorithm perform a split
-minbucket:  Set the minimum number of observations in the final note i.e. the leaf
-maxdepth: Set the maximum depth of any node of the final tree. The root node is treated a depth 0

سنتصرف على النحو التالي:

  • إنشاء وظيفة لإرجاع الدقة
  • ضبط الحد الأقصى للعمق
  • قم بضبط الحد الأدنى لعدد العينات التي يجب أن تحتوي عليها العقدة قبل أن تتمكن من الانقسام
  • ضبط الحد الأدنى لعدد العينة التي يجب أن تحتوي عليها العقدة الورقية

يمكنك كتابة وظيفة لعرض الدقة. يمكنك ببساطة تغليف الكود الذي استخدمته من قبل:

  1. توقع: توقع_غير مرئي <- توقع (مناسب، data_test، اكتب = 'class')
  2. إنتاج الجدول: table_mat <- table(data_test$survived,predict_unseen)
  3. دقة الحساب: اختبار الدقة <- sum(diag(table_mat))/sum(table_mat)
accuracy_tune <- function(fit) {
    predict_unseen <- predict(fit, data_test, type = 'class')
    table_mat <- table(data_test$survived, predict_unseen)
    accuracy_Test <- sum(diag(table_mat)) / sum(table_mat)
    accuracy_Test
}

يمكنك محاولة ضبط المعلمات ومعرفة ما إذا كان بإمكانك تحسين النموذج على القيمة الافتراضية. للتذكير، عليك الحصول على دقة أعلى من 0.78

control <- rpart.control(minsplit = 4,
    minbucket = round(5 / 3),
    maxdepth = 3,
    cp = 0)
tune_fit <- rpart(survived~., data = data_train, method = 'class', control = control)
accuracy_tune(tune_fit)

الإخراج:

## [1] 0.7990431

مع المعلمة التالية:

minsplit = 4
minbucket= round(5/3)
maxdepth = 3cp=0

تحصل على أداء أعلى من النموذج السابق. تهنئة!

الملخص

يمكننا تلخيص الوظائف لتدريب خوارزمية شجرة القرار في R

المكتبة الهدف الوظيفة مبوبة المعلمات التفاصيل
rpart شجرة تصنيف القطارات في R ربارت () فئة الصيغة، مدافع، الطريقة
rpart تدريب شجرة الانحدار ربارت () أنوفا الصيغة، مدافع، الطريقة
rpart ارسم الأشجار rpart.plot() نموذج مناسب
قاعدة تنبأ تنبؤ() فئة النموذج المجهز، النوع
قاعدة تنبأ تنبؤ() غالبا النموذج المجهز، النوع
قاعدة تنبأ تنبؤ() ناقلات النموذج المجهز، النوع
rpart المعلمات السيطرة rpart.control() com.minsplit قم بتعيين الحد الأدنى لعدد الملاحظات في العقدة قبل إجراء الخوارزمية للتقسيم
com.minbucket قم بتعيين الحد الأدنى لعدد الملاحظات في الملاحظة النهائية، أي الورقة
أقصى عمق قم بتعيين الحد الأقصى لعمق أي عقدة من الشجرة النهائية. تتم معالجة العقدة الجذرية بعمق 0
rpart نموذج القطار مع معلمة التحكم ربارت () الصيغة، df، الطريقة، التحكم

ملاحظة: قم بتدريب النموذج على بيانات التدريب واختبار الأداء على مجموعة بيانات غير مرئية، أي مجموعة الاختبار.