Получение знаний из случайного леса


127

Случайные леса считаются черными ящиками, но недавно я подумал, какие знания можно получить из случайного леса?

Наиболее очевидной вещью является важность переменных, в простейшем варианте это можно сделать, просто рассчитав количество вхождений переменной.
Второе, о чем я думал, это взаимодействие. Я думаю, что если количество деревьев достаточно велико, можно проверить количество вхождений пар переменных (что-то вроде независимости хи-квадрат). Третье - это нелинейности переменных. Моей первой идеей было просто посмотреть на график переменной Vs, но я пока не уверен, имеет ли это смысл.

Добавлено 23.01.2012
Мотивация

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


1
Используя R, вы можете создать точечную диаграмму переменной важности, измеренную случайным лесом.
Джордж Донтас

1
связанная ветка о том, как рассчитываются показатели переменной важности для повышения стохастического градиентного дерева
Антуан

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

Ответы:


122

Случайные леса вряд ли являются черным ящиком. Они основаны на деревьях решений, которые очень легко интерпретировать:

#Setup a binary classification problem
require(randomForest)
data(iris)
set.seed(1)
dat <- iris
dat$Species <- factor(ifelse(dat$Species=='virginica','virginica','other'))
trainrows <- runif(nrow(dat)) > 0.3
train <- dat[trainrows,]
test <- dat[!trainrows,]

#Build a decision tree
require(rpart)
model.rpart <- rpart(Species~., train)

Это приводит к простому дереву решений:

> model.rpart
n= 111 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 111 35 other (0.68468468 0.31531532)  
  2) Petal.Length< 4.95 77  3 other (0.96103896 0.03896104) *
  3) Petal.Length>=4.95 34  2 virginica (0.05882353 0.94117647) *

Если Petal.Length <4.95, это дерево классифицирует наблюдение как «другое». Если оно больше 4,95, оно классифицирует наблюдение как «virginica». Случайный лес - это просто набор из множества таких деревьев, каждое из которых обучается на случайном подмножестве данных. Затем каждое дерево «голосует» за окончательную классификацию каждого наблюдения.

model.rf <- randomForest(Species~., train, ntree=25, proximity=TRUE, importance=TRUE, nodesize=5)
> getTree(model.rf, k=1, labelVar=TRUE)
  left daughter right daughter    split var split point status prediction
1             2              3  Petal.Width        1.70      1       <NA>
2             4              5 Petal.Length        4.95      1       <NA>
3             6              7 Petal.Length        4.95      1       <NA>
4             0              0         <NA>        0.00     -1      other
5             0              0         <NA>        0.00     -1  virginica
6             0              0         <NA>        0.00     -1      other
7             0              0         <NA>        0.00     -1  virginica

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

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

library(ggplot2)
pSpecies <- predict(model.rf,test,'vote')[,2]
plotData <- lapply(names(test[,1:4]), function(x){
  out <- data.frame(
    var = x,
    type = c(rep('Actual',nrow(test)),rep('Predicted',nrow(test))),
    value = c(test[,x],test[,x]),
    species = c(as.numeric(test$Species)-1,pSpecies)
    )
  out$value <- out$value-min(out$value) #Normalize to [0,1]
  out$value <- out$value/max(out$value)
  out
})
plotData <- do.call(rbind,plotData)
qplot(value, species, data=plotData, facets = type ~ var, geom='smooth', span = 0.5)

участок

Я нормализовал переменные (длина и ширина чашелистика и лепестка) в диапазоне 0-1. Ответ также 0-1, где 0 - другое, а 1 - virginica. Как видите, случайный лес - хорошая модель, даже на тестовом наборе.

Кроме того, случайный лес вычислит различную меру переменной важности, которая может быть очень информативной:

> importance(model.rf, type=1)
             MeanDecreaseAccuracy
Sepal.Length           0.28567162
Sepal.Width           -0.08584199
Petal.Length           0.64705819
Petal.Width            0.58176828

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

plot(model.rf)
plot(margin(model.rf)) 
MDSplot(model.rf, iris$Species, k=5)
plot(outlier(model.rf), type="h", col=c("red", "green", "blue")[as.numeric(dat$Species)])

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


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

@TomekTarczynski - это интересная проблема, похожая на ту, с которой я сейчас сталкиваюсь. Я предполагаю, что под "моделью логита" вы подразумеваете логистическую регрессию или что-то подобное? Я использую логистическую регрессию Лассо (из пакета glmnet R) для выбора предикторов из модели с взаимодействиями между всеми парами переменных. Я еще не добавил ни одного нелинейного термина - но в принципе это тоже должно быть возможно. Единственная проблема, которую я предполагаю, - это решить, какие нелинейные термины использовать (полиномиальные термины, экспоненциальные преобразования и т. Д.?). Кроме того, я не собираю никаких взаимодействий высшего порядка, но это тоже легко.
Анна З.

2
@ Томек, что ты не получаешь от этого ответа? Если вы используете пакет randomForest в R, то описания, описанные Заком, должны быть очень полезны. В частности, вы могли бы использовать varImpPlot для выбора объектов в своей модели logit и partPlot, чтобы оценить тип преобразования, чтобы примерить непрерывные предикторы в модели logit. Я бы предложил использовать последний график для определения того, где существуют нелинейные отношения между предиктором и ответом, а затем позволяет явно выполнить это преобразование или использовать сплайн для этой переменной.
B_Miner

2
@b_miner - просто предположение, но похоже, что Томек спрашивает, как найти нелинейные взаимодействия между переменными, потому что логистическая регрессия уже захватывает линейные отношения.
rm999

@ rm999 Как вы определяете нелинейное взаимодействие в модели логита? Условия взаимодействия, созданные между преобразованными переменными?
B_Miner

52

Некоторое время назад мне пришлось обосновать соответствие модели RF некоторым химикам в моей компании. Я потратил много времени, пробуя разные методы визуализации. В ходе этого процесса я также случайно придумал несколько новых методов, которые я поместил в пакет R ( forestFloor ) специально для визуализации случайных лесов.

Классическим подходом являются графики частичной зависимости, поддерживаемые: Rminer (анализ чувствительности на основе данных - это повторная частичная зависимость), или partPlot в пакете randomForest . Я считаю пакет с частичной зависимостью iceBOX элегантным способом обнаружения взаимодействий. Я не использовал пакет edarf , но, похоже, у него есть несколько прекрасных визуализаций, посвященных RF. Пакет ggRandomForest также содержит большой набор полезных визуализаций.

В настоящее время forestFloor поддерживает объекты randomForest (поддержка других реализаций RF уже доступна). Также вклады характеристик могут быть рассчитаны для деревьев с градиентным усилением, поскольку эти деревья после обучения мало чем отличаются от случайных лесных деревьев. Таким образом, forestFloor может поддерживать XGBoost в будущем. Графики частичной зависимости полностью модельно инвариантны.

Все пакеты имеют общее для визуализации геометрической структуры сопоставления модели из пространства признаков в пространство назначения. Кривая синуса y = sin (x) будет отображением от x до y и может быть построена в 2D. Для непосредственного построения RF-отображения часто требуется слишком много измерений. Вместо этого общая структура отображения может быть спроецирована, разрезана или разложена таким образом, что вся структура отображения сводится к последовательности 2D предельных графиков. Если ваша RF-модель имеет только основные эффекты и не взаимодействует между переменными, классические методы визуализации подойдут просто отлично. Тогда вы можете упростить структуру вашей модели следующим образом:y=F(X)f1(x1)+f2(x2)+...+fd(xd), Тогда каждая частичная функция по каждой переменной может быть визуализирована как синусоида. Если ваша модель RF зафиксировала значительные взаимодействия, то это более проблематично. Трехмерные фрагменты структуры могут визуализировать взаимодействие между двумя элементами и выходными данными. Проблема состоит в том, чтобы знать, какую комбинацию функций визуализировать ( iceBOX действительно решает эту проблему). Также нелегко определить, не учитываются ли другие скрытые взаимодействия.

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

Я вставил смоделированный пример из пакета forestFloor, где я показываю, как раскрыть смоделированную скрытую функцию noisey=x12+sin(x2π)+2x3x4+

#1 - Regression example:
set.seed(1234)
library(forestFloor)
library(randomForest)

#simulate data y = x1^2+sin(x2*pi)+x3*x4 + noise
obs = 5000 #how many observations/samples
vars = 6   #how many variables/features
#create 6 normal distr. uncorr. variables
X = data.frame(replicate(vars,rnorm(obs)))
#create target by hidden function
Y = with(X, X1^2 + sin(X2*pi) + 2 * X3 * X4 + 0.5 * rnorm(obs)) 

#grow a forest
rfo = randomForest(
  X, #features, data.frame or matrix. Recommended to name columns.
  Y, #targets, vector of integers or floats
  keep.inbag = TRUE,  # mandatory,
  importance = TRUE,  # recommended, else ordering by giniImpurity (unstable)
  sampsize = 1500 ,   # optional, reduce tree sizes to compute faster
  ntree = if(interactive()) 500 else 50 #speedup CRAN testing
)

#compute forestFloor object, often only 5-10% time of growing forest
ff = forestFloor(
  rf.fit = rfo,       # mandatory
  X = X,              # mandatory
  calc_np = FALSE,    # TRUE or FALSE both works, makes no difference
  binary_reg = FALSE  # takes no effect here when rfo$type="regression"
)


#plot partial functions of most important variables first
plot(ff,                       # forestFloor object
     plot_seq = 1:6,           # optional sequence of features to plot
     orderByImportance=TRUE    # if TRUE index sequence by importance, else by X column  
)

введите описание изображения здесь

#Non interacting features are well displayed, whereas X3 and X4 are not
#by applying color gradient, interactions reveal themself 
#also a k-nearest neighbor fit is applied to evaluate goodness-of-fit
Col=fcol(ff,3,orderByImportance=FALSE) #create color gradient see help(fcol)
plot(ff,col=Col,plot_GOF=TRUE) 

введите описание изображения здесь

#feature contributions of X3 and X4 are well explained in the context of X3 and X4
# as GOF R^2>.8


show3d(ff,3:4,col=Col,plot_GOF=TRUE,orderByImportance=FALSE)

введите описание изображения здесь введите описание изображения здесь

Наконец, код для графиков частичной зависимости, закодированный A.Liaw, описанный J.Friedman. Что хорошо для основных эффектов.

par(mfrow=c(2,3))
for(i in 1:6) partialPlot(rfo,X,x.var=names(X)[i])

введите описание изображения здесь


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

Oups! Я нашел ошибку в своем тестовом алгоритме, из-за которой все проблемы исчезли. ForestFloor может быть обновлен, чтобы работать нормально для деревьев с градиентным усилением.
Сорен Хавелунд Веллинг

3
ForestFloor обновлен, чтобы принимать объекты gbm?
Миша

До сих пор я делал обратную реализацию, которая превращает реализацию randomForest в полностью функциональную машину повышения градиента и определяет некоторые методы для вычисления вклада функций. Я думаю, что реализация на самом деле является достаточно эффективной. Вы можете найти код здесь: github.com/sorhawell/forestFloor/blob/master/inst/examples/…
Сорен Хавелунд Веллинг,

Сделать полный порт - тяжелая работа :) Эта реализация была сделана в несколько строк.
Сорен Хавелунд Веллинг

24

Чтобы дополнить эти прекрасные ответы, я бы упомянул использование деревьев с градиентным усилением (например, пакет GBM в R ). В R я предпочитаю это случайным лесам, потому что пропущенные значения допустимы по сравнению с randomForest, где требуется вменение. Имеются переменные значения и частичные графики (как в randomForest), помогающие в выборе объектов и исследовании нелинейных преобразований в вашей модели логита. Кроме того, взаимодействие с переменными решается с помощью H-статистики Фридмана ( interact.gbm) со ссылкой, заданной как J.H. Friedman and B.E. Popescu (2005). “Predictive Learning via Rule Ensembles.” Section 8.1. Коммерческая версия под названием TreeNet доступна от Salford Systems, и эта видеопрезентация рассказывает о том, как они оценивают переменное взаимодействие. Видео .


2
Я согласен, GBM - логичный следующий шаг от случайных лесов.
Зак

@B_miner: Отлично! Я не знаю как, но я упустил из виду GBM. Кажется, что с помощью GBM легко обнаружить взаимодействия и нелинейности.
Томек Тарчински

15

Поздний ответ, но я наткнулся на недавний пакет R forestFloor(2015), который помогает вам автоматически выполнять эту задачу «unblackboxing». Это выглядит очень многообещающе!

library(forestFloor)
library(randomForest)
#simulate data
obs=1000
vars = 18
X = data.frame(replicate(vars,rnorm(obs)))
Y = with(X, X1^2 + sin(X2*pi) + 2 * X3 * X4 + 1 * rnorm(obs))
#grow a forest, remeber to include inbag
rfo=randomForest(X,Y,keep.inbag = TRUE,sampsize=250,ntree=50)
#compute topology
ff = forestFloor(rfo,X)
#ggPlotForestFloor(ff,1:9)
plot(ff,1:9,col=fcol(ff))

Производит следующие участки:

введите описание изображения здесь

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


4
Я удалил поддержку ggplot2, вместо последней строки попробуйте, например: plot (ff, 1: 9, col = fcol (ff, 1: 4))
Сорен Хавелунд Веллинг

9

Как упомянул Зак, один из способов понять модель - это построить ответ, когда предсказатели меняются. Вы можете сделать это легко для "любой" модели с пакетом plotmo R. Например

library(randomForest)
data <- iris
data$Species <- factor(ifelse(data$Species=='virginica','virginica','other'))
mod <- randomForest(Species~Sepal.Length+Sepal.Width, data=data)
library(plotmo)
plotmo(mod, type="prob")

который дает

участок

Это изменяет одну переменную, в то время как другие сохраняют их медианные значения. Для графиков взаимодействия это меняет две переменные. (Примечание добавлено в ноябре 2016 года: plotmoтеперь также поддерживаются графики частичной зависимости.)

В приведенном выше примере используются только две переменные; более сложные модели можно визуализировать по частям, просматривая одну или две переменные одновременно. Поскольку «другие» переменные хранятся в своих медианных значениях, это показывает только часть данных, но все еще может быть полезным. Некоторые примеры приведены в виньетке для пакета plotmo . Другие примеры приведены в главе 10 « Построение деревьев rpart с помощью пакета rpart.plot» .


4

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

Что касается взаимодействий, кажется, что Брейман и Культер уже пытались взглянуть на это, особенно для классификации RF.

Насколько мне известно, это не было реализовано в пакете randomForest R. Может быть, потому что это может быть не так просто и потому что значение «переменных взаимодействий» очень зависит от вашей проблемы.

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


3

Позднее в игре, но есть некоторые новые разработки в этой области, например LIME и SHAP . Также стоит проверить пакет DALEX (в частности, если используется R, но в любом случае он содержит отличные читы и т. Д.), Хотя в данный момент он не охватывает взаимодействия. И все это не зависит от модели, поэтому будет работать для случайных лесов, GBM, нейронных сетей и т. Д.


(+1) Хорошие ресурсы!
mkt

2

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

В более ранней статье ( здесь ) подробно описывается простой причинный лес. На странице 9 статьи приведена пошаговая процедура выращивания причинного дерева, которое затем можно распространить до леса обычными способами.Взято со страницы 9 Athey and Wager 2017

Уравнение 4:

Уравнение 4

Уравнение 5: Уравнение 5


1
Обновлен ссылками на более раннюю статью и скриншотами из этой статьи, чтобы показать процедуру причинного дерева.
Ганнаваг

1

Поздний ответ связан с моим вопросом здесь ( Можем ли мы сделать Случайный Лес на 100% интерпретируемым, исправив начальное число? ):

z1z2

  1. z1mD1(z1)D2(z1)D3(z1)Dm(z1)
  2. mT1(z1,z2)T2(z1,z2)T3(z1,z2)Tm(z1,z2)
  3. jth(j=1,2,...,m)xif^j(xi)(in,jm)
    F^(xi)=>1mj=1mf^j(xi)
  4. F^(xi)(z1,z2)xi
  5. xi
    x1F^(x1) - which is fixed> thanks to (z1,z2)
    x2F^(x2) -> which is fixed thanks to (z1,z2)
    x3→>F^(x3) - which is fixed thanks to (z1,z2)
    x4>→F^(x4) - which is fixed thanks to (z1,>z2)
    ....
  6. x1x2

Это работает также для всех методов ансамбля, основанных на агрегации деревьев.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.