Python XGBoost + pandas 連携の改善
一部 こちらの続き。その後 いくつかプルリクを送り、XGBoost
と pandas
を連携させて使えるようになってきたため、その内容を書きたい。
できるようになったことは 以下 3 点。
DMatrix
でのラベルと型の指定pd.DataFrame
からのDMatrix
の作成xgb.cv
の結果をpd.DataFrame
として取得
補足 XGBoost では PyPI の更新をスクリプトで不定期にやっているようで、同一バージョンに見えても枝番が振られていたりして見分けにくい。記載は本日時点のこのコミットの情報。
%matplotlib inline import numpy as np import xgboost as xgb from sklearn import datasets import matplotlib.pyplot as plt plt.style.use('ggplot') xgb.__version__ # '0.4'
1. DMatrix
でのラベルと型の指定
これは pandas
関係ない。DMatrix
に以下 2 つのプロパティが追加され、任意のラベル/型が指定できるようにした (指定しない場合はこれまでと同じ挙動)。
feature_names
: 変数のラベルを指定。ラベルは英数字のみ。feature_types
: 変数の型を指定。指定できるのは、q
: 量的変数 (既定)i
: ダミー変数int
: 整数型float
: 浮動小数点型。
まずは feature_names
のみを指定。
iris = datasets.load_iris() features = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'] dm = xgb.DMatrix(iris.data, label=iris.target, feature_names=features) dm.feature_names # ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']
DMatrix
で指定した feature_names
は xgb.Booster
へも引き継がれ、.get_dump
時 やプロットに利用できる。
params={'objective': 'multi:softprob', 'eval_metric': 'mlogloss', 'eta': 0.3, 'num_class': 3} np.random.seed(1) bst = xgb.train(params, dm, num_boost_round=18) xgb.plot_importance(bst)
次に feature_types
。これは内部的な学習には全く関係がなく、.get_dump
や xgb.plot_tree
をする際の ラベルのフォーマットを決めるだけ。例えば以下のようなデータがあったとする。
data = np.array([[1, 0.1, 0], [2, 0.2, 1], [2, 0.2, 0]]) data # array([[ 1. , 0.1, 0. ], # [ 2. , 0.2, 1. ], # [ 2. , 0.2, 0. ]])
このとき、1 列目を int
, 2 列目を float
, 3 列目をダミー変数 i
として扱いたければ以下のように指定すると、 plot_tree
したときの見た目が若干変わる。全変数 既定 (q
: 量的変数) でもとくに不自由ないので、強いこだわりのある方以外は指定する必要はない。
dm = xgb.DMatrix(data, feature_names=['A', 'B', 'C'], feature_types=['int', 'float', 'i']) dm.feature_types # ['int', 'float', 'i']
補足 これまで、変数のラベル/型の指定は "featmap.txt" のような設定ファイルから別途指定する必要があった。この指定が Python で直接できるようになった。
2 pd.DataFrame
からの DMatrix
の作成
pandas.DataFrame
から DMatrix
が作成できるようにした。このとき、feature_names
と feature_types
は DataFrame
の定義から適当に設定される。
補足 DMatrix
の label
には pd.Series
が渡せる (前からできた)。
import pandas as pd # 表示行数を指定 pd.set_option('display.max_rows', 8) # 一行に表示する文字数を指定 pd.set_option('display.width', 120) # pd.DataFrame を作成 train = pd.DataFrame(iris.data, columns=['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']) train # SepalLength SepalWidth PetalLength PetalWidth # 0 5.1 3.5 1.4 0.2 # 1 4.9 3.0 1.4 0.2 # 2 4.7 3.2 1.3 0.2 # 3 4.6 3.1 1.5 0.2 # .. ... ... ... ... # 146 6.3 2.5 5.0 1.9 # 147 6.5 3.0 5.2 2.0 # 148 6.2 3.4 5.4 2.3 # 149 5.9 3.0 5.1 1.8 # # [150 rows x 4 columns] # DMatrix を作成 dm = xgb.DMatrix(train, label=pd.Series(iris.target)) dm.feature_names # ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'] dm.feature_types # ['q', 'q', 'q', 'q']
DMatrix
に変換できるのは、列が int64
, float64
, bool
の DataFrame
のみ。他の型を自動でダミー変数に変換したりはしない。
df = pd.DataFrame([[1, 0.1, False], [2, 0.2, True], [2, 0.2, False]], columns=['A', 'B', 'C']) df # A B C # 0 1 0.1 False # 1 2 0.2 True # 2 2 0.2 False df.dtypes # A int64 # B float64 # C bool # dtype: object dm = xgb.DMatrix(df) dm.feature_names # ['A', 'B', 'C'] dm.feature_types # ['int', 'q', 'i'] # object 型を含むためエラー df = pd.DataFrame([[1, 0.1, 'x'], [2, 0.2, 'y'], [2, 0.2, 'z']], columns=['A', 'B', 'C']) df # A B C # 0 1 0.1 x # 1 2 0.2 y # 2 2 0.2 z df.dtypes # A int64 # B float64 # C object # dtype: object xgb.DMatrix(df) # ValueError: DataFrame.dtypes must be int, float or bool
3. xgb.cv
の結果を pd.DataFrame
として取得
クロスバリデーションを行う xgb.cv
はこれまで実行結果を文字列で返していたため、その結果をパースもしくは目視で確認する必要があった。この返り値を pd.DataFrame
もしくは np.ndarray
とし、プログラムで処理がしやすいようにした。
train = pd.DataFrame(iris.data, columns=['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']) dm = xgb.DMatrix(train, label=pd.Series(iris.target)) cv = xgb.cv(params, dm, num_boost_round=50, nfold=10) cv # test-mlogloss-mean test-mlogloss-std train-mlogloss-mean train-mlogloss-std # 0 0.753459 0.027033 0.737631 0.003818 # 1 0.552303 0.048738 0.526929 0.005102 # 2 0.423481 0.066469 0.390115 0.005873 # 3 0.339942 0.082163 0.295637 0.006148 # .. ... ... ... ... # 46 0.208001 0.259161 0.018070 0.001759 # 47 0.208355 0.261166 0.017898 0.001724 # 48 0.208468 0.261520 0.017755 0.001703 # 49 0.208566 0.260967 0.017617 0.001686 # # [50 rows x 4 columns] cv.sort(columns='test-mlogloss-mean') # test-mlogloss-mean test-mlogloss-std train-mlogloss-mean train-mlogloss-std # 11 0.175506 0.177823 0.058496 0.004715 # 13 0.175514 0.194641 0.045222 0.004117 # 14 0.176150 0.203265 0.040493 0.004050 # 12 0.176277 0.186902 0.051074 0.004423 # .. ... ... ... ... # 3 0.339942 0.082163 0.295637 0.006148 # 2 0.423481 0.066469 0.390115 0.005873 # 1 0.552303 0.048738 0.526929 0.005102 # 0 0.753459 0.027033 0.737631 0.003818 # # [50 rows x 4 columns]
pandas
がインストールされていない、もしくは as_pandas=False
を指定した場合、返り値は np.ndarray
となる。
cv = xgb.cv(params, dm, num_boost_round=50, nfold=10, as_pandas=False) cv # array([[ 0.7534586 , 0.02703308, 0.7376308 , 0.00381775], # ... # [ 0.2085664 , 0.26096721, 0.0176169 , 0.00168606]]) np.argmin(cv, axis=0) # array([11, 0, 49, 49])
まとめ
XGBoost と pandas
をより便利に使うため、以下 3 点の修正を行った。
DMatrix
でのラベルと型の指定pd.DataFrame
からのDMatrix
の作成xgb.cv
の結果をpd.DataFrame
として取得
これ使って Kaggle で大勝利したい...と思ったのですが、他の日本勢に使っていただいたほうが勝率高いはずなのでシェアします。