Получить важность отдельных деревьев в RandomForest - программирование

Получить важность отдельных деревьев в RandomForest

Вопрос: Есть ли способ извлечь значение переменной для каждой отдельной модели CART из объекта randomForest?

rf_mod$forest, похоже, не имеет этой информации, и в документации не упоминается.


В пакете R randomForest средняя значимость переменной для всего леса моделей CART определяется по importance(rf_mod).

library(randomForest)

df <- mtcars

set.seed(1)
rf_mod = randomForest(mpg ~ ., 
                      data = df, 
                      importance = TRUE, 
                      ntree = 200)

importance(rf_mod)

       %IncMSE IncNodePurity
cyl  6.0927875     111.65028
disp 8.7730959     261.06991
hp   7.8329831     212.74916
drat 2.9529334      79.01387
wt   7.9015687     246.32633
qsec 0.7741212      26.30662
vs   1.6908975      31.95701
am   2.5298261      13.33669
gear 1.5512788      17.77610
carb 3.2346351      35.69909

Мы также можем извлечь отдельную древовидную структуру с помощью getTree. Здесь первое дерево.

head(getTree(rf_mod, k = 1, labelVar = TRUE))
  left daughter right daughter split var split point status prediction
1             2              3        wt        2.15     -3   18.91875
2             0              0      <NA>        0.00     -1   31.56667
3             4              5        wt        3.16     -3   17.61034
4             6              7      drat        3.66     -3   21.26667
5             8              9      carb        3.50     -3   15.96500
6             0              0      <NA>        0.00     -1   19.70000

Один из обходных путей - вырастить много CART (то есть - ntree = 1), получить значение переменной каждого дерева и усреднить полученный %IncMSE:

# number of trees to grow
nn <- 200

# function to run nn CART models 
run_rf <- function(rand_seed){
  set.seed(rand_seed)
  one_tr = randomForest(mpg ~ ., 
                        data = df, 
                        importance = TRUE, 
                        ntree = 1)
  return(one_tr)
}

# list to store output of each model
l <- vector("list", length = nn)
l <- lapply(1:nn, run_rf)

Этап извлечения, усреднения и сравнения.

# extract importance of each CART model 
library(dplyr); library(purrr)
map(l, importance) %>% 
  map(as.data.frame) %>% 
  map( ~ { .$var = rownames(.); rownames(.) <- NULL; return(.) } ) %>% 
  bind_rows() %>% 
  group_by(var) %>% 
  summarise('%IncMSE' = mean('%IncMSE')) %>% 
  arrange(-'%IncMSE')

    # A tibble: 10 x 2
   var   '%IncMSE'
   <chr>     <dbl>
 1 wt        8.52 
 2 cyl       7.75 
 3 disp      7.74 
 4 hp        5.53 
 5 drat      1.65 
 6 carb      1.52 
 7 vs        0.938
 8 qsec      0.824
 9 gear      0.495
10 am        0.355

# compare to the RF model above
importance(rf_mod)

       %IncMSE IncNodePurity
cyl  6.0927875     111.65028
disp 8.7730959     261.06991
hp   7.8329831     212.74916
drat 2.9529334      79.01387
wt   7.9015687     246.32633
qsec 0.7741212      26.30662
vs   1.6908975      31.95701
am   2.5298261      13.33669
gear 1.5512788      17.77610
carb 3.2346351      35.69909

Я хотел бы иметь возможность извлекать значение переменной каждого дерева непосредственно из объекта randomForest, без этого обходного метода, который включает в себя полный повторный запуск RF, чтобы упростить воспроизводимые кумулятивные графики важности переменных, подобные этому, и показанному ниже показано для mtcars. Минимальный пример здесь.

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

enter image description here

4b9b3361

Ответ 1

При обучении модели randomForest оценки важности вычисляются для всего леса и сохраняются непосредственно внутри объекта. Специфичные для дерева оценки не сохраняются и поэтому не могут быть напрямую получены из объекта randomForest.

К сожалению, вы правы относительно необходимости постепенного создания леса. Хорошей новостью является то, что объект randomForest является автономным, и вам не нужно реализовывать свой собственный run_rf. Вместо этого вы можете использовать stats::update для повторного randomForest::grow модели случайного леса с одним деревом и randomForest::grow для добавления дополнительных деревьев по одному:

## Starting with a random forest having a single tree,
##   grow it 9 times, one tree at a time
rfs <- purrr::accumulate( .init = update(rf_mod, ntree=1),
                          rep(1,9), randomForest::grow )

## Retrieve the importance scores from each random forest
imp <- purrr::map( rfs, ~importance(.x)[,"%IncMSE"] )

## Combine all results into a single data frame
dplyr::bind_rows( !!!imp )
# # A tibble: 10 x 10
#      cyl  disp    hp  drat    wt   qsec    vs     am    gear  carb
#    <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl>  <dbl>   <dbl> <dbl>
#  1 0      18.8  8.63 1.05   0     1.17  0     0       0      0.194
#  2 0      10.0 46.4  0.561  0    -0.299 0     0       0.543  2.05 
#  3 0      22.4 31.2  0.955  0    -0.199 0     0       0.362  5.1
#  4 1.55   24.1 23.4  0.717  0    -0.150 0     0       0.272  5.28
#  5 1.24   22.8 23.6  0.573  0    -0.178 0     0      -0.0259 4.98
#  6 1.03   26.2 22.3  0.478  1.25  0.775 0     0      -0.0216 4.1
#  7 0.887  22.5 22.5  0.406  1.79 -0.101 0     0      -0.0185 3.56
#  8 0.776  19.7 21.3  0.944  1.70  0.105 0     0.0225 -0.0162 3.11
#  9 0.690  18.4 19.1  0.839  1.51  1.24  1.01  0.02   -0.0144 2.77
# 10 0.621  18.4 21.2  0.937  1.32  1.11  0.910 0.0725 -0.114  2.49

Фрейм данных показывает, как значение функции изменяется с каждым дополнительным деревом. Это правая панель вашего примера сюжета. Сами деревья (для левой панели) можно получить из конечного леса, который задается с помощью dplyr::last( rfs ).

Ответ 2

Отказ от ответственности: это не совсем ответ, но слишком долго, чтобы оставлять комментарии. Удалит, если сочтет неуместным.

Хотя я (думаю, что) понимаю ваш вопрос, если честно, я не уверен, имеет ли ваш вопрос смысл с точки зрения статистики /ML. Следующее основано на моем явно ограниченном понимании RF и CART. Возможно, мой комментарий-пост приведет к некоторому пониманию.

Давайте начнем с некоторой общей теории случайных лесов (RF) о переменной значимости из Хасти, Тибширани, Фридмана, Элементы статистического обучения, с. 593 (жирное лицо):

При каждом разделении в каждом дереве улучшение критерия разделения является показателем важности, приписываемым переменной разделения, и накапливается по всем деревьям в лесу отдельно для каждой переменной. [...] Случайные леса также используют oob выборки, чтобы построить другую меру переменной важности, очевидно, чтобы измерить силу предсказания каждой переменной.

Таким образом, показатель важности переменной в РФ определяется как показатель, накопленный по всем деревьям.


В традиционных единичных классификационных деревьях (CART) важность переменных характеризуется индексом Джини, который измеряет примеси в узлах (см., Например, " Как измерить/оценить" важность переменных "при использовании CART?" (В частности, используя {rpart} из R) и " Carolin Strobl PhD". дипломная работа)

Существуют более сложные меры для характеристики важности переменных в CART-подобных моделях; например в rpart:

Общим показателем переменной важности является сумма добротности показателей разделения для каждого разделения, для которого оно было первичной переменной, плюс качество * (скорректированное соглашение) для всех разделений, в которых оно было суррогатом. В распечатке они масштабируются до суммы 100 и отображаются округленные значения, исключая любую переменную, доля которой составляет менее 1%.


Итак, суть здесь в следующем: по крайней мере будет непросто (а в худшем случае это не имеет смысла) сравнивать переменные меры из отдельных деревьев классификации с мерами переменной важности, применяемыми к методам на основе ансамбля. как РФ.

Что заставляет меня спросить: почему вы хотите извлечь переменные значения важности для отдельных деревьев из модели RF? Даже если вы придумали метод вычисления значений переменных из отдельных деревьев, я считаю, что они не будут иметь особого значения, и им не придется "сходиться" к накопленным в ансамбле значениям.

Ответ 3

Мы можем упростить это путем

library(tidyverse)
out <- map(seq_len(nn),  ~ 
          run_rf(.x) %>% 
          importance) %>%
       reduce('+') %>% 
       magrittr::divide_by(nn)