Отримання знань з випадкового лісу


127

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

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

Додано 23.01.2012
Мотивація

Я хочу використовувати ці знання для вдосконалення моделі logit. Я думаю (або, принаймні, сподіваюсь), що можна знайти взаємодії та нелінійності, які були не помічені.


1
Використовуючи R, ви можете створити точкову діаграму змінної важливості, виміряну випадковим лісом.
Джордж Донтас

1
пов'язана нитка про те, як обчислюються мінливі значення важливості для збільшення стохастичного градієнтного дерева
Антуан

Я усвідомлюю, що це, мабуть, пізно, але якщо ви просто хочете вдосконалити модель logit, чому б вам не скористатися логістичною регресією після лассо? Ви можете просто доопрацювати модель, використовуючи вибрані коефіцієнти після вибору без штрафу / усадки. Вам доведеться трохи налаштувати процедуру настройки, але це набагато більш прямий варіант, який робить саме те, що ви хочете.
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, це класифікує спостереження як "віргініку". Випадковий ліс - це проста сукупність багатьох таких дерев, де кожне тренується за випадковим набором даних. Тоді кожне дерево "голосує" за остаточну класифікацію кожного спостереження.

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

Можна навіть витягнути окремі дерева з rf та подивитися на їх будову. Формат дещо інший, ніж для 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 - віргініка. Як ви бачите, випадковий ліс є хорошою моделлю навіть на тестовому наборі.

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

> 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
Дякую за відповідь, є багато корисної інформації, але це не зовсім те, що я шукав. Можливо, мені потрібно краще уточнити мотивацію, що стоїть за цим питанням. Я хочу використовувати випадковий ліс для вдосконалення моделі logit, покращуючи, я маю на увазі додати деяку взаємодію або використовувати нелінійне перетворення.
Томек Тарчинський

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

2
@Tomek, чого ти не отримуєш від цієї відповіді? Якщо ви використовуєте пакет randomForest в R, графіки, описані Zach, повинні бути дуже корисними. Зокрема, ви можете використовувати varImpPlot для вибору функцій у вашій моделі logit та partPlot для оцінки типу трансформації, щоб приміряти безперервні прогнози в моделі logit. Я б запропонував використати останній сюжет для визначення місця існування нелінійних зв’язків між прогноктором та відповіддю, а потім дозволяє зробити це перетворення явно або використовувати сплайн на цій змінній.
B_Miner

2
@b_miner - лише здогадка, але, схоже, томек запитує, як знайти нелінійні взаємодії між змінними, оскільки логістична регресія вже фіксує лінійні зв’язки.
rm999

@ rm999 Як визначити нелінійну взаємодію в моделі logit? Терміни взаємодії, створені між трансформованими змінними?
B_Miner

52

Деякий час тому мені довелося виправдовувати RF-модель, підходящу до деяких хіміків моєї компанії. Я провів досить багато часу, пробуючи різні методи візуалізації. Під час процесу я випадково також придумав нові методи, які я вклав у пакет R ( forestFloor ) спеціально для випадкових візуалізацій лісу.

Класичний підхід - це графіки часткової залежності, які підтримуються: Rminer (аналіз чутливості на основі даних - це відновлювана часткова залежність) або частковий Plot у пакеті randomForest . Я вважаю пакет часткової залежності iceBOX елегантним способом виявлення взаємодій. Не використовували пакет Edarf , але, схоже, є деякі чудові візуалізації, присвячені РФ. Пакет ggRandomForest також містить великий набір корисних візуалізацій.

В даний час ForestFloor підтримує об'єкти randomForest (підтримується інша реалізація РФ). Також можна внести підрахунок функціональних внесків для дерев, підсилених градієнтом, оскільки ці дерева після тренувань не сильно відрізняються від випадкових лісових дерев. Тож ForestFloor може підтримувати XGBoost у майбутньому. Діаграми часткової залежності є повністю інваріантними за моделлю.

Всі пакети мають спільне для візуалізації геометричної структури відображення моделі від простору функцій до цільового простору. Синусова крива y = sin (x) буде відображенням від x до y і може бути побудована в 2D. Для побудови прямої RF-карти часто потрібно занадто багато розмірів. Натомість загальну структуру картографування можна проектувати, нарізати чи розкладати, так що вся структура картографування зводиться до послідовності 2D граничних ділянок. Якщо ваша RF модель має лише основні ефекти та не має взаємодії між змінними, класичні методи візуалізації стануть чудово. Тоді ви можете спростити структуру моделі так, як цеy=F(X)f1(x1)+f2(x2)+...+fd(xd). Тоді кожна часткова функція кожної змінної може бути візуалізована так само, як і синусова крива. Якщо ваша РФ модель охопила значні взаємодії, то це більш проблематично. 3D-фрагменти структури можуть візуалізувати взаємодію між двома ознаками та результатом. Проблема полягає в тому, щоб знати, яку комбінацію функцій візуалізувати ( IceBOX вирішує цю проблему). Крім того, непросто визначити, чи інші приховані взаємодії все ще не враховуються.

У цій роботі я використав дуже ранню версію ForestFloor, щоб пояснити, яким фактичним біохімічним зв’язком була захоплена дуже мала модель РФ. І в цій роботі ми докладно описуємо візуалізацію виставкових внесків, Візуалізацію лісових підлоги випадкових лісів .

Я вставив модельований приклад із пакету forestFloor, де я показую, як розкрити змодельовану приховану функцію шумy=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)

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

Нарешті, код для графіків часткової залежності, кодований А. Ліавом, описаний Дж. Фрідманом. Які добре впливають на основні ефекти.

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

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


Раніше я постулював, що внесок у функції також може бути обчислений для підсилених дерев. Я написав алгоритм тестування і це можливо. На жаль, внесені вкладки для підсилених дерев не виявляють таких самих корисних властивостей, як для мішків. Ефекти однієї функції мають тенденцію розповсюджуватися на всі вкладення, тому візуалізація стає досить заплутаною.
Soren Havelund Welling

Ой! Я знайшов помилку в своєму тестовому алгоритмі, який змусив усі проблеми розвіятися. ForestFloor може бути оновлений, щоб він працював чудово для дерев, підсилених градієнтом.
Сорен Хавелунд Веллінг

3
оновлено ForestFloor для прийому gbm-об’єктів?
Міша

Поки що я зробив зворотну реалізацію, яка перетворює реалізацію randomForest у повністю функціональну машину підвищення градієнта і визначає деякі методи для обчислення вкладів функцій. Я думаю, що реалізація насправді є досить ефективною. Ви можете знайти код тут: github.com/sorhawell/forestFloor/blob/master/inst/examples/…
Soren Havelund Welling

Зробити повноцінний порт - це важка робота :) Ця реалізація була виконана у досить невеликих рядках.
Soren Havelund Welling

24

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


2
Я погоджуюсь, що ГБ є логічним наступним кроком від випадкових лісів.
Зак

@B_miner: Чудово! Не знаю як, але я не помітив ГБМ. Здається, що за допомогою ГБМ легко виявити взаємодії та нелінійності.
Томек Тарчинський

15

Пізня відповідь, але я натрапив на нещодавній пакет R forestFloor(2015), який допомагає вам автоматично виконувати цю задачу "unblockboxing". Це виглядає дуже перспективно!

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))
Soren Havelund Welling

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

Мене дуже цікавлять подібні питання. Я думаю, що є багато інформації, яку ми можемо отримати з випадкового лісу.

Щодо взаємодій, то, схоже, Брейман і Кульєр вже намагалися розглянути це, особливо для класифікаційних радіочастот.

Наскільки мені відомо, це не було реалізовано в пакеті randomForest R. Може тому, що це може бути не так просто і тому, що значення "змінних взаємодій" дуже залежить від вашої проблеми.

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


3

Пізно в грі, але на цьому фронті є нові зміни, наприклад LIME та SHAP . Також пакунок, який варто перевірити, - це DALEX (зокрема, якщо використовується R, але в будь-якому випадку містить приємні шаблони тощо), хоча, схоже, не охоплює взаємодії на даний момент. І все це модельно-агностичні, тому вони працюватимуть для випадкових лісів, ГБМ, нейронних мереж тощо.


(+1) Приємні ресурси!
mkt

2

Невелика модифікація випадкових лісів, які надають більше інформації про дані, - це нещодавно розроблені причинно-наслідкові методи лісу. Дивіться пакет пакету GRF та мотиваційний папір тут . Ідея полягає у використанні випадкових лісових базових методів для пошуку неоднорідності причинних ефектів.

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

Рівняння 4:

Рівняння 4

Рівняння 5: Рівняння 5


1
Оновлено посиланнями на попередній папір та скріншоти з цього паперу, щоб показати процедуру дерева причинних наслідків.
gannawag

1

Пізня відповідь, пов’язана з моїм запитанням тут ( Чи можемо ми зробити Random Forest 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)
    х ' 4 > Р (х ' 4 ) - яка фіксується завдяки  ( г 1 , > г 2 ) . . . .
    x3→>F^(x3) - which is fixed thanks to (z1,z2)
    x4>→F^(x4) - which is fixed thanks to (z1,>z2)
    ....
  6. Останні можна легко представити у вигляді одного (величезного) дерева . Наприклад: : (Вік = 18, стать = М, ...), = (Вік = 18, стать = F, ...), ... можна перегрупувати для створення аркуша. x 2x1x2

Це працює також для всіх методів ансамблю, заснованих на агрегації дерев.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.