Аннотация

Это первая записная книжка из серии о деревьях решений. В первый входит следующее:

  • Базовая визуализация данных
  • Одно горячее кодирование
  • Моделирование дерева решений с параметрами по умолчанию (без какой-либо настройки)
  • Создание файла pdf для представления дерева решений
  • Небольшие подсказки (например, почему drop_first следует применять после одного горячего кодирования? Почему для дерева решений требуется одно горячее кодирование?)

В следующих тетрадях будут подробно описаны следующие темы:

  • Механизм моделей дерева решений
  • Объяснение параметров и настройка дерева решений
  • Детали обрезки для дерева решений
  • Применение случайного леса для тех же данных
  • Применение XGBoost для тех же данных
  • Применение Light GBM для тех же данных
  • Сравнение применяемых моделей дерева решений

Объяснение исследования

Дерево решений по классификации моделируется для прогнозирования успехов учащегося в математике в зависимости от его характеристик (пол, раса / этническая принадлежность, уровень образования родителей, обед, курс подготовки к экзаменам).

Модель создается с использованием только параметров по умолчанию, поэтому дерево очень большое. Уровень точности составляет 56% для тестового набора и 80% для обучающего набора. Эта большая разница между точностью комплектов поездов и тестов подразумевает случай переобучения.

Импорт библиотек

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn import preprocessing
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
from sklearn import tree
import graphviz 
import numpy as np
from sklearn.externals.six import StringIO  
from IPython.display import Image  
from sklearn.tree import export_graphviz
import pydotplus

Загрузка и отображение набора данных

dataset = pd.read_csv("StudentsPerformance.csv")
dataset.head()

Визуализации

ax = sns.countplot(x="gender", data=dataset)

ax = sns.countplot(x="race/ethnicity", data=dataset)

ax = sns.countplot(y="parental level of education", data=dataset)

ax = sns.countplot(x="lunch", data=dataset)

ax = sns.countplot(x="test preparation course", data=dataset)

ax = sns.distplot(dataset["math score"]);

Обработка данных

Давайте создадим новую функцию, названную математической оценкой. Оценка по математике считается удовлетворительной, если оценка по математике выше 60, оценка по математике - Неудача, если оценка по математике ниже 60. Это всего лишь предположение. После создания нового столбца я удалил ненужные столбцы и снова проверил последнюю форму набора данных.

* Подсказка: это хороший пример изменения некоторых строк столбца в соответствии с некоторыми условными выражениями на значениях других столбцов.

dataset["math grade"] = ""
dataset.loc[(dataset["math score"] >= 60), "math grade"] = "Pass"
dataset.loc[(dataset["math score"] < 60), "math grade"] = "Fail"
dataset.drop(columns=['math score', 'reading score', 'writing score'], inplace=True)
dataset.head()

Одно горячее кодирование

* Подсказка: одна горячая кодировка необходима, если вы используете библиотеку scikit-learn для моделирования.

Scikit-learn использует только числовые функции, и эти числовые функции рассматриваются как непрерывные числовые значения. В нашем случае функция гонки включает группу A, группу B, группу C, группу D, группу E, и она будет закодирована как 1,2,3,4,5 в одном столбце, если вы используете только кодировку метки, а не одну горячую кодировку. До сих пор все в порядке, но тут начинаются проблемы. Поскольку модель предполагает, что все числовые значения являются непрерывными, вы увидите такие внутренние узлы (точку разделения): «гонка‹ 4, что означает расу группы A ‹расу группы B», что очень странно. Странно, потому что между расовыми ценностями нет преемственности. Было бы то же самое, если бы нашей фичей был цвет. Это может быть приемлемо для родительского уровня образования, если вы правильно установите порядок, потому что существует преемственность. Конечно, степень магистра выше, чем степень бакалавра, и она выше, чем в некоторых колледжах и т. Д. Вот почему мы должны применять одно горячее кодирование, если мы используем scikit-learn.

* Подсказка: удаление одного из столбцов с горячим кодированием - хороший подход, в противном случае вес и влияние функций на модель могут быть выше или ниже.

Предположим, что в нашем наборе данных есть только возрастные и гендерные особенности. Поскольку возраст - это непрерывная числовая переменная, которая хороша для scikit-learn, но мы должны применить одну горячую кодировку для пола. После его применения у нас есть 3 функции: возраст, пол_мужской, пол_женщина. Приведем пример, допустим, что наш человек - мужчина, так что значения полового члена - 1, а пол_женщины - 0, а допустим, возраст - 20. Звучит нормально, но на самом деле это не так. Допустим, вы разговариваете с моделью, проверяете возраст и говорите: «Этому человеку 20 лет». Затем вы проверяете функцию «пол_мужской» и говорите «этот человек - мужчина». Затем вы проверяете столбец пол_женщина и говорите «этот человек не женщина (что означает, что человек мужчина)». Чтобы один раз сообщить модели о поле человека, вам нужно отбросить один из столбцов с горячим кодированием, и drop_first = True сделает это за нас.

one_hot = pd.get_dummies(dataset['gender'], prefix='gender', drop_first=True)
dataset = dataset.join(one_hot)
one_hot = pd.get_dummies(dataset['race/ethnicity'], prefix='race/ethnicity', drop_first=True)
dataset = dataset.join(one_hot)
one_hot = pd.get_dummies(dataset['parental level of education'], prefix='parental level of education', drop_first=True)
dataset = dataset.join(one_hot)
one_hot = pd.get_dummies(dataset['lunch'], prefix='lunch', drop_first=True)
dataset = dataset.join(one_hot)
one_hot = pd.get_dummies(dataset['test preparation course'], prefix='test preparation course', drop_first=True)
dataset = dataset.join(one_hot)
dataset.head()

Поезд - тестовый сплит

Разделение набора данных на два набора данных: наборы данных для обучения, тестирования и слепого удержания. Соотношение 70/20/10.

* Подсказка: разделение и хранение наборов данных вслепую - важная концепция. Поскольку мы пытаемся настроить модель с помощью обучающих и тестовых наборов данных, возможно, что в определенной точке модель может даже переобучиться для тестирования набора данных. Вот почему мы храним набор данных вслепую и используем его очень часто, только когда мы думаем, что наша модель настроена и работает очень хорошо.

* Подсказка: параметр random_state используется, если вы хотите иметь точно такие же наборы данных для обучения и тестирования и если вы не хотите, чтобы они были разными при каждом разбиении, когда вы запускаете записную книжку с самого начала. Неважно, какое число, вы можете установить 5, 77, 100 как угодно. Единственный важный момент - установить такое же число. Для меня в данном случае это 21 год.

data_train, data_test_hold = train_test_split(dataset, test_size=0.30, random_state=21)
data_test, data_hold = train_test_split(data_test_hold, test_size=0.33, random_state=21)

Создание числовых массивов для модели

columns_move = ["gender", "race/ethnicity", "parental level of education", "lunch", "test preparation course", "gender_male", "race/ethnicity_group B", "race/ethnicity_group C", "race/ethnicity_group D", "race/ethnicity_group E", "parental level of education_bachelor's degree", "parental level of education_high school", "parental level of education_master's degree", "parental level of education_some college", "parental level of education_some high school", "lunch_standard", "test preparation course_none"]
y_train = data_train["math grade"].values
X_train = data_train[columns_move].values
y_test = data_test["math grade"].values
X_test = data_test[columns_move].values

Создать модель

Тюнинга для модели нет. Он создается со значениями по умолчанию DecisionTreeClassifier. Вот почему это будет очень-очень большое дерево решений по переобучению :).

model = DecisionTreeClassifier(criterion='gini', splitter='best', 
          max_depth=None, min_samples_split=2, 
          min_samples_leaf=1, min_weight_fraction_leaf=0.0, 
          max_features=None, random_state=None, 
          max_leaf_nodes=None, min_impurity_decrease=0.0, 
          min_impurity_split=None, class_weight=None, presort=False)

Обучение модели

model.fit(X_train[:,5:], y_train)

Прогнозы

y_pred = model.predict(X_test[:,5:])
print("Model Accuracy (%):", accuracy_score(y_test,y_pred)*100)
Model Accuracy (%): 56.72
a = pd.DataFrame(confusion_matrix(y_test,y_pred), columns=['prediction/fail', 'prediction/pass'], index=['actual/fail', 'actual/pass'])
print("Confusion Matrix:")
print(a)
Confusion Matrix:
             prediction/fail  prediction/pass
actual/fail         28               40
actual/pass         47               86
print("Classification Report:")
print("")
print(classification_report(y_test,y_pred))
Classification Report:

             precision    recall  f1-score   support

       Fail       0.37      0.41      0.39        68
       Pass       0.68      0.65      0.66       133

avg / total       0.58      0.57      0.57       201

Точность модели 56%. Как все, что мы знаем, всегда недостаточно проверять только точность, поэтому давайте посмотрим, как мы можем получить матрицу путаницы, точность, отзыв и оценки f1 с помощью библиотеки scikit-learn.

Есть так много ресурсов, объясняющих эти концепции. Я могу посоветовать статью Точность, прецизионность, отзыв или F1? пользователя Ку Пинг Шунг. Вот ссылка: https://towardsdatascience.com/accuracy-precision-recall-or-f1-331fb37c5cb9

Давайте сделаем прогнозы, используя набор данных поезда, и сравним его с результатами из набора тестов. Цель состоит в том, чтобы проверить, есть ли переоснащение. Как я сказал в начале, 56% и 80% сильно отличаются друг от друга, 56% - это почти пустяковая монета, с другой стороны, 80% - многообещающий показатель точности.

y_pred_train = model.predict(X_train[:,5:])
print("Model Accuracy: %.2f" % (accuracy_score(y_train,y_pred_train)*100), "%")
Model Accuracy: 79.86 %

График дерева решений

dot_data = StringIO()
export_graphviz(model, out_file=dot_data,  
                filled=True, rounded=True,
                special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())  
Image(graph.create_png())

Поскольку оно не настроено и не моделируется с параметрами по умолчанию, дерево бессмысленно огромно для такого набора данных. В следующих статьях у нас будут более простые и лучшие деревья.

Комбинированный фрейм данных результатов с прогнозами

columns_move.append("math grade test")
columns_move.append("math grade pred")
y_pred = y_pred.reshape(len(y_pred),1)
y_test = y_test.reshape(len(y_test),1)
resultarray = np.append(X_test, y_test, axis=1)
resultarray = np.append(resultarray, y_pred, axis=1)
resultdf = pd.DataFrame(resultarray, columns=columns_move)
resultdf.drop(columns=["gender_male", "race/ethnicity_group B", "race/ethnicity_group C", "race/ethnicity_group D", "race/ethnicity_group E", "parental level of education_bachelor's degree", "parental level of education_high school", "parental level of education_master's degree", "parental level of education_some college", "parental level of education_some high school", "lunch_standard", "test preparation course_none"], inplace=True)
resultdf.head(20)