Hallo, ich bin Autor von Dask , einer Bibliothek für paralleles und verteiltes Rechnen in Python. Ich bin gespannt, ob in dieser Community Interesse besteht, bei der Verteilung von XGBoost auf Dask entweder für paralleles Training oder für ETL zusammenzuarbeiten.
Es gibt wahrscheinlich zwei Komponenten von Dask, die für dieses Projekt relevant sind:
Besteht hier Interesse an einer Zusammenarbeit?
@mrocklin Ich dachte, Dask hat Integrationen mit sklearn. Haben Sie sich unseren sklearn-Wrapper angesehen, um zu sehen, ob er damit funktioniert?
Eine sinnvolle Integration in ein verteiltes System muss typischerweise eher auf der Ebene pro Algorithmus als auf der Bibliotheksebene erfolgen. Es gibt einige Möglichkeiten, wie sich SKLearn und Dask gegenseitig helfen können, ja, aber sie sind nicht besonders tiefgreifend.
Dask Dataframe wäre ein guter Anfang. In unserer Codebasis haben wir eine Prüfung auf Pandas-Datenrahmen. Da könnte dask dataframe als Anfang passen.
Was passiert also, wenn jemand mit einem Multi-Terabyte-Dask-Datenrahmen ankommt? Konvertieren Sie es einfach in Pandas und fahren Sie fort? Oder gibt es eine Möglichkeit, XGBoost intelligent über einen Cluster hinweg zu parallelisieren und auf die verschiedenen Pandas-Datenrahmen zu verweisen, die einen Dask-Datenrahmen bilden?
Benutzer können Stapelgröße angeben? Ich könnte mir vorstellen, dass Benutzer von partial_fit profitieren können.
cc @tqchen , der mit dem verteilten Teil des Codes besser vertraut ist.
Die verteilte Version von xgboost kann in einen verteilten Jobstarter eingehängt werden, idealerweise erhalten Sie Datenpartitions-Feeds in xgboost und fahren dann fort.
@mrocklin Ich denke, der relevanteste Teil ist das Modul xgboost-spark und xgboost-flink, das xgboost in die mapPartition-Funktion von spark/flink einbettet. Ich schätze, es gäbe etwas Ähnliches in Dask
Die Anforderung von xgboost-Seite ist, dass XGBoost die Verbindung zwischen Prozessen per Rabit handhabt und einen Tracker (der jeden Job verbindet) von der Client-Seite starten muss.
siehe relevanten Code in https://github.com/dmlc/xgboost/blob/master/jvm-packages/xgboost4j-spark/src/main/scala/ml/dmlc/xgboost4j/scala/spark/XGBoost.scala#L112
Rabit ist so konzipiert, dass es in andere verteilte Systeme eingebettet werden kann, daher denke ich, dass es nicht allzu schwer sein könnte, die Anpassung auf der Python-Seite vorzunehmen.
Das Starten anderer verteilter Systeme von Dask aus ist normalerweise ziemlich machbar. Wie verschieben Sie Daten vom verteilten Hosting-System (spark/flink/dask) nach xg-boost? Oder dient dies dem verteilten Training für Small-Data?
Konkret erwarte ich, ein System wie folgt aufzubauen:
Entspricht dies Ihrer Erwartung? Ist es für Sie einfach, mich auf die relevante Python-API hinzuweisen?
Ja, relevante Informationen finden Sie hier https://github.com/dmlc/xgboost/blob/master/tests/distributed/ für die Python-API.
Was Sie zusätzlich tun müssen, ist, einen Rabit-Tracker auf der Treiberseite zu starten (wahrscheinlich der Ort, der dask antreibt), dies geschieht im dmlc-submit-Skript hier https://github.com/dmlc/dmlc-core /tree/master/tracker/dmlc_tracker
OK, fülle meine Gliederung von vorher aus:
Auf dem Driver/Scheduler-Knoten starten wir einen Rabit-Tracker
envs = {'DMLC_NUM_WORKER' : nworker,
'DMLC_NUM_SERVER' : nserver}
rabit = RabitTracker(hostIP=ip_address, nslave=num_workers)
envs.update(rabit.slave_envs())
rabit.start(args.num_workers) # manages connections in background thread
Ich kann auch einen ähnlichen Prozess durchlaufen, um ein PSTracker
zu starten. Sollte sich dies auf demselben zentralisierten Computer befinden oder an einem anderen Ort innerhalb des Netzwerks? Sollen es ein paar davon sein? Sollte dies vom Benutzer konfigurierbar sein?
Irgendwann lasse ich meinen Tracker (und pstrackers?) dem Rabit-Netzwerk beitreten und blockieren.
rabit.join() # join network
Auf Worker-Knoten muss ich diese Umgebungsvariablen (die ich durch normale Dask-Kanäle verschieben werde) in die lokale Umgebung ausgeben. Dann sollte es genügen, xgboost.rabit.init()
anzurufen
import os
os.environ.update(envs)
xgboost.rabit.init()
Wenn man sich den Rabit-Code ansieht, sieht es so aus, als ob Umgebungsvariablen die einzige Möglichkeit sind, diese Informationen bereitzustellen. Können Sie das verifizieren? Gibt es eine Möglichkeit, Host-/Portinformationen des Trackers als direkte Eingaben bereitzustellen?
Dann konvertiere ich meine numpy Arrays / Pandas Dataframes / scipy Sparse Arrays in DMatrix-Objekte, das scheint relativ einfach zu sein. Ich habe jedoch wahrscheinlich mehrere Datenstapel pro Arbeiter. Gibt es eine saubere Möglichkeit, den Zug mehrmals mit mehr Daten anzurufen, sobald diese verfügbar sind? Ich bin besorgt über die Kommentare zu diesen Zeilen:
# Run training, all the features in training API is available.
# Currently, this script only support calling train once for fault recovery purpose.
bst = xgb.train(param, dtrain, num_round, watchlist, early_stopping_rounds=2)
Müssen wir warten, bis alle Daten eintreffen, bevor wir mit dem Training beginnen?
Angenommen, ich habe alles oben Richtige, gibt es dann ein standardmäßiges verteiltes Trainingsbeispiel, das die Leute zur Demonstration verwenden?
Es ist nicht erforderlich, pstracker zu starten.
Ich hatte heute morgen etwas Zeit damit zu spielen. Ergebnisse hier: https://github.com/mrocklin/dask-xgboost
Bisher übernimmt es nur das verteilte Lernen eines einzelnen In-Memory-Datensatzes. Es kamen einige Fragen auf:
rabit.init
? Was genau ist die erwartete Form von Eingaben für rabit.init
? Das Ergebnis von slave_envs()
an rabit.init zu übergeben, wird offensichtlich nicht funktionieren, weil es eine Liste erwartet. Sollten wir jeden Schlüsselnamen in --key
umwandeln, vielleicht das Präfix DMLC
weglassen und in Kleinbuchstaben umwandeln?rabit.init(['DMLC_KEY1=VALUE1', 'DMLC_KEY2=VALUE2']
Zwei weitere allgemeine Fragen dazu, wie dies verwendet wird (ich habe keine Erfahrung mit XGBoost und nur wenig Erfahrung mit maschinellem Lernen, bitte verzeihen Sie meine Unwissenheit).
Welcher Anwendungsfall ist häufiger?
Jede Arbeit sollte an einer anderen Datenpartition (nach Zeilen) arbeiten, sie sollten NICHT dieselben Eingabedaten betrachten.
Dies entspricht normalerweise dem mapPartition-Betrieb in Frameworks wie Spark/Flink
Angenommen, mein Datensatz hat 8 Zeilen und 4 Spalten, wenn wir zwei Worker starten
OK, was da oben jetzt ist, ist etwas sauberer. Es wäre schön, wenn wir die Möglichkeit hätten, Ergebnisse so zu konsumieren, wie sie auf jedem Worker generiert wurden, aber wir haben das vorerst umgangen. Hier die aktuelle Lösung:
Diese Lösung erscheint handhabbar, ist aber nicht ideal. Es wäre praktisch, wenn xgboost-python Ergebnisse akzeptieren könnte, sobald sie ankommen. Aber ich denke, das nächste, was zu tun ist, ist es, es in der Praxis zu versuchen.
Ich werde mich im Internet nach Beispielen umsehen. Wenn jemand zufällig ein künstliches Problem hat, das ich einfach mit der numpy- oder Pandas-API generieren kann, wäre das willkommen. Bis dahin hier ein triviales Beispiel auf meinem Laptop mit zufälligen Daten:
In [1]: import dask.dataframe as dd
In [2]: df = dd.demo.make_timeseries('2000', '2001', {'x': float, 'y': float, 'z': int}, freq='1s', partition_freq=
...: '1D') # some random time series data
In [3]: df.head()
Out[3]:
x y z
2000-01-01 00:00:00 0.778864 0.824796 977
2000-01-01 00:00:01 -0.019888 -0.173454 1023
2000-01-01 00:00:02 0.552826 0.051995 1083
2000-01-01 00:00:03 -0.761811 0.780124 959
2000-01-01 00:00:04 -0.643525 0.679375 980
In [4]: labels = df.z > 1000
In [5]: del df['z']
In [6]: df.head()
Out[6]:
x y
2000-01-01 00:00:00 0.778864 0.824796
2000-01-01 00:00:01 -0.019888 -0.173454
2000-01-01 00:00:02 0.552826 0.051995
2000-01-01 00:00:03 -0.761811 0.780124
2000-01-01 00:00:04 -0.643525 0.679375
In [7]: labels.head()
Out[7]:
2000-01-01 00:00:00 False
2000-01-01 00:00:01 True
2000-01-01 00:00:02 True
2000-01-01 00:00:03 False
2000-01-01 00:00:04 False
Name: z, dtype: bool
In [8]: from dask.distributed import Client
In [9]: c = Client() # creates a local "cluster" on my laptop
In [10]: from dask_xgboost import train
/home/mrocklin/Software/anaconda/lib/python3.5/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
"This module will be removed in 0.20.", DeprecationWarning)
In [11]: param = {'max_depth': 2, 'eta': 1, 'silent': 1, 'objective': 'binary:logistic'} # taken from example
In [12]: bst = train(c, param, df, labels)
/home/mrocklin/Software/anaconda/lib/python3.5/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
"This module will be removed in 0.20.", DeprecationWarning)
/home/mrocklin/Software/anaconda/lib/python3.5/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
"This module will be removed in 0.20.", DeprecationWarning)
/home/mrocklin/Software/anaconda/lib/python3.5/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
"This module will be removed in 0.20.", DeprecationWarning)
/home/mrocklin/Software/anaconda/lib/python3.5/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
"This module will be removed in 0.20.", DeprecationWarning)
[14:46:20] Tree method is automatically selected to be 'approx' for faster speed. to use old behavior(exact greedy algorithm on single machine), set tree_method to 'exact'
[14:46:20] Tree method is automatically selected to be 'approx' for faster speed. to use old behavior(exact greedy algorithm on single machine), set tree_method to 'exact'
[14:46:20] Tree method is automatically selected to be 'approx' for faster speed. to use old behavior(exact greedy algorithm on single machine), set tree_method to 'exact'
[14:46:20] Tree method is automatically selected to be 'approx' for faster speed. to use old behavior(exact greedy algorithm on single machine), set tree_method to 'exact'
In [13]: bst
Out[13]: <xgboost.core.Booster at 0x7fbaacfd17b8>
Relevanter Code ist hier, falls jemand einen Blick darauf werfen möchte: https://github.com/mrocklin/dask-xgboost/blob/master/dask_xgboost/core.py
Wie gesagt, ich bin neu bei XGBoost, also vermisse ich wahrscheinlich Dinge.
Ein typisches Spielzeugbeispiel zum Ausprobieren finden Sie unter https://github.com/dmlc/xgboost/tree/master/demo/data
Es ist jedoch im libsvm-Format und muss ein wenig analysiert werden, um es in numpy zu bringen
Etwas Größeres (für das Sie eigentlich einen Cluster benötigen würden)? Oder gibt es eine Standardmethode, um einen Datensatz beliebiger Größe zu generieren?
Oder vielleicht ist eine bessere Frage: "Was würden Sie (oder jemand anderes, der diese Ausgabe liest) hier gerne sehen?"
Gebäudevorhersage jetzt. Wenn ich das Modell zurück zu einem Worker verschiebe (der Pickle/Unpickle-Prozess durchläuft) und dann bst.predict
für einige Daten aufrufe, erhalte ich die folgende Fehlermeldung:
Doing rabit call after Finalize
Meine Annahme war, dass das Modell zu diesem Zeitpunkt in sich geschlossen ist und kein Rabit mehr verwenden muss. Es scheint auf dem Client-Rechner gut zu funktionieren. Irgendwelche Gedanken, warum ich diesen Fehler erhalten könnte, wenn ich predict
?
Ein Teil von Predict verwendet immer noch Rabit, hauptsächlich weil der Predictor noch Learner mit einigen Initialisierungsroutinen verwendet, die mit dem Training geteilt werden. Irgendwann sollte dies behoben werden, aber das ist vorerst der Fall.
Ich denke, solange es für den gemeinsamen Datensatz gut funktioniert, ist es ein interessanter Ausgangspunkt.
Es gibt sowieso Gründe, einen Cluster für mittlere Daten zu verwenden (einfache Planung in Cluster env), einige der Pyspark-Benutzer könnten daran interessiert sein, es auszuprobieren, wenn wir es ein wenig bewerben
Das Testen des wirklich wichtigen Datensatzes war schwierig, z. B. (versuchen Sie 1 Datensatz mit 1 Milliarde Zeilen). Kaggle könnte einen großen Datensatz haben, der relevant sein könnte, der etwa 10 Millionen beträgt.
Dieses Repository zeigt Experimente mit dem Datensatz der Fluggesellschaften, der meiner Meinung nach in zig Millionen Zeilen und zig Spalten liegt (Tausend nach One-Hot-Codierung?). Für ihren Benchmark sieht es so aus, als hätten sie eine Stichprobe von 100.000 Zeilen genommen und künstlich generiert größere Datensätze aus dieser Stichprobe. Vermutlich könnten wir dies bei Bedarf skalieren.
Hier ist ein Beispiel, in dem diese Daten mit Pandas und xgboost auf einem einzelnen Kern verwendet werden. Alle Empfehlungen zur Datenvorbereitung, zu Parametern oder zur richtigen Vorgehensweise sind willkommen.
In [1]: import pandas as pd
In [2]: df = pd.read_csv('train-0.1m.csv')
In [3]: df.head()
Out[3]:
Month DayofMonth DayOfWeek DepTime UniqueCarrier Origin Dest Distance \
0 c-8 c-21 c-7 1934 AA ATL DFW 732
1 c-4 c-20 c-3 1548 US PIT MCO 834
2 c-9 c-2 c-5 1422 XE RDU CLE 416
3 c-11 c-25 c-6 1015 OO DEN MEM 872
4 c-10 c-7 c-6 1828 WN MDW OMA 423
dep_delayed_15min
0 N
1 N
2 N
3 N
4 Y
In [4]: labels = df.dep_delayed_15min == 'Y'
In [5]: del df['dep_delayed_15min']
In [6]: df = pd.get_dummies(df)
In [7]: len(df.columns)
Out[7]: 652
In [8]: import xgboost as xgb
/home/mrocklin/Software/anaconda/lib/python3.5/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
"This module will be removed in 0.20.", DeprecationWarning)
In [9]: dtrain = xgb.DMatrix(df, label=labels)
In [10]: param = {} # Are there better choices for parameters? I could use help here
In [11]: bst = xgb.train(param, dtrain) # or other parameters here?
[17:50:28] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 124 extra nodes, 0 pruned nodes, max_depth=6
[17:50:30] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 120 extra nodes, 0 pruned nodes, max_depth=6
[17:50:32] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 120 extra nodes, 0 pruned nodes, max_depth=6
[17:50:33] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 116 extra nodes, 0 pruned nodes, max_depth=6
[17:50:35] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 112 extra nodes, 0 pruned nodes, max_depth=6
[17:50:36] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 114 extra nodes, 0 pruned nodes, max_depth=6
[17:50:38] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 106 extra nodes, 0 pruned nodes, max_depth=6
[17:50:39] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 116 extra nodes, 0 pruned nodes, max_depth=6
[17:50:41] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 104 extra nodes, 0 pruned nodes, max_depth=6
[17:50:43] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 100 extra nodes, 0 pruned nodes, max_depth=6
In [12]: test = pd.read_csv('test.csv')
In [13]: test.head()
Out[13]:
Month DayofMonth DayOfWeek DepTime UniqueCarrier Origin Dest Distance \
0 c-7 c-25 c-3 615 YV MRY PHX 598
1 c-4 c-17 c-2 739 WN LAS HOU 1235
2 c-12 c-2 c-7 651 MQ GSP ORD 577
3 c-3 c-25 c-7 1614 WN BWI MHT 377
4 c-6 c-6 c-3 1505 UA ORD STL 258
dep_delayed_15min
0 N
1 N
2 N
3 N
4 Y
In [14]: test_labels = test.dep_delayed_15min == 'Y'
In [16]: del test['dep_delayed_15min']
In [17]: test = pd.get_dummies(test)
In [18]: len(test.columns) # oops, looks like the columns don't match up
Out[18]: 670
In [19]: dtest = xgb.DMatrix(test)
In [20]: predictions = bst.predict(dtest) # this fails because of mismatched columns
Wie auch immer, hier ist eine Option. Der Datensatz der Fluggesellschaften scheint bekannt zu sein und kann in der Praxis unpraktisch groß sein. Auch hier ist maschinelles Lernen nicht meine Spezialität, daher weiß ich nicht, ob dies angemessen ist oder nicht.
cc @TomAugspurger , der der Typ zu sein scheint, der sich Gedanken darüber machen könnte.
In Bezug auf Dask und Predict kann ich Rabit immer wieder neu einrichten. Das fühlt sich allerdings ein bisschen unsauber an, weil es die Bewertung erzwingt, anstatt die Dinge faul zu halten. Aber dies ist kein ernsthafter Blocker.
Es treten einige Probleme mit der Vorhersage auf. Zwei Fragen:
Booster.predict
innerhalb derselben Rabit-Session mehrmals anrufen?rabit.init
, Booster.predict
und rabit.finalize
in separaten Threads anrufen?Derzeit erstelle ich einen neuen Tracker und rufe rabit.init
im Haupt-Thread des Workers auf. Das funktioniert gut. Wenn ich jedoch Booster.predict
in Worker-Threads aufrufe (jeder Dask-Worker verwaltet einen Thread-Pool für die Berechnung), erhalte ich Fehler wie Doing rabit call after Finalize
. Irgendwelche Empfehlungen?
Ein Teil von Predict verwendet immer noch Rabit, hauptsächlich weil der Predictor noch Learner mit einigen Initialisierungsroutinen verwendet, die mit dem Training geteilt werden. Irgendwann sollte dies behoben werden, aber das ist vorerst der Fall.
Ich bin neugierig. Nachdem wir das trainierte Modell von einem Worker auf meinen Client-Computer serialisiert, übertragen und deserialisiert haben, scheint es mit normalen Daten gut zu funktionieren, obwohl kein Rabit-Netzwerk vorhanden ist. Es scheint, als ob ein mit Rabit trainiertes Modell verwendet werden kann, um Daten ohne Rabit vorherzusagen. Dies scheint auch in der Produktion notwendig zu sein. Können Sie hier mehr über die Einschränkungen bei der Verwendung eines mit Kaninchen trainierten Modells sagen?
Beispieldatensatz / Problem
Angenommen, ich habe alles oben Richtige, gibt es dann ein standardmäßiges verteiltes Trainingsbeispiel, das die Leute zur Demonstration verwenden?
Ich würde gerne die Ergebnisse dieses Experiments reproduzieren:
https://github.com/Microsoft/LightGBM/wiki/Experiments#parallel -experiment
mit der neuen Option binning + fast hist von XGBoost (#1950) sollten ähnliche Ergebnisse möglich sein.
Ein typisches Spielzeugbeispiel zum Ausprobieren finden Sie unter https://github.com/dmlc/xgboost/tree/master/demo/data
Es ist jedoch im libsvm-Format und muss ein wenig analysiert werden, um es in numpy zu bringen
Diese PR in sklearn könnte Sie interessieren: https://github.com/scikit-learn/scikit-learn/pull/935
@mrocklin Es gibt keine Einschränkung für die Wiederverwendung des Modells. So kann das in der verteilten Version trainierte Modell in der seriellen Version verwendet werden. Es ist nur so, dass die aktuelle Einschränkung des Prädiktors (wenn er mit Rabit kompiliert wird) eine gemischte Funktion mit der Trainingsfunktion hat (so dass der Rabit-Call passiert ist).
Jetzt wo du es sagst, denke ich, dass wir vielleicht eine Lösung für das Problem haben. Führen Sie einfach ein rabit.init
durch (ohne etwas einzugeben, und lassen Sie den Prädiktor glauben, er sei der einzige Arbeiter ), bevor die Vorhersage das Problem lösen sollte
Jawohl. Tatsächlich löst das das Problem. dask-xgboost unterstützt jetzt Predict: https://github.com/mrocklin/dask-xgboost/commit/827a03d96977cda8d104899c9f42f52dac446165
Danke für die Problemumgehung @tqchen !
Hier ist ein Workflow mit dask.dataframe und xgboost auf einem kleinen Beispiel des Datasets der Fluggesellschaften auf meinem lokalen Laptop. Sieht das für alle OK aus? Gibt es API-Elemente von XGBoost, die mir hier fehlen?
In [1]: import dask.dataframe as dd
In [2]: import dask_xgboost as dxgb
In [3]: df = dd.read_csv('train-0.1m.csv')
In [4]: df.head()
Out[4]:
Month DayofMonth DayOfWeek DepTime UniqueCarrier Origin Dest Distance \
0 c-8 c-21 c-7 1934 AA ATL DFW 732
1 c-4 c-20 c-3 1548 US PIT MCO 834
2 c-9 c-2 c-5 1422 XE RDU CLE 416
3 c-11 c-25 c-6 1015 OO DEN MEM 872
4 c-10 c-7 c-6 1828 WN MDW OMA 423
dep_delayed_15min
0 N
1 N
2 N
3 N
4 Y
In [5]: labels = df.dep_delayed_15min == 'Y'
In [6]: del df['dep_delayed_15min']
In [7]: df = df.categorize()
In [8]: df = dd.get_dummies(df)
In [9]: data_train, data_test = df.random_split([0.9, 0.1], random_state=123)
In [10]: labels_train, labels_test = labels.random_split([0.9, 0.1], random_state=123)
In [11]: from dask.distributed import Client
In [12]: client = Client() # in a large-data situation I probably should have done this before calling categorize above (which requires computation)
In [13]: param = {} # Are there better choices for parameters?
In [14]: bst = dxgb.train(client, {}, data_train, labels_train)
[14:00:46] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 120 extra nodes, 0 pruned nodes, max_depth=6
[14:00:48] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 120 extra nodes, 0 pruned nodes, max_depth=6
[14:00:50] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 122 extra nodes, 0 pruned nodes, max_depth=6
[14:00:53] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 118 extra nodes, 0 pruned nodes, max_depth=6
[14:00:55] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 120 extra nodes, 0 pruned nodes, max_depth=6
[14:00:57] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 114 extra nodes, 0 pruned nodes, max_depth=6
[14:00:59] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 118 extra nodes, 0 pruned nodes, max_depth=6
[14:01:01] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 118 extra nodes, 0 pruned nodes, max_depth=6
[14:01:04] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 94 extra nodes, 0 pruned nodes, max_depth=6
[14:01:06] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 102 extra nodes, 0 pruned nodes, max_depth=6
In [15]: bst
Out[15]: <xgboost.core.Booster at 0x7f689803af60>
In [16]: predictions = dxgb.predict(client, bst, data_test)
In [17]: predictions
Out[17]:
Dask Series Structure:
npartitions=1
None float32
None ...
Name: predictions, dtype: float32
Dask Name: _predict_part, 9 tasks
Mein kurzfristiges Ziel ist es, einen kurzen Blogpost darüber zu schreiben, damit hoffentlich jemand anderes mit mehr Erfahrung mit XGBoost und mehr Zeit kommt, um dieses Projekt zu übernehmen und voranzutreiben. (Ich arbeite, wie alle anderen hier, gleichzeitig an ein paar anderen Projekten wie diesem.)
Ich bin parteiisch für den Datensatz der Fluggesellschaften, nur weil ich ihn bereits in einem S3-Bucket habe. Ich stimme jedoch zu, dass der Criteo-Datensatz für eine bessere Demonstration in großem Maßstab sorgen würde.
Ich bin mir immer noch nicht sicher, welche Parameter ich verwenden oder wie ich das Ergebnis beurteilen soll. Für Parameter kann ich hier das Experiment von @szilard verwenden . Gibt es eine gute Möglichkeit, die Vorhersagen zu beurteilen? Suchen wir zum Beispiel nach predictions > 0.5
passend zu labels_test
?
Die vielleicht gebräuchlichste Methode zur Bewertung der Vorhersageleistung für die binäre Klassifizierung (insbesondere in Forschungs- oder Wettbewerbsumgebungen) besteht darin, die Fläche unter der ROC-Kurve (AUC) zu verwenden, obwohl man in realen Anwendungen Metriken verwenden sollte, die an den „Geschäftswerten“ ausgerichtet sind anhand der Modelle hergestellt.
Suchen wir beispielsweise nach Vorhersagen > 0,5, die mit labels_test übereinstimmen?
Jawohl. Mittelt man das am Testgerät, ist das die Testgenauigkeit. Aber es ist wahrscheinlich, dass der Datensatz unausgewogen ist (es fehlen viel mehr Klicks als Klicks). In diesem Fall ist der ROC-AUC- Wert eine bessere Metrik.
from sklearn.metrics import roc_auc_score
print(roc_auc_score(labels_test, predictions))
Angenommen, predictions
ist ein 1D-Array positiver Wahrscheinlichkeiten, die vom Modell für jede Zeile im Testsatz geschätzt werden.
@mrocklin Eine Folgefrage: Erlaubt dask Multithread-Worker-Jobs? Ich weiß, dass dies für Python aufgrund von GIL nicht sehr relevant ist. Aber xgboost kann Multi-Thread-Training pro Worker ermöglichen und sich dennoch verteilt miteinander abstimmen. Wir sollten die nthread-Argumente von xgboost immer auf die Anzahl der Arbeitskerne dieses Workers setzen
Kurze Antwort ist "ja". Die meiste Verwendung von Dask findet bei Projekten wie NumPy, Pandas, SKLearn und anderen statt, die meistens nur C- und Fortran-Code sind, der mit Python verpackt ist. Die GIL betrifft diese Bibliotheken nicht. Einige Leute verwenden Dask für ähnliche Anwendungen wie PySpark RDD (siehe dask.bag ) und werden davon betroffen sein. Diese Gruppe ist jedoch in der Minderheit.
Also ja, Dask erlaubt Multithreading-Aufgaben. Wie weisen wir XGBoost an, mehrere Threads zu verwenden? In meinen bisherigen Experimenten sehe ich eine hohe CPU-Auslastung, ohne irgendwelche Parameter zu ändern, also funktioniert vielleicht standardmäßig alles gut?
XGBoost verwendet standardmäßig Multithreading und verwendet alle verfügbaren CPU-Threads auf dem Computer (statt auf diesem Worker), wenn nthread nicht festgelegt ist. Dies kann zu einer Race-Bedingung führen, wenn mehrere Worker derselben Maschine zugewiesen werden.
Daher ist es immer gut, den nthread-Parameter auf die maximale Anzahl von Kernen zu setzen, die der Worker verwenden darf. Normalerweise ist es eine gute Praxis, etwa 4 Threads pro Arbeiter zu verwenden
Sicher, sollte in vollendet werden
https://github.com/mrocklin/dask-xgboost/commit/c22d066b67c78710d5ad99b8620edc55182adc8f
Am Montag, 20. Februar 2017 um 18:31 Uhr, Tianqi Chen [email protected]
schrieb:
XGBoost verwendet standardmäßig Multithreading und verwendet die gesamte verfügbare CPU
Threads auf dem Computer (statt auf diesem Worker), wenn nthread nicht festgelegt ist.
Dies kann zu einer Race-Bedingung führen, wenn mehrere Worker demselben zugewiesen werden
Maschine.Daher ist es immer gut, den nthread-Parameter auf die maximale Anzahl von zu setzen
Kerne, die der Arbeiter verwenden darf. Normalerweise ist es eine gute Praxis, sagen wir, es zu verwenden
4 Threads pro Arbeiter—
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/dmlc/xgboost/issues/2032#issuecomment-281205747 , oder stumm
der Faden
https://github.com/notifications/unsubscribe-auth/AASszPELRoeIvqEzyJhkKumIs-vd0PHiks5reiJngaJpZM4L_PXa
.
Notizbuch: https://gist.github.com/19c89d78e34437e061876a9872f4d2df
Kurzer Screencast (sechs Minuten): https://youtu.be/Cc4E-PdDSro
Kritisches Feedback ist sehr willkommen. Bitte entschuldigen Sie noch einmal meine Unwissenheit auf diesem Gebiet.
@mrocklin tolle Demo! Ich denke, die Laufzeitleistung (und möglicherweise die Speichernutzung) könnte durch die Verwendung von 'tree_method': 'hist', 'grow_policy': 'lossguide'
im param dict erheblich verbessert werden.
Danke @ogrisel. Mit diesen Parametern geht die Trainingszeit von sechs Minuten auf eine Minute. Die Speicherauslastung scheint jedoch ungefähr gleich zu bleiben.
OK, darauf zurückkommen. Gibt es andere XGBoost-Operationen als Trainieren und Vorhersagen, die wir implementieren sollten?
@tqchen oder @ogrisel , wenn einer von euch die Zeit hat, die Implementierung unter https://github.com/mrocklin/dask-xgboost/blob/master/dask_xgboost/core.py durchzusehen, wäre ich dankbar. Ich verstehe jedoch, dass das Durchsuchen einer fremden Codebasis nicht immer ganz oben auf der Prioritätsliste steht.
Wenn alles in Ordnung ist, werde ich der README etwas mehr hinzufügen, auf PyPI veröffentlichen, und dann können wir dieses Problem wahrscheinlich schließen.
Ich denke, nur trainieren und vorhersagen müssen verteilt werden. Andere Dinge müssen nicht verteilt werden, da sie nicht auf den Datensatz antworten
Ich habe dask-xgboost zu PyPI gepusht und nach https://github.com/dask/dask-xgboost verschoben
Danke @tqchen und @ogrisel für eure Hilfe hier. Die Zusammenarbeit machte dies relativ einfach.
Ich würde gerne Leuten helfen, wenn sie Benchmarks durchführen möchten. Bis dahin Schließung.
Hilfreichster Kommentar
Notizbuch: https://gist.github.com/19c89d78e34437e061876a9872f4d2df
Kurzer Screencast (sechs Minuten): https://youtu.be/Cc4E-PdDSro
Kritisches Feedback ist sehr willkommen. Bitte entschuldigen Sie noch einmal meine Unwissenheit auf diesem Gebiet.