GBDT系の機械学習モデルであるXGBoost, LightGBM, CatBoostを動かしてみる。


# 目的
本ページの目的はGBDT(Gradient Boosting Decision Tree)の代表的な機械学習モデルを利用可能にする。

# 内容
- 本ページで扱う機械学習モデルはXGBoost, LightGBM, CatBoostとする。
- GBDT系の機械学習モデルの大元論文を整理し、大元のサイトを整理する。
- 動かしてみるが目的なので、詳細な解説はせず、重要なパラメーターに対しては恣意的に選んだ数字をおいた

    
# 本ページを読み終えてできるようになること
- GBDTの代表的な手法を自身の環境で動かせるようになる
- GBDTの主要研究の背景に触れる


# 背景
- 本ページで扱う機械学習モデルの学術的な背景
    - XGBoost (eXtreme Gradient Boosting) は[Chen et al., 2016, Proceedings of the 22nd acm sigkdd international conference on knowledge discovery and data mining](https://dl.acm.org/citation.cfm?id=2939785)に掲載された。
        - Kaggle でよく利用されているGBDT(Gradient Boosting Decision Tree)の一種(
        - 結構早くてスパースデータに対して良い？
        - データサイズが大き場合、問題点(efficiency と scalability)があるらしい
    - LightGBM は [Ke et al., 2017, NIPS](https://papers.nips.cc/paper/6907-lightgbm-a-highly-efficient-gradient-boosting-decision-tree)に掲載された。
        - 従来のGBDTのトレーニングデータに対する学習速度を最大で20倍までスピードアップできるらしい。
    - CatBoost [Prokhorenkova et al., 2018, NeurIPS](https://nips.cc/Conferences/2018/Schedule?showEvent=11641)
        - 様々なデータセットに対して、他のブースティングよりも優れているといわれている。
            - 研究会では怪しいほど性能が高くなるといわれていた
        - 特にサンプル数が中から小のデータセットでかつカテゴリカルデータに対して優れているらしい。
        - Ordered Target Statistics という戦略を利用することで 40,000 以下のデータに良いらしい。
- そもそも Gradient boosting って何？
    - 様々な実用的なタスク(データ)に対して、高水準の結果を示すことが可能な強力な機械学習技術
    - 勾配降下によりアンサンブルを行う

# インストール
- xgboost の準備: Mac OS の場合 (Windows は[こちら](https://xgboost.readthedocs.io/en/latest/build.html)を参照)
	- brew install gcc@7
		- Error: Xcode alone is not sufficient on High Sierra. が出た場合
			- xcode-select --install で回避
	- pip install xgboost
- lightgbm の準備: Mac OS の場合([参考](https://github.com/Microsoft/LightGBM/tree/master/python-package) 
	- brew install libomp
	- pip install lightgbm
- Catboost の準備: Mac OS の場合([参照](https://tech.yandex.com/catboost/doc/dg/installation/python-installation-method-conda-install-docpage/)
	- pip install catboost または conda install catboost のいずれかを実行

# 実験
## データの読み込み
- 重要変数
    - tata_setは機械学習の用語である特徴量（もしくは特徴変数) を表す
    - target_setは機械学習の用語であるクラス (分類対象)を表す
- データ (Kaggle)
    - Predict FIFA 2018 Man of the Match Match statistics with which team player has won Man of the match https://www.kaggle.com/mathan/fifa-2018-match-statistics
    - 列名Man of the Matchには、いわゆる MVPの選手がいるかどうかを判定する2値が含まれている。このデータを分類対象(クラス)とする。


```python
import numpy as np
import pandas as pd
#from sklearn.model_selection import train_test_split
#from sklearn.tree import DecisionTreeClassifier
#from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_validate, KFold
# CatBoostを利用した分類
from catboost import CatBoostClassifier
import lightgbm as lgb
import xgboost as xgb
#from sklearn.model_selection import train_test_split,  GridSearchCV
# 指標を計算するため
from sklearn.metrics import accuracy_score, cohen_kappa_score, make_scorer, f1_score, recall_score
# 見た目を綺麗にするもの
#import matplotlib.pyplot as plt
#import seaborn as sns
import pprint, pydotplus
# 保存
from sklearn.externals.joblib import dump
from sklearn.externals.joblib import load

data = pd.read_csv("FIFA_data.csv")
pd.set_option('display.max_rows', 10)
print(data.columns)
feature_names = [i for i in data.columns if data[i].dtype in [np.int64]]
target_set = data['Man of the Match'] 
target_set = target_set.replace('Yes', 1)
target_set = target_set.replace('No', 0)
print(target_set,type(target_set))
print(feature_names)
data_set = data[feature_names]
```

    Index(['Date', 'Team', 'Opponent', 'Goal Scored', 'Ball Possession %',
           'Attempts', 'On-Target', 'Off-Target', 'Blocked', 'Corners', 'Offsides',
           'Free Kicks', 'Saves', 'Pass Accuracy %', 'Passes',
           'Distance Covered (Kms)', 'Fouls Committed', 'Yellow Card',
           'Yellow & Red', 'Red', 'Man of the Match', '1st Goal', 'Round', 'PSO',
           'Goals in PSO', 'Own goals', 'Own goal Time'],
          dtype='object')
    0      1
    1      0
    2      0
    3      1
    4      0
          ..
    123    0
    124    1
    125    0
    126    1
    127    0
    Name: Man of the Match, Length: 128, dtype: int64 <class 'pandas.core.series.Series'>
    ['Goal Scored', 'Ball Possession %', 'Attempts', 'On-Target', 'Off-Target', 'Blocked', 'Corners', 'Offsides', 'Free Kicks', 'Saves', 'Pass Accuracy %', 'Passes', 'Distance Covered (Kms)', 'Fouls Committed', 'Yellow Card', 'Yellow & Red', 'Red', 'Goals in PSO']



## 共通準備
- 評価指標: Accuracy と Kappa 係数を利用する。
- 交差検証: Ten-fold-stratified-cross-validation
 - これらが不明な場合は[Cross-validation: KFold と StratifiledKFold の性能の違い](https://qiita.com/KROYO/items/66d613356e2bf4cf9fed)を参照


```python
scoring = {'accuracy': make_scorer(accuracy_score),
           'kappa': make_scorer(cohen_kappa_score)
          }
skf = StratifiedKFold(n_splits=10,
                      shuffle=True,
                      random_state=0)
```

## XGBoost
XGBoost を動かしてみる。


```python
%%time
model = xgb.XGBClassifier(max_depth = 50,
                           learning_rate = 0.16,
                           min_child_weight = 1,
                           n_estimators = 200)
scores_xgb = cross_validate(model, 
                        data_set,   # 
                        target_set,
                        cv=skf, 
                        n_jobs = -1,
                        scoring=scoring)
pprint.pprint("XGBoost:{}".format(scores_xgb))
pprint.pprint("XGBoost_accuracy:{}".format(scores_xgb["test_accuracy"].mean()))
pprint.pprint("XGBoost_kappa:{}".format(scores_xgb["test_kappa"].mean()))
```

    ("XGBoost:{'fit_time': array([0.07296681, 0.07270503, 0.06822896, 0.08056283, "
     '0.06243491,\n'
     '       0.06437612, 0.080024  , 0.07008386, 0.08049893, 0.09190106]), '
     "'score_time': array([0.00357699, 0.0035851 , 0.00481105, 0.00362611, "
     '0.0033021 ,\n'
     '       0.00332212, 0.00316381, 0.00327396, 0.00260711, 0.00225186]), '
     "'test_accuracy': array([0.57142857, 0.71428571, 0.64285714, 0.85714286, "
     '0.83333333,\n'
     '       0.5       , 0.5       , 0.91666667, 0.33333333, 0.58333333]), '
     "'train_accuracy': array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]), "
     "'test_kappa': array([ 0.14285714,  0.42857143,  0.28571429,  0.71428571,  "
     '0.66666667,\n'
     '        0.        ,  0.        ,  0.83333333, -0.33333333,  0.16666667]), '
     "'train_kappa': array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])}")
    'XGBoost_accuracy:0.6452380952380952'
    'XGBoost_kappa:0.29047619047619044'
    CPU times: user 64.7 ms, sys: 44.8 ms, total: 110 ms
    Wall time: 2.03 s


## LightGBM
LightGBMを動かしてみる。


```python
%%time
model = lgb.LGBMClassifier(num_leaves = 31,
                           num_trees = 100,
                           objective = 'binary',
                           metric = 'binary_logloss',
                           silent=False)

scores_lgb = cross_validate(model, 
                        data_set,   # 
                        target_set,
                        cv=skf, 
                        n_jobs = -1,
                        scoring=scoring)

```

    CPU times: user 34.1 ms, sys: 3.33 ms, total: 37.4 ms
    Wall time: 283 ms



```python
pprint.pprint("LightGBM:{}".format(scores_lgb))
pprint.pprint("LightGBM_accuracy:{}".format(scores_lgb["test_accuracy"].mean()))
pprint.pprint("LightGBM_kappa:{}".format(scores_lgb["test_kappa"].mean()))
```

    ("LightGBM:{'fit_time': array([0.05086493, 0.04860687, 0.04421997, 0.04607511, "
     '0.0268271 ,\n'
     '       0.0277791 , 0.02678585, 0.02445316, 0.01633024, 0.02036715]), '
     "'score_time': array([0.02009106, 0.01956511, 0.02327609, 0.02541184, "
     '0.01577282,\n'
     '       0.01657176, 0.01604891, 0.01570582, 0.00908589, 0.00763202]), '
     "'test_accuracy': array([0.57142857, 0.57142857, 0.57142857, 0.78571429, "
     '0.75      ,\n'
     '       0.66666667, 0.41666667, 0.91666667, 0.33333333, 0.66666667]), '
     "'train_accuracy': array([0.94736842, 0.96491228, 0.93859649, 0.94736842, "
     '0.94827586,\n'
     '       0.96551724, 0.96551724, 0.94827586, 0.97413793, 0.95689655]), '
     "'test_kappa': array([ 0.14285714,  0.14285714,  0.14285714,  0.57142857,  "
     '0.5       ,\n'
     '        0.33333333, -0.16666667,  0.83333333, -0.33333333,  0.33333333]), '
     "'train_kappa': array([0.89473684, 0.92982456, 0.87719298, 0.89473684, "
     '0.89655172,\n'
     '       0.93103448, 0.93103448, 0.89655172, 0.94827586, 0.9137931 ])}')
    'LightGBM_accuracy:0.625'
    'LightGBM_kappa:0.25000000000000006'


## CatBoost
CatBoost を利用するためには CatBoostClassifier 関数を利用する。


```python
%%time
# Catboost のモデルの準備
model = CatBoostClassifier(iterations=500, 
                           eval_metric = 'Kappa',
                           learning_rate=0.01, 
                           l2_leaf_reg = 9,
                           depth=10,
                           one_hot_max_size = 50,
                           loss_function='Logloss')

scores_cat = cross_validate(model, 
                        data_set,   # 
                        target_set,
                        cv=skf, 
                        n_jobs = -1,
                        scoring=scoring)
pprint.pprint("CatBoost:{}".format(scores_cat))
pprint.pprint("CatBoost_accuracy:{}".format(scores_cat["test_accuracy"].mean()))
pprint.pprint("CatBoost_kappa:{}".format(scores_cat["test_kappa"].mean()))
```

    ("CatBoost:{'fit_time': array([33.30261898, 33.46484399, 33.24761176, "
     '33.5051899 , 37.29434705,\n'
     '       38.02754211, 37.78509092, 38.18597794, 21.43462086, 19.71722698]), '
     "'score_time': array([0.01334691, 0.01270199, 0.01476884, 0.00987601, "
     '0.01335907,\n'
     '       0.01004386, 0.01510406, 0.01020694, 0.00449705, 0.00260401]), '
     "'test_accuracy': array([0.5       , 0.78571429, 0.78571429, 0.92857143, "
     '0.83333333,\n'
     '       0.66666667, 0.66666667, 0.75      , 0.5       , 0.66666667]), '
     "'train_accuracy': array([0.95614035, 0.94736842, 0.93859649, 0.93859649, "
     '0.95689655,\n'
     '       0.93965517, 0.93103448, 0.93965517, 0.94827586, 0.92241379]), '
     "'test_kappa': array([0.        , 0.57142857, 0.57142857, 0.85714286, "
     '0.66666667,\n'
     '       0.33333333, 0.33333333, 0.5       , 0.        , 0.33333333]), '
     "'train_kappa': array([0.9122807 , 0.89473684, 0.87719298, 0.87719298, "
     '0.9137931 ,\n'
     '       0.87931034, 0.86206897, 0.87931034, 0.89655172, 0.84482759])}')
    'CatBoost_accuracy:0.7083333333333333'
    'CatBoost_kappa:0.4166666666666667'
    CPU times: user 56.7 ms, sys: 34.1 ms, total: 90.7 ms
    Wall time: 1min 33s


## 結果
- 正しい設定で調査をしていないが、
    - 予測精度は CatBoostが最も良い。
    - しかし、CatBoostの計算時間は1分30秒でありLightGBMの283msと比較すると果てしなく長い。


```python
pprint.pprint("XGBoost_accuracy:{}".format(scores_xgb["test_accuracy"].mean()))
pprint.pprint("XGBoost_kappa:{}".format(scores_xgb["test_kappa"].mean()))
pprint.pprint("LightGBM_accuracy:{}".format(scores_lgb["test_accuracy"].mean()))
pprint.pprint("LightGBM_kappa:{}".format(scores_lgb["test_kappa"].mean()))
pprint.pprint("CatBoost_accuracy:{}".format(scores_cat["test_accuracy"].mean()))
pprint.pprint("CatBoost_kappa:{}".format(scores_cat["test_kappa"].mean()))
```

    'XGBoost_accuracy:0.6452380952380952'
    'XGBoost_kappa:0.29047619047619044'
    'LightGBM_accuracy:0.625'
    'LightGBM_kappa:0.25000000000000006'
    'CatBoost_accuracy:0.7083333333333333'
    'CatBoost_kappa:0.4166666666666667'