Один шаг, чтобы деревья принятия решений давали лучшие результаты

Один простой шаг к более успешному принятию решений

Фон, осуществление и улучшение модели

Среди деревьев (фото автора)

Решающие деревья (DT) слишком быстро отбрасываются.

Это происходит так:

DT обучается. Присутствует естественное переобучение. Гиперпараметры настраиваются (неудовлетворительно). Наконец, дерево заменяется случайным лесом.

Хотя это может быть быстрым приростом производительности, замена приоритетом ставит “черный ящик”. Это не идеально. Только DT может производить интуитивные результаты, предлагать руководителям бизнеса возможность сравнивать выбор между уступками и давать им важную роль в улучшении процесса.

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

(Пометка: Люди часто спрашивают: “Случайные леса дают важность признакам, это не объясняет, какие признаки являются важными?” Не совсем. Важность признаков практически сразу же интерпретируется как причинные факторы (например, признаки, имеющие зависимость от целевого показателя), но они не более чем модельные факторы. В то время как это полезно для техника в этом отношении, важность признаков обычно: (1) неудо-бва в слабых моделях (2) переоценивают признаки с большим кардинальным числом и (3) смещаются в сторону коррелированных признаков. Это совсем другое направление исследования, но в общем-то это и есть суть.)

Как решающие деревья принимают решения

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

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

Приблизительно DT разделяет классы, создавая ортогональные границы принятия решения (перпендикулярные разделения) между классами во всех данных, которые вы передаете в него. Это делается жадным алгоритмическим способом – сначала выбираются признаки, которые лучше всего разделяют, затем продвигаются к менее оптимальным разделениям в других признаках.

Мы можем визуально проверить наши признаки на ортогональные границы принятия решений. Давайте посмотрим на признаки из публично доступного набора данных о раке груди ниже. На верхнем графике ниже, построение «Worst Perimeter» и «Mean Perimeter» приводит к хорошей ортогональной границе принятия решений, которая может хорошо отделять злокачественные и доброкачественные классы. Так что это были бы отличные признаки для включения в модель DT.

Изображение автора

Нижний график выше показывает «Mean Area» и «Mean Perimeter», для которых DT создал ортогональные границы принятия решений (как оно делает это по умолчанию), но они являются излишне сложными. Возможно, здесь было бы лучше использовать диагональное разделение, но именно так разделяют классификаторы DT. Кроме того, DT очень чувствителен даже к небольшим изменениям в обучающих данных, таким как выбросы, что известно приводит к существенно разным деревьям.

Для адаптации этого уникального и базового механизма DT – и в конечном счете для улучшения производительности и обобщаемости – можно применить метод главных компонент (PCA).

PCA улучшает производительность DT по двум важным направлениям:

(1) выстраивает ключевые признаки вместе (которые объясняют наибольшую вариацию)

(2) уменьшает пространство признаков

Фактически, процесс PCA + DT естественным образом выявил признаки “Worst Perimeter” и “Mean Perimeter”, которые вы видите выше на верхнем графике. Это две наиболее предсказуемые переменные и, как не удивительно, имеют отличную ортогональную границу принятия решений.

Внедрение процесса

Напомним, что PCA предназначен для непрерывных данных. Набор данных о раке груди полностью состоит из непрерывных переменных. (Еще одна пометка: Я вижу, как PCA используется на категориальных переменных – не рекомендуется. Номинальные уровни не имеют подразумеваемого расстояния, уровни порядка не всегда равноотстоящие, и применение представления расстояний к дискретным функциям обычно приводит к образованию неконтролируемых переменных. Еще разговор на другой раз.)

Давайте начнем с загрузки необходимых пакетов и преобразования нашего набора данных о раке груди в признакиX и целевую переменнуюy.

import numpy as npfrom sklearn.tree import DecisionTreeClassifierfrom sklearn.decomposition import PCAfrom sklearn.datasets import load_breast_cancerfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import accuracy_score, confusion_matrix, precision_score, recall_scoreimport matplotlib.pyplot as pltimport seaborn as sns# Загрузка набора данных о раке грудифeatures = load_breast_cancer()X = features.datay = features.target

Для осмотра можно вызвать начало кадра данных этого набора данных.

cancer = load_breast_cancer()df = pd.DataFrame(np.c_[cancer['data'], cancer['target']],                  columns= np.append(cancer['feature_names'], ['target']))df.head()
Picture by author

Сначала обучите DecisionTreeClassifier без использования PCA и соберите эти предсказания (original_predictions).

# Разделение данных на обучающую и тестовую выборкиX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)# Обучение модели Decision Tree Classifier на исходном наборе данныхoriginal_tree = DecisionTreeClassifier(random_state=42)original_tree.fit(X_train, y_train)# Предсказания для исходного набора данныхoriginal_predictions = original_tree.predict(X_test)

Теперь примените PCA, чтобы выбрать минимальное количество измерений, которое может объяснить наибольшую часть дисперсии в обучающей выборке. Вместо произвольного выбора этого количества измерений можно использовать “метод локтя” для определения количества измерений, объясняющих 99% дисперсии, как захардкожено ниже.

# Поиск оптимального количества компонентов PCA с использованием метода локтяpca = PCA()pca.fit(X_train)explained_variance = pca.explained_variance_ratio_cumulative_explained_variance = np.cumsum(explained_variance)# Визуализация объясненной дисперсииplt.plot(range(1, len(cumulative_explained_variance) + 1), cumulative_explained_variance, marker='o')plt.xlabel('Количество компонентов')plt.ylabel('Накопленная объясненная дисперсия')plt.title('Объясненная дисперсия PCA')plt.grid()plt.show()# Определение оптимального количества компонентов (точка локтя)optimal_num_components = np.where(cumulative_explained_variance >= 0.99999)[0][0] + 1print(f"Оптимальное количество компонентов PCA: {optimal_num_components}")

Визуально, основываясь на месте изгиба графика, можно увидеть, что 6 компонентов PCA объясняют 99% дисперсии обучающей выборки.

Picture by author

Теперь примените PCA для извлечения 6 основных компонентов на обучающей выборке. Можно сделать это с помощью сингулярного разложения (SVD), которое является стандартным методом факторизации матрицы (процесс, находящийся вне рамок здесь). Как и ранее, обучите модель DecisionTreeClassifier на обучающей выборке, преобразованной с использованием PCA, и соберите эти предсказания (pca_predictions).

# Применение PCA с оптимальным количеством компонентовpca = PCA(n_components=optimal_num_components, svd_solver="full")X_train_pca = pca.fit_transform(X_train)X_test_pca = pca.transform(X_test)# Обучение модели Decision Tree Classifier на преобразованной PCA-обучающей выборкеpca_tree = DecisionTreeClassifier(random_state=42)pca_tree.fit(X_train_pca, y_train)# Предсказания для преобразованной PCA-выборкиpca_predictions = pca_tree.predict(X_test_pca)

# Матрица ошибокpca_cm = confusion_matrix(y_test, pca_predictions)# Точность и полнота для исходного набора данныхoriginal_precision = precision_score(y_test, original_predictions, average=’weighted’)original_recall = recall_score(y_test, original_predictions, average='weighted')original_accuracy = accuracy_score(y_test, original_predictions)# Точность и полнотапca_precision = precision_score(y_test, pca_predictions)pca_recall = recall_score(y_test, pca_predictions)pca_accuracy = accuracy_score(y_test, pca_predictions)# Вывод точности и полнотыprint(f"Исходный набор данных - Точность: {original_precision:.4f}, Полнота: {original_recall:.4f}, Точность: {original_accuracy:.4f}")print(f"Преобразованный набор данных PCA - Точность: {pca_precision:.4f}, Полнота: {pca_recall:.4f}, Точность: {pca_accuracy:.4f}")

Теперь мы можем сравнить наши оригинальные предсказания (без преобразования PCA) с предсказаниями PCA, чтобы пронаблюдать относительное улучшение наших метрик оценки точности, точности и полноты.

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

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

# Построение матриц ошибокplt.figure(figsize=(12, 5))plt.subplot(1, 2, 1)sns.heatmap(original_cm, annot=True, fmt="d", cmap="Blues", xticklabels=data.target_names, yticklabels=data.target_names)plt.title("Матрица ошибок оригинального решающего дерева\nТочность: {:.2f}, Полнота: {:.2f}".format(original_precision, original_recall))plt.xlabel("Предсказанные")plt.ylabel("Фактические")plt.subplot(1, 2, 2)sns.heatmap(pca_cm, annot=True, fmt="d", cmap="Blues", xticklabels=data.target_names, yticklabels=data.target_names)plt.title("Матрица ошибок решающего дерева с PCA\nТочность: {:.2f}, Полнота: {:.2f}".format(pca_precision, pca_recall))plt.xlabel("Предсказанные")plt.ylabel("Фактические")plt.tight_layout()plt.show()
Изображение автора

Наконец, стоит выяснить, какие из наших исходных признаков используются для генерации 6 главных компонент. Технически, PCA генерирует новые признаки, которые являются линейными комбинациями исходных признаков. Эти новые признаки ортогональны друг другу и ранжируются по объясняемой ими дисперсии. Однако вызов компонента_атрибута может идентифицировать признаки, используемые для создания этих компонентов.

# Получение объясненной доли дисперсии каждой главной компонентыexplained_variance_ratio = pca.explained_variance_ratio_# Получение компонентов PCApca_components = pca.components_# Создание DataFrame для отображения вкладов исходных признаков в каждую главную компонентудф_components = pd.DataFrame(data=pca_components, columns=data.feature_names)# Вывод наиболее значимых признаков, вносящих вклад в каждую главную компонентуfor i in range(optimal_num_components):    print(f"Верхние признаки для PC{i+1}:")    sorted_features = df_components.iloc[i].abs().sort_values(ascending=False)    print(sorted_features.head())    print("\nОбъясненная доля дисперсии:", explained_variance_ratio[i])    print("=" * 50)

Таким образом, для выбранных нами 6 главных компонент модель использует следующие 5 признаков:

Изображение автора

Вывод

Решающие деревья слишком рано отбрасываются в пользу более эффективных алгоритмов. Важно достичь высокой производительности, но это может не быть наилучшим решением — окончательное решение зависит от потребностей заказчика и объяснения причин, по которым модель предлагает определенный результат (см. “объяснимый ИИ”).

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

Спасибо за чтение. Я буду рад пообщаться с кем-либо на LinkedIn! Если у вас есть интересные задачи по науке о данных, с которыми вы сталкиваетесь в настоящее время, пожалуйста, оставьте комментарий или напишите мне личное сообщение, и я с удовольствием попытаюсь исследовать и написать об этом.

Мои последние статьи:

Отладка ошибок логистической регрессии — что они означают и как исправить

Использование байесовых сетей для прогнозирования объема дополнительных услуг в больницах

Почему балансировка классов завышена