DSOC R&Dグループの中野です。
前回の記事では、XGBoostの単調性制約とProbability calibrationについて記事を書きましたが、ブースティングを使ってキャリブレーションをやることに関心がある日本語圏の人は少ないだろうと反省がありました。
少しでもリーチする人を増やすために、本家へのマージを目指して取り組んでいることを紹介します。
やったこと
前回の記事にもあるように、単調性制約は param['monotone_constraints'] = '(0,1,0,-1,0)'
といった具合にタプルを文字列にして設定してあげる必要があり、直感的に使いにくいと感じられます。Isotonic regression感覚で使えるキャリブレーション用クラスが便利なので、実装を試みました。
実装について
単調性制約をかければ、Isotonic regression相当に機能するので、数値計算の処理を書く必要はありません。パラメータを渡してやるだけです。
キャリブレーションは分類問題に対して行うので、 XGBClassifier
を継承することにします。このときにユーザーを煩わせることなく、単調性制約が設定されるようにしました。
class XGBCalibrator(XGBClassifier):
def __init__(self, max_depth=5, **kwargs):
kwargs_calibration = kwargs
kwargs_calibration['monotone_constraints'] = '(1)'
super(XGBCalibrator, self).__init__(max_depth, **kwargs_calibration)
これで目的は達成されたのですが、もう一工夫します。
fit
に特徴量として1次元配列を渡すと、XGBoostの内部的に利用するデータ構造がうまく作れないためにエラーが出てしまいます。また、キャリブレーション用であれば、ベースのモデルが出力した確率が1次元配列となることが多く、不便です。
1次元配列を渡してもエラーとならないようにしましょう。
def fit(self, X, y, sample_weight=None, eval_set=None, eval_metric=None,
early_stopping_rounds=None, verbose=True, xgb_model=None):
if len(X.shape) == 1:
# 1d array
X = np.reshape(X, (len(X), 1))
super(XGBCalibrator, self).fit(X, y, sample_weight, eval_set, eval_metric,
early_stopping_rounds, verbose, xgb_model)
return self
インプットを2次元配列に変換した後は親クラスと同じ処理になるので、 super()
を使って親クラスのメソッドを呼びます。
これで完成です。
今後の展望
現在は、Issueを立てて様子を伺っている状況です。キャリブレーションにブースティングを使ってみたい人が多いようであれば、PRを出してみたいです。
今回の開発は、チームメンバーにSlackで意見をもらって、Pythonの多重継承や sklearn.base.BaseEstimator
についての理解を再確認することができました。
OSS開発は自分にとっても得るものが大きく、またXGBoostをはじめとしたデータ分析周辺のライブラリーには日頃からお世話にもなっていますので、何かしらコミュニティーへ貢献できるように取り組みを続けていきたいです。
「浅いモデルはXGBoostで」
2週続けて同じネタになってしまいましたね。来週は、別の話題で書きたいと思います。
リンク
-
XGBoostに関するもの
- XGBoost
- Monotonic constraints (XGBoost)
執筆者プロフィール
text: DSOC R&Dグループ 中野良則