</>
Тема 6 из 6

Деревья решений и кросс-валидация

Дерево решений — модель, которая принимает решение серией простых вопросов, как в игре «да/нет»: «доход больше 50 тысяч?» → «да» → «есть просрочки?» → «нет» → выдать кредит. Её легко объяснить человеку, и она не требует масштабирования признаков.

Как дерево учится

На каждом шаге дерево перебирает признаки и пороги и выбирает вопрос вида «признак ≤ порог?», который лучше всего разделяет объекты на однородные группы. Затем то же повторяется для каждой группы. Порог ставится посередине между соседними значениями признака.

from sklearn.tree import DecisionTreeClassifier, export_text

X = [[25, 0], [40, 1], [60, 0], [80, 1], [90, 0], [30, 1]]   # доход, просрочки
y = ["нет", "нет", "да", "нет", "да", "нет"]

tree = DecisionTreeClassifier(max_depth=2, random_state=0).fit(X, y)
print(tree.predict([[70, 0]]))
print(export_text(tree, feature_names=["income", "late"]))

export_text печатает дерево вопросов — можно увидеть, как модель рассуждает.

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

Глубина и переобучение

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

Рост ограничивают параметрами:

  • max_depth — максимальное число вопросов от корня до ответа;
  • min_samples_leaf — минимальное число объектов в листе.

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

Регрессия деревом

DecisionTreeRegressor устроен так же, только в листе хранится не класс, а среднее значение ответа объектов, попавших в лист:

from sklearn.tree import DecisionTreeRegressor
model = DecisionTreeRegressor(max_depth=3, random_state=0).fit(X_train, y_train)

Важность признаков

После обучения в feature_importances_ лежит вклад каждого признака: насколько вопросы по нему уменьшили ошибку. Сумма важностей равна 1, признак, по которому не задано ни одного вопроса, получает 0.

import pandas as pd
imp = pd.Series(model.feature_importances_, index=X_train.columns)
print(imp.sort_values(ascending=False))

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

Кросс-валидация

Одна отложенная выборка даёт оценку, которая зависит от того, какие объекты в неё случайно попали. Надёжнее кросс-валидация: данные делятся на k частей, модель обучается k раз — каждый раз на всех частях, кроме одной, и проверяется на оставшейся.

from sklearn.model_selection import cross_val_score

scores = cross_val_score(DecisionTreeClassifier(max_depth=3, random_state=0), X, y, cv=5)
print(scores)          # пять оценок accuracy
print(scores.mean())   # средняя

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

Так подбирают параметры: для каждого кандидата считают среднюю оценку и берут лучший. Готовый инструмент для перебора — GridSearchCV, он делает то же самое за вас.

Деревья — основа более сильных моделей: RandomForestClassifier усредняет много деревьев, обученных на случайных частях данных, и обычно переобучается гораздо меньше одного дерева.

Итог

  • DecisionTreeClassifier / DecisionTreeRegressor — модель из вопросов «признак ≤ порог?»;
  • max_depth, min_samples_leaf — защита от переобучения;
  • feature_importances_ — вклад признаков, в сумме 1;
  • cross_val_score(model, X, y, cv=5) — пять оценок, их среднее надёжнее одной;
  • подбор параметра: перебрать значения и выбрать лучшее по кросс-валидации.