Xgboost: Wie generiert XGBoost Klassenwahrscheinlichkeiten in einem Klassifikationsproblem mit mehreren Klassen?

Erstellt am 8. Nov. 2016  Â·  15Kommentare  Â·  Quelle: dmlc/xgboost

Wenn ich versuche, einen trainierten XGBoost-Baum auszugeben, erhalte ich ihn im folgenden Format:

0:[f37<39811] 
    1:[f52<199.5] 
        3:leaf=-0.021461
        4:[f2<0.00617284] 
            9:leaf=-0.0118755
            10:[f35<-83.548] 
                19:[f13<0.693844] 
                    23:[f37<1831]
                        29:leaf=-0
                        30:leaf=0.123949
                    24:leaf=0.0108628
                20:[f35<-74.6198] 
                    25:leaf=-0.0175897
                    26:leaf=0.051898
    2:leaf=-0.0239901

Es ist zwar klar, was die Baumstruktur ist, es ist jedoch nicht klar, wie die Blattwerte zu interpretieren sind. FĂŒr ein _ _ binĂ€res Klassifikationsproblem und eine Log-Verlust Kostenfunktion, können die Blattwert Klassenwahrscheinlichkeiten umgewandelt werden , um eine logistische Funktion: 1 / (1 + exp (Wert)). Bei einem _multiclass_- Klassifikationsproblem gibt es jedoch keine Informationen darĂŒber, zu welcher Klasse diese Werte gehören, und ohne diese Informationen ist nicht klar, wie die Klassenwahrscheinlichkeiten berechnet werden.

Irgendwelche Ideen? Oder gibt es eine andere Funktion, um diese Informationen aus den trainierten BĂ€umen zu holen?

Hilfreichster Kommentar

Das ist eine sehr gute Beobachtung, die mir vorher nicht aufgefallen war. Es stimmt, die Anzahl der BĂ€ume im Modell ist n_estimator x n_class . Um jedoch die Reihenfolge der BĂ€ume herauszufinden, habe ich das folgende Spielzeugbeispiel verwendet:

x = np.concatenate([np.ones([10,1])*np.array([1,0,0]),np.ones([10,1])*np.array([0,1,0]),np.ones([10,1])*np.array([0,0,1])], axis=0)
y = np.array([['a']*10+['c']*10+['b']*10]).reshape([30,1])
model = xgb.XGBClassifier(n_estimators=2, objective='mlogloss').fit(x, y)
model.booster().dump_model('trees.txt')

wodurch im Grunde ein Trainingsdatensatz erstellt wird, bei dem [1,0,0] 'a' zugeordnet ist, [0,1,0] 'c' zugeordnet ist und [0,0,1] 'b' zugeordnet ist. Ich habe absichtlich die Reihenfolge von 'b' und 'c' geÀndert, um zu sehen, ob xgboost Klassen basierend auf ihren Labels sortiert, bevor die BÀume gelöscht werden.

Hier ist das resultierende gedumpte Modell:

booster[0]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[1]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[2]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[3]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[4]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[5]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941

und hier die Schlussfolgerungen:

  1. Wie Sie bereits erwÀhnt haben, betrÀgt die Anzahl der BÀume 2x3=6, obwohl
  2. Betrachtet man die Reihenfolge der Merkmale, die die BÀume verwenden, scheint es, dass der erste Baum zur ersten Klasse gehört, der zweite Baum zur zweiten Klasse und so weiter bis hinunter zur letzten Klasse. Dann wird das gleiche Muster wiederholt, bis alle SchÀtzer abgedeckt sind.
  3. Die Reihenfolge der Klassen scheint mit dem konsistent zu sein, was numpy.unique() zurĂŒckgibt - hier ist sie alphabetisch, und deshalb verwendet der erste Baum f0, wĂ€hrend der zweite f2 verwendet .

Es wĂ€re schön, wenn diese Informationen in die xgboost-Dokumentation aufgenommen wĂŒrden.

Alle 15 Kommentare

Ich habe auch die gleiche Frage. Das einzige, was mir aufgefallen ist, ist, dass, wenn Sie 20 Klassen haben und die Anzahl der SchÀtzer auf 100 setzen, Sie 20 * 100 = 2000 BÀume in Ihrem Modell gedruckt haben. Meine Vermutung war, dass die ersten 100 SchÀtzer die erste Klasse im Vergleich zu anderen klassifizieren. Die nÀchsten 100 SchÀtzer klassifizieren die zweite Klasse im Vergleich zu anderen usw.

Kann ich nicht bestÀtigen, aber vielleicht könnte es so sein?

Das ist eine sehr gute Beobachtung, die mir vorher nicht aufgefallen war. Es stimmt, die Anzahl der BĂ€ume im Modell ist n_estimator x n_class . Um jedoch die Reihenfolge der BĂ€ume herauszufinden, habe ich das folgende Spielzeugbeispiel verwendet:

x = np.concatenate([np.ones([10,1])*np.array([1,0,0]),np.ones([10,1])*np.array([0,1,0]),np.ones([10,1])*np.array([0,0,1])], axis=0)
y = np.array([['a']*10+['c']*10+['b']*10]).reshape([30,1])
model = xgb.XGBClassifier(n_estimators=2, objective='mlogloss').fit(x, y)
model.booster().dump_model('trees.txt')

wodurch im Grunde ein Trainingsdatensatz erstellt wird, bei dem [1,0,0] 'a' zugeordnet ist, [0,1,0] 'c' zugeordnet ist und [0,0,1] 'b' zugeordnet ist. Ich habe absichtlich die Reihenfolge von 'b' und 'c' geÀndert, um zu sehen, ob xgboost Klassen basierend auf ihren Labels sortiert, bevor die BÀume gelöscht werden.

Hier ist das resultierende gedumpte Modell:

booster[0]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[1]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[2]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[3]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[4]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[5]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941

und hier die Schlussfolgerungen:

  1. Wie Sie bereits erwÀhnt haben, betrÀgt die Anzahl der BÀume 2x3=6, obwohl
  2. Betrachtet man die Reihenfolge der Merkmale, die die BÀume verwenden, scheint es, dass der erste Baum zur ersten Klasse gehört, der zweite Baum zur zweiten Klasse und so weiter bis hinunter zur letzten Klasse. Dann wird das gleiche Muster wiederholt, bis alle SchÀtzer abgedeckt sind.
  3. Die Reihenfolge der Klassen scheint mit dem konsistent zu sein, was numpy.unique() zurĂŒckgibt - hier ist sie alphabetisch, und deshalb verwendet der erste Baum f0, wĂ€hrend der zweite f2 verwendet .

Es wĂ€re schön, wenn diese Informationen in die xgboost-Dokumentation aufgenommen wĂŒrden.

Ich schließe dieses Thema jetzt.

Ich habe hier noch einen Kommentar. Ich stimme der Struktur der BĂ€ume im Mehrklassenfall zu. Aber wie werden die Blattwerte in Wahrscheinlichkeiten umgewandelt?

Es scheint, als mĂŒssten Sie fĂŒr den binĂ€ren Fall 1/(1+exp(Wert)) anwenden, wĂ€hrend Sie fĂŒr den Fall mit mehreren Klassen 1/(1+exp(-Wert)) anwenden mĂŒssen.

Das scheint wahr zu sein, wenn man sich die Beispiele ansieht. Danke fĂŒr den Tipp!

Aber ich verstehe nicht, warum diese fĂŒr BinĂ€r- und MehrklassenfĂ€lle unterschiedlich definiert sind.

Ja, in der Tat, es ist sehr seltsam und verwirrend, um es gelinde auszudrĂŒcken.

Ich bin zu diesem Schluss gekommen, indem ich es mit dem UCI-Autodatensatz getestet habe (wo Sie Autos in inakzeptabel, akzeptabel, gut oder sehr gut klassifizieren).

WÀre schön, eine zusÀtzliche Funktion zu haben, mit der Sie die BÀume in Sklearn-BÀume oder Àhnliches konvertieren können (derzeit habe ich eine Codebasis, mit der ich diese xgb-BÀume und Sklearn-BÀume in meinen eigenen definierten Entscheidungsbaum konvertieren kann). Von letzteren, den Sklearn-BÀumen, bin ich mir mindestens 100% sicher, dass diese richtig konvertiert werden. Bei den xgb-BÀumen bleiben Zweifel.

Zustimmen, das wÀre ein tolles Feature.

@GillesVandewiele @sosata : also in dieser Logistik (sagen wir fĂŒr Klasse i ) [ 1/(1+exp(-value)) ] , ist value die Summe aller Blattwerte, die den BĂ€umen dieser bestimmten Klasse entsprechen mit der Probe?

FĂŒr das Beispiel in meinem frĂŒhen Kommentar, wenn ich versuche, die Klassenwahrscheinlichkeiten fĂŒr [1 0 0] vorherzusagen:

print(model.predict_proba(np.array([[1,0,0]])))

es wird diese Ergebnisse erzeugen:

[[ 0.41852772  0.29073614  0.29073614]]

Es gibt keine Möglichkeit fĂŒr 1/(1+exp(-value)) , dies zu generieren. Die einzige Möglichkeit besteht darin, dass diese Wahrscheinlichkeiten durch eine _softmax_-Funktion der summierten Werte ĂŒber die Klassen hinweg generiert werden:

p[i] = exp(val[i])/(exp(val[1])+exp(val[2])+...+exp(val[N]))

wobei i die Zielklasse (von N Klassen) ist und val[i] die Summe aller Werte ist, die aus den zu dieser Klasse gehörenden BÀumen generiert wurden.
In unserem Beispiel:

print(np.exp(+0.122449+0.10941)/(np.exp(+0.122449+0.10941)+np.exp(-0.0674157-0.0650523)+np.exp(-0.0674157-0.0650523)))
print(np.exp(-0.0674157-0.0650523)/(np.exp(+0.122449+0.10941)+np.exp(-0.0674157-0.0650523)+np.exp(-0.0674157-0.0650523)))
print(np.exp(-0.0674157-0.0650523)/(np.exp(+0.122449+0.10941)+np.exp(-0.0674157-0.0650523)+np.exp(-0.0674157-0.0650523)))

wird erzeugen:

0.418527719063
0.290736140469
0.290736140469

Genau das hat uns die Funktion vorhersage_proba() gegeben.

Das scheint richtig zu sein @sosata

In meinem Fall wollte ich jeden der BĂ€ume einzeln konvertieren (also keine Blattwerte anderer BĂ€ume verwenden). Dort schien eine Sigmoidfunktion die Aufgabe zu erfĂŒllen.

@sosata Gibt es trotzdem eine Funktion, um die Bedeutung von Funktionen pro Klasse zu ermitteln? Ich denke, die aktuelle Implementierung summiert einfach alle BeitrĂ€ge ĂŒber alle Klassen. Die R-API scheint dafĂŒr jedoch einen solchen Parameter zu haben (siehe https://github.com/CodingCat/xgboost/commit/e9405236a01715be1550a7e3809f36fc69ad4e8a).

@mlxai Ich habe auch nur die Python-API ausprobiert und erinnere mich nicht, dass ich sie pro Klasse

Meiner Meinung nach ist die Definition der Bedeutung von Funktionen in XGBoost jedoch als die Gesamtzahl der Aufteilungen fĂŒr diese Funktion etwas vereinfacht. Persönlich berechne ich die Merkmalswichtigkeit, indem ich dieses Merkmal aus dem Merkmalssatz entferne und den resultierenden RĂŒckgang (oder Anstieg) der Genauigkeit berechne, wenn das Modell ohne dieses Merkmal trainiert und getestet wird. Ich mache dies fĂŒr alle Funktionen und definiere "Funktionswichtigkeit" als den Betrag des Abfalls (oder minus der Zunahme) der Genauigkeit. Da das Training und das Testen mehrmals mit unterschiedlichen Trainings-/Test-Teilmengen fĂŒr jede Merkmalsentfernung durchgefĂŒhrt werden können, kann man auch die Konfidenzintervalle der Merkmalswichtigkeit schĂ€tzen. Außerdem erhalten Sie durch die separate Berechnung der Wichtigkeit fĂŒr jede Klasse die Merkmalwichtigkeiten pro Klasse. Dies hat in meinen Projekten zu aussagekrĂ€ftigeren Ergebnissen gefĂŒhrt, als die Anzahl der Splits zu zĂ€hlen.

@sosata Dein Beispiel ist sehr intuitiv, aber ich weiß immer noch nicht, wie du die Punktzahlen fĂŒr jede Klasse

@chrisplyn Sie bewerten alle EntscheidungsbÀume in Ihrem Ensemble, die zu einer bestimmten Klasse gehören und summieren die resultierenden Punktzahlen.

Wie @sosata bereits richtig gesagt hat: Die Anzahl der BÀume in Ihrem Ensemble betrÀgt n_esimators * n_classes

@sosata dein Beispiel ist sehr klar. Vielen Dank!

@chrisplyn Die Vorhersageinstanz [1,0,0] wird von booster[0~5] klassifiziert und erhÀlt die Blattwerte [0.122449, -0.0674157, -0.0674157, 0.10941, -0.0650523, -0.0650523].
Denn Booster[0, 3] gehört zur Klasse 0, Booster[1, 4] gehört zur Klasse 1 und Booster[2, 5] gehört zur Klasse 2, also val[0] =(0.122449 + 0.10941), val[1 ] = (–0,0674157 + –0,0650523), val[2] = (–0,0674157 + –0,0650523).
Ist es klarer?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen