XGBoostでマルチGPUを利用する
XGBoostでもDeepLearning FWのようにマルチGPUを利用することができます。 分散のための下回りはdaskが担当し、daskを利用するためにxgboostのdask用wrapper(dask-xgboost)を利用します。 利用方法としてはPandas DataFrameをdask形式に変換して、dask-xgboostに渡してあげる形になっています。 Pandasをdask形式に変換するタイミングは以下の記述があるので、XGBoostに渡す直前がいいかもしれないです。 docs.dask.org
Note that, despite parallelism, Dask.dataframe may not always be faster than Pandas. We recommend that you stay with Pandas for as long as possible before switching to Dask.dataframe.
dask-cudaだとpandasの操作は早いかもしれないが未確認。
必要モジュールのインストール
pip install xgboost dask-xgboost pandas fsspec
dask-cudaが以下のエラーが発生してしまう関係でバージョン0.10以降を利用する必要があります。
ImportError: cannot import name 'TOTAL_MEMORY'
現時点(2019/11/30)で公開されている最新バージョンはpipだと0.6となっているので,dask-cudaから最新版を直接インストールして解決しました。
サンプルコード
dask-schedulerを走行
pythonコードを走らせる前に、まずはdaskのスケジューラを起動する必要があります。
(venv) ➜ sample ✗ dask-scheduler distributed.scheduler - INFO - ----------------------------------------------- distributed.scheduler - INFO - Local Directory: /tmp/scheduler-j9te68us distributed.scheduler - INFO - ----------------------------------------------- distributed.scheduler - INFO - Clear task state distributed.scheduler - INFO - Scheduler at: tcp://{IPADRESS}:8786
pytonサンプル
以下がpythonのサンプルです. ノード内マルチにのみ対応してるよう.
参照 (情報は少し古め..)
from dask import dataframe as dd from dask_cuda import LocalCUDACluster from dask.distributed import Client, LocalCluster import dask_xgboost as dxgb import pandas as pd import time if __name__ == '__main__': cluster = LocalCUDACluster() client = Client(cluster) df = pd.read_csv(f"data/train.csv") # Pandasで処理を行う label_train = df['meter_reading'] df_train = df.drop(columns=['meter_reading', 'timestamp']) # Pandasからdaskへの変換 ddf_train = dd.from_pandas(df_train, npartitions=10) dlabel_train = dd.from_pandas(label_train, npartitions=10) # dask_xgboostのtrainを呼び出す(通常のxgboostを利用するのと同様に利用できそう) start = time.time() dxgb.train(client, {"boosting": "gbdt", 'objective': 'reg:squarederror', 'tree_method': 'gpu_hist', 'num_iterations': 1000, 'learning_rate':0.01}, data=ddf_train, labels=dlabel_train) end = time.time() print("elapsed train time:", round(end -start, 3), '[s]')
実行時間
1GPUと2GPUのtrainの実行時間を比較してみました.(上記スクリプトのstartからendまで) どちらも1080tiを利用しています.
1GPU | 2GPU | |
---|---|---|
実行時間[s] | 1.577 | 6.063 |
結果は1GPUの方が早いという結果になってしまいました.純粋にマルチが遅いのか,もしかしたらGPUへのデータ転送時間も含まれてしまっているのかもしれません. cudf形式に変換したりして,事前にGPUにデータ転送完了させとけばよいとかなのかな? もうちょっと詳細に見ていく必要がありそう.