Деревья решений: основа ансамблей
Стройте и визуализируйте деревья решений с помощью rpart и изучайте компромисс между смещением и дисперсией
«Деревья решений: основа ансамблей» — бесплатный урок R Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.
Как деревья решений выполняют разделение
Дерево решений рекурсивно разделяет пространство признаков на прямоугольные области. На каждом узле алгоритм перебирает все признаки и все возможные точки разделения, чтобы найти разделение, наилучшим образом отделяющее целевую переменную. В результате получается дерево правил «если — то — иначе».
library(rpart)
# Fit a classification tree
tree <- rpart(
Species ~ .,
data = iris,
method = 'class' # use 'anova' for regression
)
print(tree)Критерии разделения: GINI и энтропия
Критерий разделения измеряет неоднородность узла. Неоднородность по GINI оценивает вероятность ошибочной классификации случайно выбранного элемента. Энтропия (прирост информации) измеряет уменьшение информационного беспорядка. Обычно оба критерия дают похожие деревья; GINI вычисляется быстрее и используется по умолчанию в rpart.
# Default: Gini impurity (parms = list(split = 'gini'))
tree_gini <- rpart(Species ~ ., data = iris, method = 'class')
# Using information gain (entropy)
tree_entropy <- rpart(
Species ~ ., data = iris, method = 'class',
parms = list(split = 'information')
)
cat('Gini root split:', tree_gini$frame$var[1])
cat('Entropy root split:', tree_entropy$frame$var[1])printcp() — Таблица сложности
printcp(tree) выводит таблицу параметров сложности (CP). В каждой строке указаны размер дерева (количество разбиений), относительная ошибка на обучающих данных и ошибка при перекрёстной проверке (xerror). Таблица CP используется для выбора оптимального уровня обрезки.
tree <- rpart(medv ~ ., data = MASS::Boston, method = 'anova',
control = rpart.control(minsplit = 5, cp = 0.001))
printcp(tree)
# Identify the CP with minimum cross-validated error
best_cp <- tree$cptable[
which.min(tree$cptable[, 'xerror']),
'CP'
]
cat('Best CP:', best_cp)prune() — Обрезка дерева
prune(tree, cp) обрезает дерево до уровня сложности, заданного параметром cp. Обрезка предотвращает переобучение, удаляя ветви, которые дают небольшую прогностическую ценность. Стандартный подход: найти CP, при котором ошибка CV минимальна, а затем выполнить обрезку.
best_cp <- tree$cptable[
which.min(tree$cptable[, 'xerror']), 'CP'
]
pruned_tree <- prune(tree, cp = best_cp)
cat('Original tree nodes:', nrow(tree$frame))
cat('Pruned tree nodes:', nrow(pruned_tree$frame))rpart.plot() — Визуализация дерева
rpart.plot(tree) из пакета rpart.plot создаёт наглядное цветное представление дерева решений. В каждом внутреннем узле отображается правило разбиения, а в каждом листе — предсказанный класс и доля обучающих наблюдений.
library(rpart.plot)
tree <- rpart(Species ~ ., data = iris, method = 'class')
pruned <- prune(tree, cp = 0.02)
rpart.plot(
pruned,
type = 4, # split labels on branches
extra = 104, # show class + probability
fallen.leaves = TRUE
)Компромисс между смещением и дисперсией
Глубокое необрезанное дерево имеет низкое смещение (почти идеально соответствует обучающим данным), но высокую дисперсию (небольшие изменения в данных приводят к совершенно разным деревьям). Неглубокое или обрезанное дерево имеет более высокое смещение, но меньшую дисперсию. Оптимальное дерево уравновешивает эти два источника ошибки.
Ансамблевые методы, такие как случайные леса и бустинг, напрямую используют этот компромисс.
# Deep tree = low bias, high variance (overfits)
deep_tree <- rpart(medv ~ ., data = MASS::Boston,
control = rpart.control(minsplit = 2, cp = 0))
# Shallow tree = high bias, low variance (underfits)
shallow_tree <- rpart(medv ~ ., data = MASS::Boston,
control = rpart.control(maxdepth = 2))
cat('Deep nodes:', nrow(deep_tree$frame))
cat('Shallow nodes:', nrow(shallow_tree$frame))Переобучение дерева решений
Необрезанное дерево может добиться нулевой ошибки на обучающих данных, запомнив каждый обучающий пример. Однако при проверке того же дерева на невидимых данных его качество резко падает. Это классический пример переобучения в обучении с учителем.
set.seed(42)
train_idx <- sample(nrow(MASS::Boston), 400)
train_bos <- MASS::Boston[train_idx, ]
test_bos <- MASS::Boston[-train_idx, ]
# Fully grown tree
full <- rpart(medv ~ ., data = train_bos,
control = rpart.control(cp = 0, minsplit = 2))
train_pred <- predict(full, train_bos)
test_pred <- predict(full, test_bos)
cat('Train RMSE:', sqrt(mean((train_pred - train_bos$medv)^2)))
cat('Test RMSE:', sqrt(mean((test_pred - test_bos$medv)^2)))Важность переменных в rpart
rpart сохраняет значение variable.importance для каждого предиктора: общее улучшение критерия разбиения, обусловленное этой переменной во всех разбиениях. Это позволяет быстро понять, какие признаки определяют решения модели.
tree <- rpart(medv ~ ., data = MASS::Boston, method = 'anova')
# Variable importance (sorted)
imp <- sort(tree$variable.importance, decreasing = TRUE)
print(imp)
# Quick barplot
barplot(imp, las = 2, main = 'Variable Importance',
col = 'steelblue', cex.names = 0.8)От деревьев к ансамблям
Одно дерево решений нестабильно: повторная выборка данных приводит к появлению совершенно разных деревьев. Ансамблевые методы используют эту нестабильность:
- Бэггинг / случайные леса: усредняют результаты множества деревьев, построенных на бутстреп-выборках.
- Бустинг: строит деревья последовательно, причём каждое исправляет ошибки предыдущего дерева.
- Оба метода уменьшают дисперсию, сохраняя выразительные возможности деревьев.
# Demonstrating instability of a single tree
set.seed(1); t1 <- rpart(medv ~ ., data = MASS::Boston[sample(506, 400), ])
set.seed(2); t2 <- rpart(medv ~ ., data = MASS::Boston[sample(506, 400), ])
# Root split may differ between trees
cat('Tree 1 root split:', t1$frame$var[1])
cat('Tree 2 root split:', t2$frame$var[1])Параметры управления rpart
rpart.control() управляет ростом дерева. Основные параметры: cp (штраф за сложность), minsplit (минимальное количество наблюдений для попытки разбиения), minbucket (минимальный размер листа) и maxdepth. Понимание этих параметров необходимо для настройки моделей на основе деревьев.
ctrl <- rpart.control(
cp = 0.005, # complexity penalty
minsplit = 20, # min obs to try a split
minbucket = 7, # min obs in any leaf
maxdepth = 10 # max tree depth
)
tree <- rpart(medv ~ ., data = MASS::Boston,
method = 'anova', control = ctrl)
printcp(tree)Оценка качества дерева
После обрезки оцените дерево на отложенной тестовой выборке. Для регрессии вычислите RMSE и R-квадрат, а для классификации — точность и матрицу ошибок. Сравните эти метрики с показателями эталонных моделей, чтобы понять, какую пользу даёт одно дерево.
pruned_tree <- prune(tree, cp = best_cp)
test_pred <- predict(pruned_tree, newdata = test_bos)
rmse <- sqrt(mean((test_pred - test_bos$medv)^2))
ss_res <- sum((test_pred - test_bos$medv)^2)
ss_tot <- sum((test_bos$medv - mean(test_bos$medv))^2)
r2 <- 1 - ss_res / ss_tot
cat('RMSE:', round(rmse, 3))
cat('R2:', round(r2, 3))Быстрая проверка
Какое утверждение лучше всего описывает цель вызова prune(tree, cp = best_cp)?
Повторение: деревья решений
Основные выводы из темы «Деревья решений — основа ансамблей»:
- Деревья рекурсивно разбивают пространство признаков; для разбиений используются критерии Джини или энтропия.
rpart(y ~ ., data, method)обучает дерево, аprintcp()показывает таблицу сложности.- Найдите CP с минимальной ошибкой при перекрёстной проверке, затем выполните
prune(tree, cp). rpart.plot()визуализирует структуру дерева.- Глубокие деревья переобучаются (низкое смещение, высокая дисперсия), а неглубокие деревья недообучаются.
tree$variable.importanceранжирует предикторы по их общему улучшению разбиений.- Ансамблевые методы (случайные леса, бустинг) устраняют нестабильность отдельных деревьев.
# Standard rpart workflow
tree <- rpart(y ~ ., data = train, method = 'anova',
control = rpart.control(cp = 0.001))
best_cp <- tree$cptable[which.min(tree$cptable[, 'xerror']), 'CP']
pruned <- prune(tree, cp = best_cp)
test_pred <- predict(pruned, newdata = test)
rmse <- sqrt(mean((test_pred - test$y)^2))
cat('Pruned Tree RMSE:', rmse)Часто задаваемые вопросы
Урок «Деревья решений: основа ансамблей» бесплатный?
Да — полный текст урока «Деревья решений: основа ансамблей» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.
Чему я научусь в уроке «Деревья решений: основа ансамблей»?
Стройте и визуализируйте деревья решений с помощью rpart и изучайте компромисс между смещением и дисперсией Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать R Academy?
Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Деревья решений: основа ансамблей»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке R Academy?
Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Деревья решений: основа ансамблей
- Случайный лес с ranger
- Градиентный бустинг с xgboost
- Важность признаков и интерпретация моделей