Wissen aus einer zufälligen Gesamtstruktur abrufen

127

Zufällige Wälder gelten als Black Boxes, aber in letzter Zeit habe ich darüber nachgedacht, welches Wissen aus einem zufälligen Wald gewonnen werden kann.

Das offensichtlichste ist die Wichtigkeit der Variablen. In der einfachsten Variante kann dies einfach durch Berechnen der Anzahl der Vorkommen einer Variablen erfolgen.
Das zweite, woran ich dachte, waren Interaktionen. Ich denke, wenn die Anzahl der Bäume ausreichend groß ist, kann die Anzahl der Vorkommen von Variablenpaaren getestet werden (so etwas wie Chi-Quadrat-Unabhängigkeit). Das dritte sind Nichtlinearitäten von Variablen. Meine erste Idee war es, nur ein Diagramm mit einem variablen Vs-Score zu betrachten, aber ich bin mir noch nicht sicher, ob es Sinn macht.

Hinzugefügt 23.01.2012
Motivation

Ich möchte dieses Wissen nutzen, um ein Logit-Modell zu verbessern. Ich denke (oder hoffe zumindest), dass es möglich ist, übersehene Wechselwirkungen und Nichtlinearitäten zu finden.

Tomek Tarczynski
quelle
1
Mit R können Sie ein Punktdiagramm mit variabler Wichtigkeit erstellen, das von einem zufälligen Wald gemessen wird.
George Dontas
1
Ein verwandter Thread darüber, wie Kennzahlen mit variabler Wichtigkeit für das Boosten von stochastischen Gradientenbäumen berechnet werden
Antoine,
Ich weiß, dass dies wahrscheinlich zu spät ist, aber wenn Sie nur ein Logit-Modell verbessern möchten, warum verwenden Sie keine logistische Post-Lasso-Regression? Sie können das Modell einfach mit den ausgewählten Koeffizienten nach der Auswahl ohne Bestrafung / Schrumpfung nachrüsten. Sie müssen den Tuning-Vorgang etwas anpassen, aber dies ist eine viel direktere Option, die genau das tut, was Sie wollen.
ilprincipe

Antworten:

122

Zufällige Wälder sind kaum eine Black Box. Sie basieren auf Entscheidungsbäumen, die sehr einfach zu interpretieren sind:

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

Daraus ergibt sich ein einfacher Entscheidungsbaum:

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

Wenn Petal.Length <4,95, klassifiziert dieser Baum die Beobachtung als "andere". Wenn es größer als 4,95 ist, wird die Beobachtung als "virginica" klassifiziert. Eine zufällige Gesamtstruktur ist einfach eine Sammlung vieler solcher Bäume, wobei jeder auf eine zufällige Teilmenge der Daten trainiert wird. Jeder Baum "stimmt" dann über die endgültige Klassifizierung jeder Beobachtung ab.

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

Sie können sogar einzelne Bäume aus dem RF ziehen und deren Struktur betrachten. Das Format ist ein wenig anders als bei rpartModellen, aber Sie können jeden Baum untersuchen, um zu sehen, wie die Daten modelliert werden.

Darüber hinaus ist kein Modell wirklich eine Black Box, da Sie für jede Variable im Dataset die vorhergesagten Antworten mit den tatsächlichen Antworten vergleichen können. Dies ist eine gute Idee, unabhängig davon, welche Art von Modell Sie erstellen:

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)

Handlung

Ich habe die Variablen (Länge und Breite der Kelchblätter und Blütenblätter) auf einen Bereich von 0-1 normalisiert. Die Antwort ist ebenfalls 0-1, wobei 0 eine andere und 1 eine virginica ist. Wie Sie sehen, ist der zufällige Wald ein gutes Modell, auch auf dem Test-Set.

Darüber hinaus berechnet eine zufällige Gesamtstruktur verschiedene Maße von unterschiedlicher Wichtigkeit, die sehr informativ sein können:

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

Diese Tabelle gibt an, um wie viel das Entfernen jeder Variablen die Genauigkeit des Modells verringert. Schließlich gibt es noch viele andere Diagramme, die Sie aus einem zufälligen Waldmodell erstellen können, um zu sehen, was in der Blackbox vor sich geht:

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

Sie können die Hilfedateien für jede dieser Funktionen anzeigen, um eine bessere Vorstellung davon zu erhalten, was sie anzeigen.

Zach
quelle
6
Vielen Dank für die Antwort, es gibt viele nützliche Informationen, aber es ist nicht genau das, wonach ich gesucht habe. Vielleicht muss ich die Motivation, die hinter dieser Frage steckt, besser klären. Ich möchte eine zufällige Gesamtstruktur verwenden, um ein Logit-Modell zu verbessern, indem ich Interaktionen hinzufüge oder eine nichtlineare Transformation verwende.
Tomek Tarczynski
@TomekTarczynski, das ist ein interessantes Problem und ähnlich dem, mit dem ich mich gerade befasse. Ich nehme an, mit "logit model" meinen Sie logistische Regression oder ähnliches? Ich verwende die logistische Lasso-Regression (aus dem glmnet R-Paket), um Prädiktoren aus einem Modell mit Interaktionen zwischen allen Variablenpaaren auszuwählen. Ich habe noch keine nichtlinearen Begriffe hinzugefügt - aber im Prinzip sollte das auch möglich sein. Das einzige Problem, das ich denke, ist die Entscheidung, welche nichtlinearen Terme zu versuchen sind (Polynomterme, exponentielle Transformationen usw.?). Außerdem nehme ich keine Interaktionen höherer Ordnung auf, aber das ist auch einfach.
Anne Z.
2
@Tomek, was erhältst du von dieser Antwort nicht? Wenn Sie das randomForest-Paket in R verwenden, sollten die von Zach beschriebenen Darstellungen sehr nützlich sein. Insbesondere könnten Sie varImpPlot für die Featureauswahl in Ihrem Logit-Modell und partialPlot verwenden, um die Art der Transformation abzuschätzen, mit der kontinuierliche Prädiktoren im Logit-Modell getestet werden sollen. Ich würde vorschlagen, dass der letztere Plot verwendet wird, um zu bestimmen, wo nichtlineare Beziehungen zwischen Prädiktor und Antwort existieren, und um dann diese Transformation explizit durchzuführen oder einen Spline für diese Variable zu verwenden.
B_Miner
2
@b_miner - nur eine Vermutung, aber es hört sich so an, als würde sich Tomek fragen, wie man nichtlineare Wechselwirkungen zwischen Variablen findet, da die logistische Regression bereits die linearen Beziehungen erfasst.
RM999,
@ rm999 Wie definieren Sie eine nicht lineare Interaktion in einem Logit-Modell? Interaktionsterme, die zwischen transformierten Variablen erstellt wurden?
B_Miner
52

Vor einiger Zeit musste ich einigen Chemikern in meiner Firma eine RF-Modellanpassung nachweisen. Ich habe viel Zeit damit verbracht, verschiedene Visualisierungstechniken auszuprobieren. Während des Vorgangs habe ich mir aus Versehen auch einige neue Techniken ausgedacht , die ich in ein R-Paket ( forestFloor ) speziell für zufällige Waldvisualisierungen eingefügt habe .

Der klassische Ansatz sind Teilabhängigkeitsdiagramme, die unterstützt werden von: Rminer (datenbasierte Sensitivitätsanalyse wird als Teilabhängigkeit neu erfunden) oder PartialPlot im randomForest- Paket. Das Teilabhängigkeitspaket iceBOX finde ich eine elegante Möglichkeit, Wechselwirkungen zu entdecken. Habe kein Edarf-Paket verwendet , scheint aber einige feine Visualisierungen für RF zu haben. Das ggRandomForest- Paket enthält auch eine Vielzahl nützlicher Visualisierungen.

Derzeit unterstützt forestFloor randomForest-Objekte (Unterstützung für andere RF-Implementierungen ist in Vorbereitung). Es können auch Feature-Beiträge für Bäume berechnet werden, deren Steigung erhöht wurde, da sich diese Bäume nach dem Training nicht wesentlich von zufälligen Waldbäumen unterscheiden. So könnte forestFloor zukünftig XGBoost unterstützen. Partielle Abhängigkeitsdiagramme sind vollständig modellinvariant.

Allen Paketen ist gemeinsam, die geometrische Mapping-Struktur eines Modells vom Feature-Space zum Ziel-Space zu visualisieren. Eine Sinuskurve y = sin (x) wäre eine Abbildung von x nach y und kann in 2D dargestellt werden. Um eine HF-Abbildung direkt zu zeichnen, wären häufig zu viele Dimensionen erforderlich. Stattdessen kann die gesamte Mapping-Struktur projiziert, aufgeschnitten oder zerlegt werden, sodass die gesamte Mapping-Struktur in eine Folge von 2D-Randdiagrammen zerlegt wird. Wenn Ihr RF-Modell nur die Haupteffekte und keine Wechselwirkungen zwischen Variablen erfasst, reichen die klassischen Visualisierungsmethoden aus. Dann können Sie Ihre Modellstruktur wie vereinfachen:y=F(X)f1(x1)+f2(x2)+...+fd(xd). Dann kann jede Teilfunktion jeder Variablen genauso wie die Sinuskurve visualisiert werden. Wenn Ihr RF-Modell beträchtliche Interaktionen erfasst hat, ist dies problematischer. 3D-Schnitte der Struktur können die Interaktionen zwischen zwei Features und der Ausgabe visualisieren. Das Problem besteht darin, die zu visualisierende Kombination von Funktionen zu kennen ( iceBOX behebt dieses Problem). Es ist auch nicht leicht zu sagen, ob andere latente Wechselwirkungen noch nicht berücksichtigt sind.

In diesem Artikel habe ich eine sehr frühe Version von forestFloor verwendet, um zu erklären, welche tatsächliche biochemische Beziehung ein sehr kleines RF-Modell erfasst hat. In diesem Artikel beschreiben wir ausführlich Visualisierungen von Feature-Beiträgen, Forest Floor Visualizations of Random Forests .

Ich habe das simulierte Beispiel aus dem forestFloor-Paket eingefügt, in dem ich zeige, wie eine simulierte versteckte Funktion aufgedeckt wird: 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  
)

Bildbeschreibung hier eingeben

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

Bildbeschreibung hier eingeben

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

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Zuletzt der Code für partielle Abhängigkeitsdiagramme, codiert von A. Liaw, beschrieben von J. Friedman. Welche tun gut für die Haupteffekte.

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

Bildbeschreibung hier eingeben

Soren Havelund Welling
quelle
Ich habe zuvor postuliert, dass Feature-Beiträge auch für verstärkte Bäume berechnet werden könnten. Ich habe einen Testalgorithmus geschrieben und es ist möglich. Leider weisen Feature-Beiträge für geboosterte Bäume nicht die gleichen vorteilhaften Eigenschaften auf wie für eingesackte Bäume. Die Effekte eines Features streuen sich tendenziell über alle Feature-Beiträge, wodurch die Visualisierung ziemlich verwirrend wird.
Soren Havelund Welling
Oups! Ich habe einen Fehler in meinem Testalgorithmus gefunden, durch den sich alle Probleme aufgelöst haben. forestFloor könnte so aktualisiert werden, dass es für Bäume mit Farbverlaufsverstärkung gut funktioniert.
Soren Havelund Welling
3
Wird forestFloor aktualisiert, um GBM-Objekte zu akzeptieren?
Mischa
Bisher habe ich eine umgekehrte Implementierung durchgeführt, die die randomForest-Implementierung in eine voll funktionsfähige Gradienten-Boosting-Maschine einwickelt und einige Methoden zur Berechnung von Feature-Beiträgen definiert. Ich denke, die Implementierung ist tatsächlich vernünftig effizient. Den Code finden Sie hier: github.com/sorhawell/forestFloor/blob/master/inst/examples/…
Soren Havelund Welling
Einen vollen Port zu erstellen ist harte Arbeit :) Diese Implementierung wurde in ziemlich wenigen Zeilen durchgeführt.
Soren Havelund Welling
24

Um diese feinen Antworten zu ergänzen, würde ich die Verwendung gradientenverstärkter Bäume erwähnen (z. B. das GBM-Paket in R ). In R ziehe ich dies zufälligen Gesamtstrukturen vor, da fehlende Werte im Vergleich zu randomForest zulässig sind, bei denen eine Imputation erforderlich ist. Variable Wichtigkeit und Teildiagramme stehen (wie in randomForest) zur Verfügung, um die Auswahl von Features und die Untersuchung nichtlinearer Transformationen in Ihrem Logit-Modell zu erleichtern. Weiterhin wird die variable Interaktion mit der Friedman-H-Statistik ( interact.gbm) unter Angabe von angesprochen J.H. Friedman and B.E. Popescu (2005). “Predictive Learning via Rule Ensembles.” Section 8.1. Eine kommerzielle Version namens TreeNet ist von Salford Systemen und die Video - Präsentation spricht zu ihnen nehmen auf variable Interaktion Schätzung Video .

B_Miner
quelle
2
Ich stimme zu, GBMs sind ein logischer nächster Schritt aus zufälligen Wäldern.
Zach
@B_miner: Großartig! Ich weiß nicht wie, aber ich habe GBM übersehen. Es scheint, dass es mit GBM einfach ist, Wechselwirkungen und Nichtlinearitäten zu erkennen.
Tomek Tarczynski
15

Späte Antwort, aber ich bin auf ein kürzlich veröffentlichtes R-Paket forestFloor(2015) gestoßen, mit dem Sie diese "Unblackboxing" -Aufgabe automatisiert ausführen können. Es sieht sehr vielversprechend aus!

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

Erzeugt die folgenden Handlungen:

Bildbeschreibung hier eingeben

Es bietet auch eine dreidimensionale Visualisierung, wenn Sie nach Interaktionen suchen.

RUser4512
quelle
4
Ich habe die Unterstützung für ggplot2 entfernt, anstatt die letzte Zeile zu versuchen, zB: plot (ff, 1: 9, col = fcol (ff, 1: 4))
Soren Havelund Welling
9

Wie von Zach erwähnt, besteht eine Möglichkeit, ein Modell zu verstehen, darin, die Reaktion zu zeichnen, wenn die Prädiktoren variieren. Mit dem plotmo R-Paket können Sie dies problemlos für "jedes" Modell tun . Zum Beispiel

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

was gibt

Handlung

Dies ändert eine Variable, während die anderen auf ihren Medianwerten gehalten werden. Bei Interaktionsdiagrammen werden zwei Variablen geändert. (Anmerkung hinzugefügt im November 2016: plotmoUnterstützt jetzt auch partielle Abhängigkeitsdiagramme.)

Im obigen Beispiel werden nur zwei Variablen verwendet. kompliziertere Modelle können stückweise durch gleichzeitiges Betrachten von einer oder zwei Variablen visualisiert werden. Da die "anderen" Variablen bei ihren Medianwerten gehalten werden, zeigt dies nur einen Ausschnitt der Daten, kann aber dennoch nützlich sein. Einige Beispiele finden Sie in der Vignette für das plotmo-Paket . Weitere Beispiele finden Sie in Kapitel 10 des Zeichnens von Teilbäumen mit dem Paket rpart.plot .

Stephen Milborrow
quelle
4

Diese Art von Fragen interessieren mich sehr. Ich denke, es gibt eine Menge Informationen, die wir aus einem zufälligen Wald herausholen können.

In Bezug auf Interaktionen scheinen Breiman und Cultier bereits versucht zu haben, dies zu untersuchen, insbesondere im Hinblick auf Klassifizierungs-RFs.

Meines Wissens wurde dies im randomForest R-Paket nicht implementiert. Vielleicht, weil es nicht so einfach ist und weil die Bedeutung von "variablen Wechselwirkungen" stark von Ihrem Problem abhängt.

In Bezug auf die Nichtlinearität bin ich mir nicht sicher, wonach Sie suchen. Regressionswald wird für nichtlineare multiple Regressionsprobleme verwendet, ohne dass zuvor festgelegt wurde, welche Art von nichtlinearer Funktion verwendet werden soll.

Rémy Nicolle
quelle
3

Spät im Spiel gibt es aber einige neue Entwicklungen in dieser Front, zum Beispiel LIME und SHAP . Ein weiteres Paket, das es wert ist, überprüft zu werden, ist DALEX (insbesondere wenn es R verwendet, aber auf jeden Fall nette Cheatsheets usw. enthält), obwohl es derzeit keine Interaktionen zu behandeln scheint. Und diese sind alle modellunabhängig, sodass sie für zufällige Wälder, GBMs, neuronale Netze usw. funktionieren.

Antike
quelle
(+1) Gute Ressourcen!
mkt
2

Eine geringfügige Modifikation von zufälligen Wäldern, die mehr Informationen über die Daten liefert, sind die kürzlich entwickelten Methoden für kausale Wälder. Das GRF R-Paket und das Motivationspapier finden Sie hier . Die Idee ist, die zufälligen Waldgrundlinienmethoden zu verwenden, um Heterogenität in kausalen Effekten zu finden.

Eine frühere Veröffentlichung ( hier ) gibt einen detaillierten Ansatz für einen einfachen kausalen Wald. Seite 9 des Papiers beschreibt Schritt für Schritt, wie ein kausaler Baum gezüchtet wird, der dann auf übliche Weise zu einem Wald ausgebaut werden kann.Aus Seite 9 von Athey and Wager 2017

Gleichung 4:

Gleichung 4

Gleichung 5: Gleichung 5

gannawag
quelle
1
Aktualisiert mit Links zu früheren Veröffentlichungen und Screenshots von dieser Veröffentlichung, um die kausale Baumprozedur zu zeigen.
Gannawag
1

Späte Antwort zu meiner Frage hier ( Können wir Random Forest durch Fixieren des Samens zu 100% interpretierbar machen? ):

Sei der Startwert bei der Erstellung des Boostrapped-Trainingssatzes und der Startwert bei der Auswahl der Teilmenge des Features (zur Vereinfachung liste ich hier nur zwei Arten von Startwerten auf).z1z2

  1. Aus werden Boostrapped-Trainingssätze erstellt: , , , ..., . m D 1 ( z 1 ) D 2 ( z 1 ) D 3 ( z 1 ) D m ( z 1 )z1mD1(z1)D2(z1)D3(z1)Dm(z1)
  2. Aus diesen Überleitungssätzen werden entsprechende Entscheidungsbäume erstellt und über Kreuzvalidierung abgestimmt: , , , ..., .T 1 ( z 1 , z 2 ) T 2 ( z 1 , z 2 ) T 3 ( z 1 , z 2 ) T m ( z 1 , z 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

Dies funktioniert auch für alle Ensemblemethoden, die auf der Aggregation von Bäumen basieren.

Metariat
quelle