【Python】cuMLの性能を評価してみた
KaggleでRAPIDSを使う方法 が公開されていたので、RAPIDSに含まれている機械学習ライブラリcuMLについて、簡単な性能評価を行ってみた。
cuMLを利用することで、GPU上でランダムフォレストなどの機械学習のアルゴリズムを利用することができ、学習の高速化が期待できる。
準備
今回はKaggle上でcuMLの性能評価を行うため、Kaggleで新しいNotebookを作成した上で次の手順に従い、RAPIDSを利用できる環境を整える必要がある。
- RAPIDSのDataset をNotebookに追加
- NotebookのAcceleratorをGPUに変更
- Notebookの最初に次のコマンド一式をコピペ
import sys !cp ../input/rapids/rapids.0.14.0 /opt/conda/envs/rapids.tar.gz !cd /opt/conda/envs/ && tar -xzvf rapids.tar.gz > /dev/null sys.path = ["/opt/conda/envs/rapids/lib/python3.7/site-packages"] + sys.path sys.path = ["/opt/conda/envs/rapids/lib/python3.7"] + sys.path sys.path = ["/opt/conda/envs/rapids/lib"] + sys.path !cp /opt/conda/envs/rapids/lib/libxgboost.so /opt/conda/lib/
※ Kaggleのコンテナに搭載されているPythonのバージョン等が異なる場合は上記のコマンドがエラーとなるため、適宜修正が必要
今回はお試し利用であるため、TitanicのDatasetを利用する。 ベンチマークに使用したソースコード一式は、Kaggle上で公開している。
cuMLのベンチマークプログラム
今回は、RAPIDSに含まれているDataFrameライブラリ cuDF と機械学習ライブラリ cuML を利用し、scikit-learn と性能比較した。
cuDFとcuMLを利用するためには、以下のパッケージをインポートする必要がある。
import cudf import cuml
pandasのDataFrameからcuDFのDataFrameへは、cudf.from_pandas
で変換できる。
train_df = pd.read_csv("../input/titanic/train.csv")
train_df_gpu = cudf.from_pandas(train_df)
cuMLはscikit-learnと似たようなインターフェースを提供しているため、過去にscikit-learnを使ったことがある人なら比較的簡単にcuMLを使うことができる。 しかし一部のAPIの引数が異なっていることと、入力データとして受け付けるデータ型に制約があることに注意しなければならない。 特にデータ型については、scikit-learnに入力したデータ型と同じデータ型でcuMLのAPIを利用しようとして何度もエラーで悩まされたため、結局全てのデータ型をfloat32に型変換した。
今回は、以下の3つのアルゴリズムを性能評価対象とした。
- k近傍法
- サポートベクタ―マシン
- ランダムフォレスト
scikit-learnとcuMLを用いて性能評価で使用したベンチマークプログラムを次に示す。
import time import pandas as pd import sklearn.neighbors import sklearn.svm import sklearn.ensemble from sklearn.model_selection import KFold import cudf import cuml import matplotlib.pyplot as plt import numpy as np NFOLDS = 5 ITERATION = 300 # Load data. train_orig_df = pd.read_csv("../input/titanic/train.csv") test_orig_df = pd.read_csv("../input/titanic/test.csv") # Pre-process train_df = train_orig_df.copy() train_df.drop(["Cabin", "Ticket", "Name"], axis=1, inplace=True) train_df = pd.get_dummies(train_df.iloc[:, 1:], columns=["Pclass", "Sex", "Embarked"]) train_df.dropna(inplace=True) X_all = train_df.drop(["Survived"], axis=1).astype("float32") y_all = train_df["Survived"].astype("int32") X_all_gpu = cudf.from_pandas(X_all) y_all_gpu = cudf.from_pandas(y_all) # Benchmark code. def bench(X, y, classifiers, params): elapsed = {} for name, clf_class in classifiers.items(): elapsed_list = [] for _ in range(ITERATION): kf = KFold(n_splits=NFOLDS) clf = clf_class() clf.set_params(**params[name]) elapsed_sum = 0 for i, (train_idx, val_idx) in enumerate(kf.split(X, y)): X_train = X_all.iloc[train_idx] y_train = y_all.iloc[train_idx] X_val = X_all.iloc[val_idx] y_val = y_all.iloc[val_idx] start = time.time() clf.fit(X_train, y_train) elapsed_sum += time.time() - start elapsed_list.append(elapsed_sum) elapsed[name] = pd.Series(elapsed_list).mean() return elapsed # scikit-learn classifiers = { "KNN": sklearn.neighbors.KNeighborsClassifier, "SVM": sklearn.svm.SVC, "RandomForest": sklearn.ensemble.RandomForestClassifier } params = { "KNN": {}, "SVM": { "random_state": 47 }, "RandomForest": { "n_estimators": 100, "random_state": 47 } } elapsed_sklearn = bench(X_all, y_all, classifiers, params) # cuML classifiers = { "KNN": cuml.neighbors.KNeighborsClassifier, "SVM": cuml.svm.SVC, "RandomForest": cuml.ensemble.RandomForestClassifier } params = { "KNN": {}, "SVM": {}, "RandomForest": { "n_estimators": 100 } } elapsed_cuml = bench(X_all_gpu, y_all_gpu, classifiers, params) # Results. left = np.arange(len(elapsed_sklearn.keys())) width = 0.3 fig = plt.figure(figsize=(6, 6)) fig.patch.set_alpha(1) plt.subplot(1, 1, 1) plt.bar(left, elapsed_sklearn.values(), color='b', width=width, label="Scikit Learn", align="center") plt.bar(left + width, elapsed_cuml.values(), color="g", width=width, label="cuML", align="center") plt.xticks(left + width / 2, elapsed_sklearn.keys()) plt.legend(loc=2) plt.ylabel("sec / iter") plt.title("fit() performance") plt.show()
性能評価
ベンチマークプログラムを実行すると、次のような結果が得られた。
いずれのアルゴリズムについてもcuMLの方が優れているが、今回性能評価に利用したTitanicのデータ量が小さいこともあり、ランダムフォレスト除くとその性能差はわずかしかない。 より大きなデータを用いた学習など、計算量が多いものではcuMLのほうが優勢になると考えられる。
データ型の制約によりcuMLの使い勝手はscikit-learnと比較して劣るものの、計算量の多いアルゴリズムや非常に大きなデータを扱うときにはcuMLによる高速化が活きてくると考える。 cuMLなどを開発しているRAPIDSには KaggleのGrandmasterの人も在席している ため、使い勝手の面でも性能面でも改善が期待できるcuMLの発展が楽しみである。
【2020.12.11 追記】
Home Credit Default Risk のデータセットを利用し、KNNとランダムフォレストについて性能評価した。
Titanicと比較してデータサイズが大きいこともあり、scikit-learnと比較してよりcuMLのほうが性能が高い結果が得られた。 このことから、データサイズが大きい場合はcuMLを利用したときの効果が非常に高くなると言える。 一方で、データサイズが小さいとGPUの処理量に対してCPUの処理量の割合が多くなるため、cuMLがあまり活きてこないと考えられる。
なお今回評価に使用したソースコードは、KaggleのNotebook として公開している。