В этом посте мы рассмотрим наиболее важные параметры модели дерева решений и то, как они влияют на нашу модель с точки зрения чрезмерной и недостаточной подгонки.
Мы будем использовать Titanic Data from kaggle. Для этого поста мы будем выполнять как можно меньше функций, так как это не является целью этого поста.
import numpy as np import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt
Загрузить данные поезда
# get titanic & test csv files as a DataFrame train = pd.read_csv(“input/train.csv”) print train.shape > (891, 12)
Проверить отсутствующие значения
#Checking for missing data NAs = pd.concat([train.isnull().sum()], axis=1, keys=[‘Train’]) NAs[NAs.sum(axis=1) > 0] >Age 177 Cabin 687 Embarked 2
Мы удалим столбцы «Кабина», «Имя» и «Билет», так как они требуют некоторой обработки для извлечения полезных функций.
# At this point we will drop the Cabin feature since it is missing a lot of the data train.pop(‘Cabin’) train.pop(‘Name’) train.pop(‘Ticket’) train.shape > (891, 9)
Заполните отсутствующие значения возраста средним значением
# Filling missing Age values with mean train[‘Age’] = train[‘Age’].fillna(train[‘Age’].mean())
Заполните отсутствующие значения "Embarked" наиболее частым значением.
# Filling missing Embarked values with most common value train[‘Embarked’] = train[‘Embarked’].fillna(train[‘Embarked’].mode()[0])
«Pclass» - это категориальная функция, поэтому мы преобразуем его значения в строки.
train[‘Pclass’] = train[‘Pclass’].apply(str)
Давайте выполним простое горячее кодирование категориальных функций.
# Getting Dummies from all other categorical vars for col in train.dtypes[train.dtypes == ‘object’].index: for_dummy = train.pop(col) train = pd.concat([train, pd.get_dummies(for_dummy, prefix=col)], axis=1)
# Prepare data for training models labels = train.pop(‘Survived’)
Для тестирования мы решили разделить наши данные на 75% для обучения и 25% для тестирования.
from sklearn.model_selection import train_test_split x_train, x_test, y_train, y_test = train_test_split(train, labels, test_size=0.25)
Давайте сначала подберем дерево решений с параметрами по умолчанию, чтобы получить базовое представление о производительности.
from sklearn.tree import DecisionTreeClassifier dt = DecisionTreeClassifier() dt.fit(x_train, y_train) > DecisionTreeClassifier(class_weight=None, criterion=’gini’, max_depth=None, max_features=None, max_leaf_nodes=None, min_impurity_split=1e-07, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=None, splitter=’best’) y_pred = dt.predict(x_test)
Мы будем использовать AUC (Площадь под кривой) в качестве метрики оценки. Наше целевое значение двоичное, поэтому это проблема двоичной классификации. AUC - хороший способ оценки для этого типа проблем.
from sklearn.metrics import roc_curve, auc false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred) roc_auc = auc(false_positive_rate, true_positive_rate) roc_auc > 0.72657348804500699
Максимальная глубина
Первый параметр для настройки - max_depth. Это показывает, насколько глубоким может быть дерево. Чем глубже дерево, тем больше в нем разделений и больше информации о данных. Мы подбираем дерево решений с глубиной от 1 до 32 и наносим на график оценки AUC для обучения и тестирования.
max_depths = np.linspace(1, 32, 32, endpoint=True) train_results = [] test_results = [] for max_depth in max_depths: dt = DecisionTreeClassifier(max_depth=max_depth) dt.fit(x_train, y_train) train_pred = dt.predict(x_train) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_train, train_pred) roc_auc = auc(false_positive_rate, true_positive_rate) # Add auc score to previous train results train_results.append(roc_auc) y_pred = dt.predict(x_test) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred) roc_auc = auc(false_positive_rate, true_positive_rate) # Add auc score to previous test results test_results.append(roc_auc) from matplotlib.legend_handler import HandlerLine2D line1, = plt.plot(max_depths, train_results, ‘b’, label=”Train AUC”) line2, = plt.plot(max_depths, test_results, ‘r’, label=”Test AUC”) plt.legend(handler_map={line1: HandlerLine2D(numpoints=2)}) plt.ylabel(‘AUC score’) plt.xlabel(‘Tree depth’) plt.show()
Мы видим, что наша модель подходит для больших значений глубины. Дерево идеально предсказывает все данные поезда, однако не может обобщить результаты для новых данных.
min_samples_split
min_samples_split представляет минимальное количество выборок, необходимое для разделения внутреннего узла. Это может варьироваться от рассмотрения хотя бы одной выборки в каждом узле до рассмотрения всех выборок в каждом узле. Когда мы увеличиваем этот параметр, дерево становится более ограниченным, поскольку ему приходится рассматривать больше выборок на каждом узле. Здесь мы будем варьировать параметр от 10% до 100% образцов.
min_samples_splits = np.linspace(0.1, 1.0, 10, endpoint=True) train_results = [] test_results = [] for min_samples_split in min_samples_splits: dt = DecisionTreeClassifier(min_samples_split=min_samples_split) dt.fit(x_train, y_train) train_pred = dt.predict(x_train) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_train, train_pred) roc_auc = auc(false_positive_rate, true_positive_rate) train_results.append(roc_auc) y_pred = dt.predict(x_test) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred) roc_auc = auc(false_positive_rate, true_positive_rate) test_results.append(roc_auc) from matplotlib.legend_handler import HandlerLine2D line1, = plt.plot(min_samples_splits, train_results, ‘b’, label=”Train AUC”) line2, = plt.plot(min_samples_splits, test_results, ‘r’, label=”Test AUC”) plt.legend(handler_map={line1: HandlerLine2D(numpoints=2)}) plt.ylabel(‘AUC score’) plt.xlabel(‘min samples split’) plt.show()
Мы ясно видим, что когда мы рассматриваем 100% выборок на каждом узле, модель не может узнать достаточно о данных. Это неподходящий случай.
min_samples_leaf
min_samples_leaf: минимальное количество выборок, которое требуется для конечного узла. Этот параметр аналогичен параметру min_samples_splits, однако он описывает минимальное количество выборок выборок на листьях, в основании дерева.
min_samples_leafs = np.linspace(0.1, 0.5, 5, endpoint=True) train_results = [] test_results = [] for min_samples_leaf in min_samples_leafs: dt = DecisionTreeClassifier(min_samples_leaf=min_samples_leaf) dt.fit(x_train, y_train) train_pred = dt.predict(x_train) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_train, train_pred) roc_auc = auc(false_positive_rate, true_positive_rate) train_results.append(roc_auc) y_pred = dt.predict(x_test) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred) roc_auc = auc(false_positive_rate, true_positive_rate) test_results.append(roc_auc) from matplotlib.legend_handler import HandlerLine2D line1, = plt.plot(min_samples_leafs, train_results, ‘b’, label=”Train AUC”) line2, = plt.plot(min_samples_leafs, test_results, ‘r’, label=”Test AUC”) plt.legend(handler_map={line1: HandlerLine2D(numpoints=2)}) plt.ylabel(‘AUC score’) plt.xlabel(‘min samples leaf’) plt.show()
Вывод такой же, как и по предыдущему параметру. Увеличение этого значения может привести к недостаточной подгонке.
max_features
max_features представляет количество функций, которые следует учитывать при поиске лучшего разделения.
max_features = list(range(1,train.shape[1])) train_results = [] test_results = [] for max_feature in max_features: dt = DecisionTreeClassifier(max_features=max_feature) dt.fit(x_train, y_train) train_pred = dt.predict(x_train) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_train, train_pred) roc_auc = auc(false_positive_rate, true_positive_rate) train_results.append(roc_auc) y_pred = dt.predict(x_test) false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred) roc_auc = auc(false_positive_rate, true_positive_rate) test_results.append(roc_auc) from matplotlib.legend_handler import HandlerLine2D line1, = plt.plot(max_features, train_results, ‘b’, label=”Train AUC”) line2, = plt.plot(max_features, test_results, ‘r’, label=”Test AUC”) plt.legend(handler_map={line1: HandlerLine2D(numpoints=2)}) plt.ylabel(‘AUC score’) plt.xlabel(‘max features’) plt.show()
Это тоже случай переобучения. Неожиданно получить переобучение для всех значений max_features. Однако, согласно документации sklearn для дерева решений, поиск разделения не прекращается до тех пор, пока не будет найден хотя бы один действительный раздел образцов узлов, даже если для этого требуется эффективно проверить более чем max_features функций.
Серия inDepth исследует, как параметры модели влияют на производительность с точки зрения переобучения и недостаточного подгонки. Это первый пост из серии. Следующий будет для Рэндома Форреста.