Pozyskiwanie wiedzy z przypadkowego lasu

127

Losowe lasy są uważane za czarne skrzynki, ale ostatnio zastanawiałem się, jaką wiedzę można uzyskać z losowego lasu?

Najbardziej oczywistą rzeczą jest znaczenie zmiennych, w najprostszym wariancie można to zrobić po prostu przez obliczenie liczby wystąpień zmiennej.
Drugą rzeczą, o której myślałem, były interakcje. Myślę, że jeśli liczba drzew jest wystarczająco duża, można przetestować liczbę wystąpień par zmiennych (coś w rodzaju niezależności chi-kwadrat). Trzecią rzeczą są nieliniowości zmiennych. Moim pierwszym pomysłem było przyjrzenie się wykresowi zmiennej oceny Vs, ale nie jestem jeszcze pewien, czy ma to jakiś sens.

Dodano 23.01.2012
Motywacja

Chcę wykorzystać tę wiedzę do ulepszenia modelu logit. Myślę (a przynajmniej mam nadzieję), że można znaleźć interakcje i nieliniowości, które zostały przeoczone.

Tomek Tarczyński
źródło
1
Używając R, możesz wytworzyć Dotchart o różnym znaczeniu, mierzonym przez Losowy Las.
George Dontas
1
pokrewny wątek, w jaki sposób obliczane są miary o zmiennym znaczeniu dla wzmocnienia gradientowego drzewa stochastycznego
Antoine,
Zdaję sobie sprawę, że prawdopodobnie jest już za późno, ale jeśli chcesz tylko ulepszyć model logit, dlaczego nie zastosować regresji logistycznej po lasso? Możesz po prostu zamontować model, używając wybranych współczynników po dokonaniu wyboru, bez karania / kurczenia się. Będziesz musiał nieco ulepszyć procedurę strojenia, ale jest to o wiele bardziej bezpośrednia opcja, która robi dokładnie to, co chcesz.
ilprincipe

Odpowiedzi:

122

Losowe lasy nie są czarną skrzynką. Opierają się na drzewach decyzyjnych, które są bardzo łatwe do interpretacji:

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

W rezultacie powstaje proste drzewo decyzyjne:

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

Jeśli długość płatka <4,95, drzewo to klasyfikuje obserwację jako „inne”. Jeśli jest większy niż 4,95, klasyfikuje obserwację jako „virginica”. Losowy las to prosty zbiór wielu takich drzew, z których każde jest trenowane na losowym podzbiorze danych. Każde drzewo następnie „głosuje” na ostateczną klasyfikację każdej obserwacji.

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

Możesz nawet wyciągać pojedyncze drzewa z RF i patrzeć na ich strukturę. Format jest nieco inny niż w przypadku rpartmodeli, ale możesz sprawdzić każde drzewo, jeśli chcesz, i zobaczyć, jak modeluje dane.

Ponadto żaden model nie jest tak naprawdę czarną skrzynką, ponieważ można zbadać przewidywane odpowiedzi w stosunku do rzeczywistych odpowiedzi dla każdej zmiennej w zestawie danych. To dobry pomysł niezależnie od tego, jaki model budujesz:

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)

wątek

Znormalizowałem zmienne (długość i szerokość płatka i płatka) do zakresu 0-1. Odpowiedź wynosi również 0-1, gdzie 0 to inne, a 1 to virginica. Jak widać, losowy las jest dobrym modelem, nawet na zestawie testowym.

Ponadto losowy las obliczy różne miary o różnym znaczeniu, które mogą być bardzo pouczające:

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

Ta tabela przedstawia, jak bardzo usunięcie każdej zmiennej zmniejsza dokładność modelu. Na koniec istnieje wiele innych działek, które można wykonać z losowego modelu lasu, aby zobaczyć, co dzieje się w czarnej skrzynce:

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

Możesz wyświetlić pliki pomocy dla każdej z tych funkcji, aby uzyskać lepszy obraz tego, co wyświetlają.

Zach
źródło
6
Dzięki za odpowiedź, jest wiele przydatnych informacji, ale nie jest to dokładnie to, czego szukałem. Może muszę lepiej wyjaśnić motywację stojącą za tym pytaniem. Chcę użyć losowego lasu, aby ulepszyć model logit, przez ulepszenie mam na myśli dodanie interakcji lub użycie transformacji nieliniowej.
Tomek Tarczyński
@TomekTarczynski to interesujący problem i podobny do tego, z którym mam teraz do czynienia. Zakładam, że przez „model logit” masz na myśli regresję logistyczną czy coś podobnego? Używam regresji logistycznej lasso (z pakietu glmnet R), aby wybrać predyktory z modelu z interakcjami między wszystkimi parami zmiennych. Nie dodałem jeszcze żadnych terminów nieliniowych - ale w zasadzie to też powinno być możliwe. Chyba jedynym problemem jest podjęcie decyzji, które warunki nieliniowe wypróbować (warunki wielomianowe, transformacje wykładnicze itp.). Ponadto nie odbieram żadnych interakcji wyższego rzędu, ale to też jest łatwe.
Anne Z.
2
@Tomek, czego nie otrzymujesz z tej odpowiedzi? Jeśli używasz pakietu randomForest w R, wówczas wykresy, które opisuje Zach, powinny być bardzo przydatne. W szczególności można użyć varImpPlot do wyboru funkcji w modelu logit i czesciPlot do oszacowania typu transformacji, aby wypróbować ciągłe predyktory w modelu logit. Sugerowałbym, aby ten drugi wykres służył do ustalenia, gdzie istnieją nieliniowe relacje między predyktorem a odpowiedzią, a następnie pozwala ci dokonać tej transformacji jawnie lub użyć splajnu na tej zmiennej.
B_Miner
2
@b_miner - tylko zgadnij, ale wygląda na to, że tomek pyta, jak znaleźć nieliniowe interakcje między zmiennymi, ponieważ regresja logistyczna już wychwytuje zależności liniowe.
rm999
@ rm999 Jak zdefiniować nieliniową interakcję w modelu logit? Warunki interakcji utworzone między transformowanymi zmiennymi?
B_Miner
52

Jakiś czas temu musiałem uzasadnić dopasowanie modelu RF do niektórych chemików w mojej firmie. Spędziłem dość czasu próbując różnych technik wizualizacji. Podczas tego procesu przypadkowo opracowałem też nowe techniki, które umieściłem w pakiecie R ( forestFloor ) specjalnie do losowych wizualizacji lasu.

Klasycznym podejściem są wykresy częściowej zależności wspierane przez: Rminer (analiza wrażliwości oparta na danych jest na nowo wymyślona zależność częściowa) lub częściowy wykres w pakiecie randomForest . Dla mnie iceBOX pakiet częściowej zależności jest eleganckim sposobem na odkrywanie interakcji. Nie korzystałem z pakietu edarf , ale wydaje się, że ma kilka świetnych wizualizacji dedykowanych dla RF. GgRandomForest Pakiet zawiera również duży zestaw przydatnych wizualizacje.

Obecnie forestFloor obsługuje obiekty randomForest (trwa proces obsługi innych implementacji RF). Można również obliczyć wkład funkcji dla drzew ze wzmocnieniem gradientu, ponieważ drzewa te po treningu niewiele różnią się od losowych drzew leśnych. Dlatego forestFloor może w przyszłości obsługiwać XGBoost. Wykresy częściowej zależności są całkowicie niezmienne w modelu.

Wszystkie pakiety mają wspólną wizualizację geometrycznej struktury mapowania modelu od przestrzeni cech do przestrzeni docelowej. Krzywa sinusoidalna y = sin (x) byłaby odwzorowaniem od x do y i może być wykreślona w 2D. Bezpośrednie wykreślenie mapowania RF często wymagałoby zbyt wielu wymiarów. Zamiast tego ogólną strukturę odwzorowania można rzutować, kroić lub rozkładać, tak że cała struktura odwzorowania jest sprowadzana do sekwencji dwuwymiarowych wykresów brzeżnych. Jeśli Twój model RF ma tylko główne efekty i nie ma interakcji między zmiennymi, klasyczne metody wizualizacji będą w porządku. Następnie możesz uprościć strukturę modelu w ten sposóby=F(X)f1(x1)+f2(x2)+...+fd(xd). Następnie każdą funkcję cząstkową każdej zmiennej można wizualizować tak jak krzywą sinusoidalną. Jeśli Twój model RF przechwycił spore interakcje, jest to bardziej problematyczne. Plastry 3D struktury mogą wizualizować interakcje między dwiema cechami i danymi wyjściowymi. Problem polega na tym, aby wiedzieć, którą kombinację funkcji wizualizować ( iceBOX rozwiązuje ten problem). Nie jest również łatwo stwierdzić, czy inne ukryte interakcje nadal nie są uwzględniane.

W tym artykule użyłem bardzo wczesnej wersji ForestFloor, aby wyjaśnić, jaki faktyczny związek biochemiczny uchwycił bardzo mały model RF. W tym artykule dokładnie opisujemy wizualizacje wkładu obiektów, Wizualizacje lasów losowych na dnie lasu .

Wkleiłem symulowany przykład z pakietu forestFloor, w którym pokazuję, jak odkryć symulowaną funkcję ukrytą hałasy=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  
)

wprowadź opis zdjęcia tutaj

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

wprowadź opis zdjęcia tutaj

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

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Na koniec kod wykresów częściowej zależności kodowany przez A. Liawa opisany przez J. Friedmana. Które działają dobrze dla głównych efektów.

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

wprowadź opis zdjęcia tutaj

Soren Havelund Welling
źródło
Wcześniej postulowałem, że wkład funkcji można również obliczyć dla drzew wzmocnionych. Napisałem algorytm testowy i jest to możliwe. Niestety wkład funkcji w drzewach wzmocnionych, nie wykazuje tych samych korzystnych właściwości, co w przypadku drzew w workach. Efekty jednej cechy zwykle rozpraszają się we wszystkich wkładach funkcji, więc wizualizacja staje się dość myląca.
Soren Havelund Welling,
Ojej! W moim algorytmie testowym znalazłem błąd, który sprawił, że wszystkie problemy zniknęły. forestFloor można zaktualizować tak, aby działał dobrze w przypadku drzew z podwyższonym gradientem.
Soren Havelund Welling
3
czy forestFloor został zaktualizowany, aby akceptować obiekty GBM?
Misha,
Do tej pory zrobiłem implementację odwrotną, która otacza implementację randomForest w pełni funkcjonalną maszynę do zwiększania gradientu i zdefiniowałem niektóre metody obliczania wkładu funkcji. Myślę, że wdrożenie jest w rzeczywistości dość wydajne. Kod można znaleźć tutaj: github.com/sorhawell/forestFloor/blob/master/inst/examples/…
Soren Havelund Welling
Wykonanie pełnego portu jest ciężką pracą :) Ta implementacja została wykonana w kilku liniach.
Soren Havelund Welling
24

Aby uzupełnić te doskonałe odpowiedzi, wspomnę o użyciu drzew wzmocnionych gradientem (np. Pakiet GBM w R ). W R wolę to od losowych lasów, ponieważ brakujące wartości są dozwolone w porównaniu z randomForest, gdzie wymagana jest imputacja. Dostępne są zmienne znaczenia i wykresy częściowe (jak w przypadku randomForest), aby pomóc w wyborze cech i eksploracji transformacji nieliniowej w modelu logit. Ponadto, zmienne oddziaływanie skierowane jest w Friedmana statystyki H ( interact.gbm) z zadanym jak J.H. Friedman and B.E. Popescu (2005). “Predictive Learning via Rule Ensembles.” Section 8.1. Wersja komercyjna o nazwie TreeNet jest dostępna od Salford Systems i ta prezentacja wideo mówi o ich podejściu do zmiennej oceny interakcji wideo .

B_Miner
źródło
2
Zgadzam się, GBM to logiczny następny krok od losowych lasów.
Zach
@B_miner: Świetnie! Nie wiem jak, ale przeoczyłem GBM. Wydaje się, że za pomocą GBM łatwo jest wykryć interakcje i nieliniowości.
Tomek Tarczyński
15

Późna odpowiedź, ale natknąłem się na ostatni pakiet R forestFloor(2015), który pomaga ci wykonywać to zadanie „unblackboxing” w sposób zautomatyzowany. Wygląda bardzo obiecująco!

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

Tworzy następujące wykresy:

wprowadź opis zdjęcia tutaj

Zapewnia również trójwymiarową wizualizację, jeśli szukasz interakcji.

RUser4512
źródło
4
Usunąłem obsługę ggplot2, zamiast ostatniej linii spróbuj np .: plot (ff, 1: 9, col = fcol (ff, 1: 4))
Soren Havelund Welling
9

Jak wspomniał Zach, jednym ze sposobów zrozumienia modelu jest wykreślenie odpowiedzi przy różnych predyktorach. Możesz to łatwo zrobić dla „dowolnego” modelu z pakietem plotmo R. Na przykład

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

co daje

wątek

Zmienia to jedną zmienną, utrzymując pozostałe na ich wartościach mediany. W przypadku wykresów interakcji zmienia dwie zmienne. (Uwaga dodana lis 2016: plotmoteraz obsługuje również wykresy częściowej zależności).

Powyższy przykład wykorzystuje tylko dwie zmienne; bardziej skomplikowane modele można wizualizować w sposób fragmentaryczny, patrząc na jedną lub dwie zmienne jednocześnie. Ponieważ „inne” zmienne są utrzymywane na wartościach mediany, pokazuje to tylko fragment danych, ale może być nadal użyteczny. Niektóre przykłady znajdują się w winiecie dla pakietu plotmo . Inne przykłady znajdują się w rozdziale 10 Rysowanie drzew rpart z pakietem rpart.plot .

Stephen Milborrow
źródło
4

Sam jestem bardzo zainteresowany tego typu pytaniami. Myślę, że z losowego lasu można uzyskać wiele informacji.

Jeśli chodzi o interakcje, wygląda na to, że Breiman i Cultier próbowali już na to spojrzeć, szczególnie w przypadku klasyfikacji RF.

Według mojej wiedzy nie zostało to zaimplementowane w pakiecie randomForest R. Może dlatego, że może nie być tak proste i ponieważ znaczenie „interakcji zmiennych” jest bardzo zależne od twojego problemu.

Jeśli chodzi o nieliniowość, nie jestem pewien, czego szukasz, las regresji jest używany w przypadku problemów z nieliniową regresją wielokrotną bez żadnych priorytetów dotyczących tego, jakiego rodzaju funkcji nieliniowej należy użyć.

Rémy Nicolle
źródło
3

Późno w grze, ale w tym froncie pojawiły się nowe rozwiązania, na przykład LIME i SHAP . Warto również sprawdzić pakiet DALEX (zwłaszcza jeśli używasz R, ale w każdym razie zawiera ładne kody do gry itp.), Choć w tej chwili nie obejmuje on interakcji. Wszystkie są niezależne od modelu, więc będą działać dla losowych lasów, GBM, sieci neuronowych itp.

antike
źródło
(+1) Ładne zasoby!
mkt
2

Nieznaczną modyfikacją losowych lasów, które dostarczają więcej informacji o danych, są niedawno opracowane metody lasów przyczynowych. Zobacz pakiet GRF R i motywacyjny papier tutaj . Chodzi o to, aby użyć losowych podstawowych metod leśnych do znalezienia niejednorodności w skutkach przyczynowych.

Wcześniejszy artykuł ( tutaj ) podaje szczegółowe podejście do prostego lasu przyczynowego. Strona 9 artykułu zawiera krok po kroku procedurę uprawy drzewa przyczynowego, którą można następnie rozszerzyć do lasu w zwykły sposób.Zaczerpnięte ze strony 9 z Athey and Wager 2017

Równanie 4:

Równanie 4

Równanie 5: Równanie 5

gannawag
źródło
1
Zaktualizowano z linkami do wcześniejszego artykułu i zrzutami ekranu z tego artykułu, aby pokazać procedurę drzewa przyczynowego.
gannawag
1

Późna odpowiedź związana z moim pytaniem tutaj ( czy możemy sprawić, że Random Forest będzie w 100% możliwy do interpretacji poprzez ustalenie ziarna? ):

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

Działa to również w przypadku wszystkich metod zespołowych opartych na agregacji drzew.

Metariat
źródło