Obtendo conhecimento de uma floresta aleatória

127

Florestas aleatórias são consideradas caixas pretas, mas recentemente eu estava pensando que conhecimento pode ser obtido de uma floresta aleatória?

O mais óbvio é a importância das variáveis, na variante mais simples isso pode ser feito apenas calculando o número de ocorrências de uma variável.
A segunda coisa que eu estava pensando são interações. Penso que, se o número de árvores for suficientemente grande, o número de ocorrências de pares de variáveis ​​poderá ser testado (algo como independência do qui quadrado). A terceira coisa são as não linearidades das variáveis. Minha primeira idéia foi apenas olhar para um gráfico de uma pontuação Vs variável, mas ainda não tenho certeza se faz algum sentido.

Adicionado 23.01.2012
Motivação

Eu quero usar esse conhecimento para melhorar um modelo de logit. Penso (ou pelo menos espero) que é possível encontrar interações e não linearidades que foram negligenciadas.

Tomek Tarczynski
fonte
11
Usando R, você pode produzir um Dotchart de importância variável, medido por uma Floresta Aleatória.
George Dontas
11
um relacionado tópico sobre as medidas de importância como variáveis são calculados para árvore de gradiente estocástico impulsionar
Antoine
Sei que provavelmente é tarde demais, mas se você apenas deseja melhorar um modelo de logit, por que não usa a regressão logística pós-laço? Você pode apenas recolocar o modelo usando os coeficientes selecionados após a seleção sem penalização / retração. Você precisará ajustar um pouco o procedimento de ajuste, mas essa é uma opção muito mais direta que faz exatamente o que você deseja.
ilprincipe 10/10

Respostas:

122

Florestas aleatórias dificilmente são uma caixa preta. Eles são baseados em árvores de decisão, que são muito fáceis de interpretar:

#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)

Isso resulta em uma árvore de decisão simples:

> 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) *

Se Petal.Length <4.95, essa árvore classifica a observação como "outra". Se for maior que 4,95, classifica a observação como "virginica". Uma floresta aleatória é simples, uma coleção de muitas dessas árvores, onde cada uma é treinada em um subconjunto aleatório dos dados. Cada árvore "vota" na classificação final de cada observação.

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

Você pode até extrair árvores individuais da RF e observar sua estrutura. O formato é um pouco diferente do dos rpartmodelos, mas você pode inspecionar cada árvore, se quiser, e ver como está modelando os dados.

Além disso, nenhum modelo é realmente uma caixa preta, porque você pode examinar as respostas previstas versus as respostas reais para cada variável no conjunto de dados. Essa é uma boa ideia, independentemente do tipo de modelo que você está construindo:

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)

enredo

Normalizei as variáveis ​​(comprimento e largura da sépala e pétala) para um intervalo de 0 a 1. A resposta também é 0-1, onde 0 é outro e 1 é virginica. Como você pode ver, a floresta aleatória é um bom modelo, mesmo no conjunto de testes.

Além disso, uma floresta aleatória calculará várias medidas de importância variável, que podem ser muito informativas:

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

Esta tabela representa quanto a remoção de cada variável reduz a precisão do modelo. Por fim, existem muitos outros gráficos que você pode fazer a partir de um modelo de floresta aleatório, para visualizar o que está acontecendo na caixa preta:

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)])

Você pode visualizar os arquivos de ajuda de cada uma dessas funções para ter uma idéia melhor do que elas exibem.

Zach
fonte
6
Obrigado pela resposta, há muitas informações úteis, mas não é exatamente o que eu estava procurando. Talvez eu precise esclarecer melhor a motivação que está por trás dessa pergunta. Eu quero usar uma floresta aleatória para melhorar um modelo de logit, por melhorar, quero adicionar alguma interação ou usar uma transformação não linear.
Tomek Tarczynski
@TomekTarczynski, esse é um problema interessante e semelhante a um com o qual estou lidando agora. Presumo que por "modelo de logit" você quer dizer regressão logística ou algo semelhante? Estou usando a regressão logística do laço (do pacote glmnet R) para selecionar preditores de um modelo com interações entre todos os pares de variáveis. Ainda não adicionei termos não lineares - mas em princípio isso também deveria ser possível. Acho que a única questão é decidir quais termos não lineares tentar (termos polinomiais, transformações exponenciais, etc?). Além disso, não estou percebendo interações de ordem superior, mas isso também é fácil.
Anne Z.
2
@ Tomk, o que você não está recebendo com esta resposta? Se você estiver usando o pacote randomForest no R, os gráficos que Zach descreve devem ser muito úteis. Especificamente, você pode usar varImpPlot para seleção de recurso em seu modelo de logit e parcialPlot para estimar o tipo de transformação para experimentar preditores contínuos no modelo de logit. Sugiro que o último gráfico seja usado para determinar onde existem relações não lineares entre preditor e resposta e, em seguida, permite que você faça essa transformação explicitamente ou use um spline nessa variável.
B_Miner
2
@ b_miner - apenas um palpite, mas parece que tomek está perguntando como encontrar interações não lineares entre variáveis, porque a regressão logística já captura as relações lineares.
rm999
@ rm999 Como você define uma interação não linear em um modelo de logit? Termos de interação criados entre variáveis ​​transformadas?
B_Miner
52

Há algum tempo, tive que justificar um modelo de RF adequado a alguns químicos da minha empresa. Passei bastante tempo tentando diferentes técnicas de visualização. Durante o processo, acidentalmente também inventei algumas novas técnicas que coloquei em um pacote R ( forestFloor ) especificamente para visualizações aleatórias da floresta.

A abordagem clássica são gráficos de dependência parcial suportados por: Rminer (a análise de sensibilidade baseada em dados reinventa a dependência parcial), ou o parcialPlot no pacote randomForest . Acho o pacote de dependência parcial iceBOX como uma maneira elegante de descobrir interações. Não usei o pacote edarf , mas parece ter boas visualizações dedicadas à RF. O pacote ggRandomForest também contém um grande conjunto de visualizações úteis.

Atualmente, o forestFloor suporta objetos randomForest (o suporte para outras implementações de RF está a caminho). Também podem ser computadas contribuições de recursos para árvores com aumento de gradiente, pois essas árvores após o treinamento não são muito diferentes das árvores florestais aleatórias. Portanto, o forestFloor pode suportar o XGBoost no futuro. Gráficos de dependência parcial são completamente invariantes ao modelo.

Todos os pacotes têm em comum a visualização da estrutura de mapeamento geométrico de um modelo, do espaço de recurso ao espaço de destino. Uma curva senoidal y = sin (x) seria um mapeamento de x para y e pode ser plotada em 2D. Plotar diretamente um mapeamento de RF normalmente exigiria muitas dimensões. Em vez disso, a estrutura geral do mapeamento pode ser projetada, fatiada ou decomposta, de modo que toda a estrutura do mapeamento seja resumida em uma sequência de gráficos marginais 2D. Se o seu modelo de RF capturou apenas os principais efeitos e nenhuma interação entre as variáveis, os métodos clássicos de visualização funcionarão perfeitamente. Em seguida, você pode simplificar a estrutura do seu modelo assimy=F(X)f1(x1)+f2(x2)+...+fd(xd). Então, cada função parcial de cada variável pode ser visualizada exatamente como a curva senoidal. Se o seu modelo de RF capturou interações consideráveis, é mais problemático. Fatias 3D da estrutura podem visualizar interações entre dois recursos e a saída. O problema é saber qual combinação de recursos visualizar (o iceBOX soluciona esse problema). Também não é fácil saber se outras interações latentes ainda não são contabilizadas.

No presente trabalho , eu usei uma versão muito inicial de forestFloor para explicar o real relação bioquímica um modelo RF muito pequena tinha capturado. E neste artigo, descrevemos minuciosamente as visualizações de contribuições de recursos, visualizações de florestas aleatórias no chão da floresta .

Eu colei o exemplo simulado do pacote forestFloor, onde mostro como descobrir uma função oculta simulada 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  
)

insira a descrição da imagem aqui

#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) 

insira a descrição da imagem aqui

#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)

insira a descrição da imagem aqui insira a descrição da imagem aqui

Por fim, o código para parcelas de dependência parcial codificado por A.Liaw descrito por J.Friedman. O que faz bem para os efeitos principais.

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

insira a descrição da imagem aqui

Soren Havelund Welling
fonte
Postulei anteriormente que as contribuições de recursos também poderiam ser calculadas para árvores potencializadas. Eu escrevi um algoritmo de teste e é possível. Infelizmente, as contribuições de recursos para árvores potencializadas não mostram as mesmas propriedades benéficas que as árvores ensacadas. Os efeitos de um recurso tendem a se espalhar por todas as contribuições de recursos, portanto, a visualização se torna bastante confusa.
Soren Havelund Welling
Oups! Encontrei um bug no meu algoritmo de teste que fez todos os problemas se dissiparem. O forestFloor pode ser atualizado para funcionar bem em árvores com gradiente.
Soren Havelund Welling
3
o forestFloor é atualizado para aceitar objetos gbm?
M29:
Até agora, fiz uma implementação reversa, que agrupa a implementação randomForest em uma máquina de aumento de gradiente totalmente funcional e define alguns métodos para calcular as contribuições de recursos. Eu acho que a implementação realmente é razoavelmente eficiente. Você pode encontrar o código aqui: github.com/sorhawell/forestFloor/blob/master/inst/examples/…
Soren Havelund Welling
Fazer uma porta cheia é um trabalho árduo :) Essa implementação foi feita em poucas linhas.
Soren Havelund Welling
24

Para complementar essas respostas finas, eu mencionaria o uso de árvores com gradiente (por exemplo, o Pacote GBM em R ). Em R, eu prefiro isso a florestas aleatórias porque valores ausentes são permitidos em comparação com randomForest onde a imputação é necessária. Importância variável e parcelas parciais estão disponíveis (como em randomForest) para ajudar na seleção de recursos e na exploração de transformação não linear em seu modelo de logit. Além disso, a interação variável é abordada com a estatística H de Friedman ( interact.gbm) com referência dada como J.H. Friedman and B.E. Popescu (2005). “Predictive Learning via Rule Ensembles.” Section 8.1. Uma versão comercial chamada TreeNet está disponível na Salford Systems e esta apresentação em vídeo mostra a opinião deles sobre o vídeo de estimativa de interação variável .

B_Miner
fonte
2
Concordo que os GBMs são um próximo passo lógico para florestas aleatórias.
Zach
@B_miner: Ótimo! Não sei como, mas negligenciei o GBM. Parece que usando o GBM é fácil detectar interações e não linearidades.
Tomek Tarczynski
15

Resposta tardia, mas me deparei com um recente pacote R forestFloor(2015) que ajuda você a executar essa tarefa de "desbloquear caixa" de maneira automatizada. Parece muito promissor!

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))

Produz os seguintes gráficos:

insira a descrição da imagem aqui

Ele também fornece visualização tridimensional se você estiver procurando por interações.

RUser4512
fonte
4
Eu removi o suporte ao ggplot2, em vez da última linha, tente, por exemplo: plot (ff, 1: 9, col = fcol (ff, 1: 4)) #
Soren Havelund Welling
9

Como mencionado por Zach, uma maneira de entender um modelo é plotar a resposta à medida que os preditores variam. Você pode fazer isso facilmente para "qualquer" modelo com o pacote plotmo R. Por exemplo

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")

que dá

enredo

Isso altera uma variável enquanto mantém as outras em seus valores medianos. Para gráficos de interação, ele altera duas variáveis. (Nota adicionada em novembro de 2016: plotmoagora também suporta gráficos de dependência parcial.)

O exemplo acima usa apenas duas variáveis; modelos mais complicados podem ser visualizados de maneira fragmentada, observando uma ou duas variáveis ​​de cada vez. Como as "outras" variáveis ​​são mantidas em seus valores medianos, isso mostra apenas uma fatia dos dados, mas ainda pode ser útil. Alguns exemplos estão na vinheta do pacote plotmo . Outros exemplos estão no Capítulo 10 de Plotando árvores rpart com o pacote rpart.plot .

Stephen Milborrow
fonte
4

Estou muito interessado nesse tipo de pergunta. Eu acho que há muita informação que podemos obter de uma floresta aleatória.

Sobre as interações, parece que Breiman e Cultier já tentaram analisá -lo, especialmente para RFs de classificação.

Que eu saiba, isso não foi implementado no pacote randomForest R. Talvez porque não seja tão simples e porque o significado de "interações variáveis" seja muito dependente do seu problema.

Sobre a não linearidade, não tenho certeza do que você está procurando, a floresta de regressão é usada para problemas de regressão múltipla não linear, sem antecedentes sobre o tipo de função não linear a ser usada.

Rémy Nicolle
fonte
3

No final do jogo, mas há alguns desenvolvimentos nessa frente, por exemplo, LIME e SHAP . Além disso, um pacote que vale a pena conferir é o DALEX (em particular se estiver usando R, mas de qualquer forma contiver boas folhas de dicas etc.), embora não pareça cobrir as interações no momento. E tudo isso é independente de modelo, portanto funcionará para florestas aleatórias, GBMs, redes neurais, etc.

antike
fonte
(+1) Recursos agradáveis!
Mk28
2

Uma leve modificação de florestas aleatórias que fornecem mais informações sobre os dados são os métodos de floresta causal recentemente desenvolvidos. Veja o pacote R do GRF e o documento motivador aqui . A idéia é usar os métodos aleatórios de linha de base da floresta para encontrar heterogeneidade nos efeitos causais.

Um artigo anterior ( aqui ) fornece uma abordagem detalhada de uma floresta causal simples. A página 9 do artigo fornece um procedimento passo a passo para o cultivo de uma árvore causal, que pode ser expandida para uma floresta da maneira usual.Retirado da página 9 de Athey and Wager 2017

Equação 4:

Equação 4

Equação 5: Equação 5

gannawag
fonte
11
Atualizado com links para artigos anteriores e capturas de tela desse artigo para mostrar o procedimento de árvore causal.
gannawag
1

Resposta tardia relacionada à minha pergunta aqui ( podemos tornar a floresta aleatória 100% interpretável fixando a semente? ):

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

Isso funciona também para todos os métodos de conjunto baseados na agregação de árvores.

Metariat
fonte