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

Эта статья была мотивирована моим интересом к развитию визуальной интуиции о регрессиях, выполняемых моделями машинного обучения, такими как деревья решений и случайные леса. Я выбрал небольшой набор данных, позволяющий трехмерную визуализацию переменных и пространства решений моделей. Результаты очень хорошо иллюстрируют их нелинейное поведение и помогают лучше понять их способность решать сложные многомерные задачи.

Содержание

Введение
Деревья решений и случайные леса
Анализ данных
Планирование модели
Обзор
Разделение обучения/валидации
Искусственный набор данных
Вспомогательные таблицы< br /> Модели
Модель базового среднего
Модель линейной регрессии
Модель дерева решений
Модель случайного леса

Заключительные замечания

Введение

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

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

В этой статье мы собираемся визуализировать, как регрессии дерева решений и случайного леса выглядят в трехмерном пространстве, чтобы мы могли лучше понять многомерные нелинейные решения этих моделей. Давайте разберемся, как работают эти модели!

Деревья решений и случайные леса

Деревья решений получили такое название из-за своей древовидной структуры: из одного начального корневого узла (внутри которого находятся все выборочные данные) алгоритм итеративно ищет функции, чтобы найти оптимальное разделение. точка, которая отделяет подмножество данных, обобщающее часть цели с минимальной ошибкой. Затем для этого подмножества алгоритм повторяет процедуру поиска, чтобы найти оптимальную точку разделения, и так далее, пока не будет выполнен критерий остановки (например, не будет достигнут заданный уровень глубины). Как только этот конечный узел будет достигнут, следующие шаги алгоритма включают решение проблемы разделения для оставшихся подмножеств. Результатом является набор конечных узлов, генерирующих целевые прогнозы с минимальными ошибками. На рисунке ниже показан гипотетический пример дерева решений с двумя функциями (x1 и x2). Алгоритм использовал x2 для разделения узла 0 на ветви 1 и 4, а затем узла 1 на конечные узлы 2 и 3. После этого алгоритм решил оставшуюся часть, используя x1.

В этом примере все еще возможен третий уровень разделения, что еще больше снижает ошибку и количество выборок в конечных узлах, а также генерирует более детальные прогнозы целей. Однако это нежелательно, потому что дерево становится слишком приспособленным к данным и плохо обобщается на новые данные. Таким образом, важно контролировать глубину дерева, чтобы избежать переобучения.

Случайные леса получили такое название, поскольку представляют собой набор случайных деревьев решений. Отдельные деревья в «лесу» случайны в том смысле, что каждое из них обучается на случайной выборке признаков и наблюдений таким образом, что каждое дерево разрабатывает решение для наблюдаемой части данных с доступными ресурсами (признаками). . Затем лес объединяет выходные данные нескольких деревьев решений для достижения решения. Таким образом, эта модель снижает риск переобучения и повышает гибкость прогнозов. У моделей случайных лесов есть некоторые важные параметры, такие как количество деревьев решений, их глубина и количество их признаков (например, случайный выбор 30% признаков для каждого дерева).

В этой статье мы собираемся визуализировать работу этих моделей, их характеристики и поведение. Я выбрал небольшой набор данных для простоты. Давайте проведем предварительный анализ данных до начала работы по моделированию!

Анализ данных

Здесь я включил наиболее поучительный код Python, и вы можете найти на моей странице Github полную записную книжку и материалы этого учебного проекта. Фрагмент ниже показывает библиотеки Python.

# Import libraries
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

from matplotlib import gridspec
from mpl_toolkits.mplot3d import Axes3D

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.tree import export_graphviz
from sklearn.tree import export_text
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error

import warnings # For disabling a warning message from dtreeviz
import graphviz
from dtreeviz.trees import dtreeviz 
from IPython.display import SVG

Рассмотрим небольшой набор данных, доступный на Kaggle, состоящий из оценок студента, которые должны предсказываться двумя атрибутами — количеством посещенных курсов и временем обучения.

# Dataframe df gets the csv file 'Student_Marks.csv'
df = pd.read_csv('Student_Marks.csv')
display( df.head() ) # display the first 5 rows

Приведенная ниже описательная статистика показывает, что набор данных содержит 100 наблюдений, number_courses — это дискретная переменная в диапазоне от 3 до 8, time_study — непрерывная переменная в диапазоне от 0,096 до 7,957, а Оценка — это непрерывная переменная в диапазоне от 5,609 до 55,299.

Как мы видим на графиках ниже, функции имеют нелинейные отношения с целью. С учетом number_courses (левая панель) наблюдается размытая и слабая тенденция к более высоким оценкам при большем количестве курсов. Мы можем видеть две различные модели средних значений — около 19 для 3–5 курсов и около 30 для 6–8 курсов. С учетом time_study (правая панель) прослеживается четкая закономерность повышения оценок с увеличением времени обучения. Наблюдения вблизи обоих крайних значений отклоняются от линейности, и общая картина лучше описывается кривой, чем прямой линией.

Идея состоит в том, чтобы использовать обе функции — number_courses и time_study — для прогнозирования оценок учащихся. На трехмерном графике ниже мы можем наблюдать нелинейное поведение, которое мы собираемся моделировать, и визуализировать регрессии.

Планирование модели

Обзор

Подход, который я принял, включал разработку четырех моделей:

  1. Базовая средняя модель для первоначального эталона
  2. Модель линейной регрессии для визуализации линейного решения проблемы.
  3. Регрессионная модель дерева решений
  4. Модель случайного леса

Были приняты следующие процедуры:

  1. Данные были разделены на обучающий набор (70%) и проверочный набор (30%) для оценки производительности моделей с использованием новых, ранее невиданных данных.
  2. Эффективность проверки оценивалась по средней абсолютной ошибке (или MAE), которая показывает, насколько прогнозы в среднем отклоняются от фактических значений. Чем меньше MAE, тем ближе прогнозы к фактическим значениям.
  3. До окончательного дерева решений и моделей случайного леса я следил за оптимизацией параметров для обеспечения адекватного обобщения новых данных.
  4. Для визуализации поведения моделей я создал искусственный набор данных, состоящий из комбинации возможных значений number_courses (т. е. шести чисел от 3 до 8) и time_study со 100 равноотстоящими числами от 0,096 до 7,957. Эти 600 пар были обработаны обученными моделями, чтобы построить диапазон прогнозов.

Разделение обучения/проверки

Модели обучались на 70% данных, а 30% использовались для проверки. Фрагмент ниже показывает разделение поезд-тест.

# Separating the Features and the Target
X = df[['number_courses', 'time_study']]
y = df['Marks']

# Splitting the dataset into train and validation sets
X_train, X_valid, y_train, y_valid = train_test_split(X, y,
                                                      test_size=0.3, 
                                                      random_state=13)

# X_train: 70 rows, 2 columns, y_train: 70 rows, 1 column
# X_valid: 30 rows, 2 columns, y_valid: 30 rows, 1 column

Набор искусственных данных

Фрагмент ниже показывает создание 600 пар чисел с учетом комбинации между 6 возможными значениями number_courses и 100 возможными значениями time_study. Ниже показаны первые и последние пять строк этого набора данных.

# Create values for plotting the solution space of the models

# Generate a set of values for x1 and x2 coordinates
# x1 values range from 3 to 8
x1_values = np.arange(df['number_courses'].min(),df['number_courses'].max() + 1, 1) 

# x2 values range between min and max 
# This generates 100 equally spaced values between min and max:
x2_values = np.linspace(df['time_study'].min(), df['time_study'].max(), 100) 

# Combinatory between coordinates
x1_values, x2_values = np.meshgrid(x1_values, x2_values)
X_artif = np.array([x1_values.flatten(), x2_values.flatten()]).T

# Create a dataframe and display the first and last 5 rows
X_artif = pd.DataFrame(X_artif, columns=['number_courses', 'time_study'])

display( X_artif )

Вспомогательные таблицы

Я также создал таблицу для регистрации и отображения ошибок обучения и проверки, а также таблицу для регистрации прогнозов моделей в наборе проверки.

# List of models
models = ['Mean Model',
          'Linear Regression Model',
          'Decision Tree Model',
          'Random Forest Model']
# List of columns
cols = ['Error (Train)', 'Error (Validation)']

# DF with models as index
df_models = pd.DataFrame(index=models, columns=cols)

# Create a DF for the validation set and log models' predictions
df_valid = pd.concat([X_valid, y_valid], axis=1)

Модели

Базовая средняя модель

В качестве отправной точки я рассмотрел простую модель среднего значения, которая может служить в качестве эталона. Он просто возвращает среднее значение обучающего набора для любого предоставленного тестового набора, как показано в функции ниже:

# This function returns an array with the mean of the training set target
def mean_model(y_train, X_test):
    return np.array( [y_train.mean()] * X_test.shape[0] )

Фрагмент ниже показывает среднюю модель, примененную к обучающим и проверочным наборам для вычисления ошибок. Эта модель имела большую ошибку и отклонялась на 12,85 балла от фактических оценок проверочного набора.

# Predictions for the validation set
df_valid['y_mean'] = mean_model(y_train, X_valid)

# Generate predictions based on the artificial data
predictions_m = mean_model(y_train, X_artif)

# Compute MAE - Train and Valid
mae_train_m_model = mean_absolute_error(y_train, mean_model(y_train, X_train))
mae_valid_m_model = mean_absolute_error(y_valid, df_valid['y_mean'])

# Log in the DF and display the results
df_models.loc['Mean Model', 'Error (Train)'] = mae_train_m_model
df_models.loc['Mean Model', 'Error (Validation)'] = mae_valid_m_model

display( df_models.iloc[:1] )

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

Модель линейной регрессии

Теперь давайте исследуем линейную подгонку к данным. Фрагмент ниже показывает реализацию.

# Create model and fit to the training data
lr_model = LinearRegression()
lr_model.fit(X_train, y_train)

# Generate predictions based on validation data
df_valid['y_lr'] = lr_model.predict(X_valid)

# Generate predictions based on the artificial data
predictions_lr = lr_model.predict(X_artif)

# Compute MAE - Train and Valid
mae_train_lr_model = mean_absolute_error(y_train, lr_model.predict(X_train))
mae_valid_lr_model = mean_absolute_error(y_valid, df_valid['y_lr'])

# Log in the DF
df_models.loc['Linear Regression Model', 'Error (Train)'] = mae_train_lr_model
df_models.loc['Linear Regression Model', 'Error (Validation)'] = mae_valid_lr_model

# Compute the decrease % and display df_models
df_models['Valid. Error Decrease (%)'] = df_models['Error (Validation)'].pct_change() * 100
display( df_models.iloc[:2].round(2) )

На рисунке ниже мы видим, как линейная регрессия выглядит в трехмерном пространстве (правая панель). Модель линейной регрессии фиксирует общую закономерность, но прогнозы отклоняются в среднем на 2,73 пункта от фактических оценок проверочного набора (левая панель), что представляет собой уменьшение ошибки на 78,7% по сравнению со средней моделью.

Модель дерева решений

Глубина дерева — важный параметр деревьев решений, поэтому первым шагом было обучение моделей с разными уровнями глубины для анализа производительности проверки обучения. Как показано на рисунке ниже, для глубины дерева 3 у нас есть желаемое короткое расстояние между ошибками обучения и проверки, что указывает на обобщение невидимых данных. От глубины 4 до 9 мы видим небольшое уменьшение ошибки проверки, но уменьшение ошибки обучения указывает на переоснащение модели.

Фрагмент ниже показывает реализацию дерева решений с глубиной 3 и абсолютной ошибкой в ​​качестве критерия разделения узлов.

# Create and fit the model 
dt_model = DecisionTreeRegressor(max_depth=3,
                                 criterion='absolute_error',
                                 random_state=15)
dt_model.fit(X_train, y_train)

# Generate predictions based on the validation data
df_valid['y_dt'] = dt_model.predict(X_valid)

# Generate predictions based on the artificial data
predictions_dt = dt_model.predict(X_artif)

# Compute MAE - Train and Valid
mae_train_dt_model = mean_absolute_error(y_train, dt_model.predict(X_train))
mae_valid_dt_model = mean_absolute_error(y_valid, df_valid['y_dt'])

# Log in the DF
df_models.loc['Decision Tree Model', 'Error (Train)'] = mae_train_dt_model
df_models.loc['Decision Tree Model', 'Error (Validation)'] = mae_valid_dt_model

# Compute the decrease % and display df_models
df_models['Valid. Error Decrease (%)'] = df_models['Error (Validation)'].pct_change() * 100

display( df_models.iloc[:3].round(2) )

Эффективность проверки дерева решений показала снижение ошибки на 13,5% по сравнению с линейной регрессией. На рисунке ниже показано, как регрессия дерева решений выглядит в трехмерном пространстве (правая панель). Прогнозы отклонялись в среднем на 2,36 балла от фактических оценок проверочного набора (левая панель).

Из-за процесса расщепления поведение дерева характеризуется многоуровневым паттерном ответов, способным улавливать части нелинейности. Дерево с глубиной 3 дает 8 конечных узлов, и снизу вверх на графике (правая панель) мы можем наблюдать 8 уровней регрессии.

Посмотрев на регрессию дерева решений в трехмерном пространстве, мы теперь можем дополнительно изучить, как модель разработала решение. Библиотека Python dtreeviz обеспечивает хорошую визуализацию. Если вы помните мое краткое объяснение о деревьях решений, вы должны прочитать рисунок ниже в порядке нумерации. Узлы 0 и 1 использовали значения time_study для разделения меньших выборок более похожих точек, а узел 2 использовал number_courses для дальнейшего создания конечных узлов 3 и 4. Обратите внимание на предсказанные значения Mark. в терминальных узлах.

Эта модель дает 8 различных прогнозов, и по этой причине дерево решений несколько ограничено, чтобы охватить все соответствующие части распределения. Рисунок ниже, сгенерированный библиотекой graphviz, также обеспечивает хорошую визуализацию, которая включает абсолютную ошибку и количество выборок для каждого узла.

Проверка важности функций модели показала, что time_study является самой важной переменной.

Модель случайного леса

Количество оценок — важный параметр Random Forests, и первым шагом было тестирование моделей с разным количеством оценок. Другие параметры, такие как глубина дерева и количество признаков, оставались постоянными и не ограничивались. Как показано на рисунке ниже, оптимизация показала, что минимальное расстояние проверки поезда составляет около 0,40 с 25 оценщиками.

Фрагмент ниже показывает реализацию случайного леса с 25 оценщиками и абсолютной ошибкой в ​​качестве критерия разделения. Другие параметры модели имели стандартные характеристики, что означало, что глубина дерева не ограничивалась, и все деревья имели две характеристики. Случайная составляющая модели, полученная из бутстрепной выборки обучающей выборки.

# Create and fit the model that produced the minimum MAE
best_n_estimator = 25
rf_model = model = RandomForestRegressor(n_estimators=best_n_estimator,
                                         criterion='absolute_error',
                                         random_state=15)
rf_model.fit(X_train, y_train)

# Generate predicions based on the validation set
df_valid['y_rf'] = rf_model.predict(X_valid)

# Generate predictions based on the artificial set
predictions_rf = rf_model.predict(X_artif)

# Compute MAE - Train and Valid
mae_train_rf_model = mean_absolute_error(y_train, rf_model.predict(X_train))
mae_valid_rf_model = mean_absolute_error(y_valid, df_valid['y_rf'])

# Log in the DF
df_models.loc['Random Forest Model', 'Error (Train)'] = mae_train_rf_model
df_models.loc['Random Forest Model', 'Error (Validation)'] = mae_valid_rf_model

# Compute the decrease % and display df_models
df_models['Valid. Error Decrease (%)'] = df_models['Error (Validation)'].pct_change() * 100
display( df_models.round(2) )

Эффективность проверки случайным лесом показала снижение ошибки на 58,1% по сравнению с регрессией дерева решений и уменьшение ошибки на 63,8% по сравнению с линейной регрессией. Прогнозы отклонялись в среднем на 1 пункт от фактических оценок проверочного набора, будучи намного более точными, чем другие модели. Учитывая большое количество оценщиков, Random Forest может делать более детальные прогнозы. На рисунке ниже мы можем увидеть, как регрессия Random Forest выглядит в трехмерном пространстве.

Проверка важности функции модели показала, что time_study была самой важной функцией, и в этом случае важность number_courses была выше, чем наблюдаемая для модели дерева решений.

Заключительные замечания

Цель этой статьи состояла в том, чтобы исследовать нелинейное поведение деревьев решений и случайных лесов, особенно для визуализации регрессий и нелинейных решений моделей.

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

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