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

実行時間

GPUと2GPUのtrainの実行時間を比較してみました.(上記スクリプトのstartからendまで) どちらも1080tiを利用しています.

1GPU 2GPU
実行時間[s] 1.577 6.063

結果は1GPUの方が早いという結果になってしまいました.純粋にマルチが遅いのか,もしかしたらGPUへのデータ転送時間も含まれてしまっているのかもしれません. cudf形式に変換したりして,事前にGPUにデータ転送完了させとけばよいとかなのかな? もうちょっと詳細に見ていく必要がありそう.