Stacking - テンプレート化編・計算編② -

# 1. はじめに
主に3点をまとめてみます。Stackingとはなんぞや、Stackingが何故嬉しいのかなどは[Stacking - 計算編① -](http://goo.gl/0m9oMA "Stacking - 計算編① -")を参照してください。
**1点目はStackingやBlendingをテンプレート化**しましたという話です。また、中間出力を可視化出来る様にしました。ただし、フレームワーク自体は https://github.com/AlexInTown/Santander を参照させて頂きました。**2点目は、1-LevelにGBDT+線形分類器の組み合わせを新たなに追加**し、特に線形分類器の損失関数を変えた場合の性能寄与を調べました。フレームワークを真似るだけだと芸がないので・・・。GBDT+線形分類器については http://goo.gl/1UmhsI を参照下さい。実装はsklearnを使いました。**3点目はテンプレートを使って計算**しましたという話です。実際の計算で使った設計は以下になります；
![stacking.png](https://qiita-image-store.s3.amazonaws.com/0/96204/8547cd21-af42-f29e-68e8-c1525360a66e.png)
また、性能はLRをベースラインとした場合、accuracyが約11%改善しました。

# 2. ねらい
Stacking・Blendingをする動機は**数%の改善をする方法論**が中心ですが、各線形分類器がもつ**損失関数がデータに適しているかどうか**も重要だと今回わかりました。このあたりを考察にてまとめます。

# 3. データ
curl -o adult.data.csv https://raw.githubusercontent.com/bluekingsong/simple-gbdt/master/data/adult.data.csv
(ダウンロード後、adult.data.csvをtrain・testセットに分割してください。)

# 4. StackingやBlendingをテンプレート化
StackingやBlendingをコーディングする際、「やばい、コード・ファイルが多くなって管理しにくい・・・」という状況は経験があるのではないでしょうか。これは、各レベルで複数の分類器を使用している事、何よりパラメータ管理、Meta Feature管理の複雑さに起因します。参照させて頂いた[AlexInTown](https://goo.gl/kqgg4f "AlexInTown")さんも、管理に苦心している様子が伺えます。
私の方針としては、

- 簡単に再利用できるようにしたい(何回も似たようなスクリプトを書きたくない)
- StackingとBlendingを別フォルダで管理する
- 分類器・パラメータチューニングを呼び出すラッパーはStackingとBlendingの親ノードに配置する
- Stacking・Blendingの各ノードに追加したい分類器を自由に追加できるようにする

という事にしました。
また、例えば1-LevelでGBDTを使う場合、Feature Importance(Weight, etc)をグラフとしてみたいという願望は当然ですので、**各モデルに関する可視化機能も追加しました。これにより、ずいぶん振り返り易くなった**と感じています。

# 5. 1-LevelにGBDT+線形分類器の組み合わせを新たなに追加
これは[Gradient Boostingについて - Scikit-Learnを使ったfeature transformation(GBDT + LR vs LR) -](http://goo.gl/nIot4c "Gradient Boostingについて - Scikit-Learnを使ったfeature transformation(GBDT + LR vs LR) -")で紹介した通りの方法をテンプレートに追加したという話です。

# 6. テンプレートを使って計算
## 6.1 タスク
featureが14個与えられたとき、incomeが50Kより大きいかどうかを当てる問題です。大きい場合が1、そうでない場合は0の2値予測となります。
以下、一部データ

| age | workclass        | fnlwgt | education | education-num |
|-----|------------------|--------|-----------|---------------|
| 39  | State-gov        | 77516  | Bachelors | 13            |
| 50  | Self-emp-not-inc | 83311  | Bachelors | 13            |

## 6.2 前処理
本来は外れ値の処理をすべきですがやっていません。やったことは、

- カテゴリ変数をone-hot-encoding
- fnlwgt, capital-gain, capital-lossに関してはlog(X+1)とした

の二つです。

# 7. 結果
|           | accuracy | precision | recall   | f-value  |
|-----------|----------|-----------|----------|----------|
| Stacking  | 8.68E-01** | 9.40E-01  | 8.92E-01** | 9.15E-01** |
| GBDR + LR | 8.55E-01 | 9.23E-01  | 8.90E-01 | 9.06E-01 |
| LR        | 7.58E-01 | 9.72E-01**  | 7.70E-01 | 8.59E-01 |

Stackingすげーという結果。では細かく振り返ってみましょう
## 7.1 2-Level
![blending_ridge.png](https://qiita-image-store.s3.amazonaws.com/0/96204/6c85a815-7473-cc3e-c82d-23dc9e36c83e.png)
まずは、2-Levelを振り返ってみます。ここでは、各モデルの予測確率をMetaFeatureとし、RidgeClassificationで予測をしています。
グラフの左がRegularization Path(正則化のベストは10)、右がsklearnのDecision_FunctionをPDF・CDFプロットしたものです。Decision_Functionはゼロに近い程良い予測ができていることを意味します。
Regularization Pathをよく見ると、
GBDT+線形分類器 > Xgboost > Random Forest > k-Nearest Neighbor
の順に予測に寄与しているようです。具体的には

| intercept      | meta_knn         | meta_rf        | meta_xgb       | meta_gbdt_linear_LR-L1 | meta_gbdt_linear_LR-L2 | meta_gbdt_linear_SVM-L1 | meta_gbdt_linear_SVM-L2 |
|----------------|------------------|----------------|----------------|------------------------|------------------------|-------------------------|-------------------------|
| -1.10719170205 | 0.00912243308781 | 0.115396174014 | 0.447040687379 | 0.412511013869         | 0.828135676159         | 0.110595761196          | 0.280053097773          |

となっています。で、線形分類器間の違いは、損失関数と正則化項です。損失関数はLogistic-Loss or Hinge Loss、正則化はL1 or L2です。
で、今回のデータセットでは、明らかにLogistic-Lossの方が予測に寄与する割合が高いです。つまり、**損失関数の違いが予測能力に影響している**といえそうです。
あとは、**切片も意外に重要**です。

## 7.2 1-Level
さて、2-Levelで
GBDT+線形分類器 > Xgboost > Random Forest
となっていたわけですが、そもそもGBDT・Xgboost・Random ForestでのFeature Importanceってどうなっているのかという確認をしてみましょう。

### 7.2.1 : GBDT(+ 線形分類器)
![sk-gbdt_linear_upper-grid.png](https://qiita-image-store.s3.amazonaws.com/0/96204/5cc72269-d2eb-6313-eec3-163feb7c37d7.png)

左がFeature Importanceで右が損失です(ちなみに、GBDTはfeatureを非線形変換させる器として使用しています。詳しくは[Gradient Boostingについて - Scikit-Learnを使ったfeature transformation(GBDT + LR vs LR) -](http://qiita.com/Quasi-quant2010/items/a30980bd650deff509b4 "Gradient Boostingについて - Scikit-Learnを使ったfeature transformation(GBDT + LR vs LR) -")を参照)。
年収を説明する変数は年齢・投資損失・労働時間/週・投資利益・教育年数(fnlwgtが理解できていません。ググるとSampling Weightだと書いてあるのですが、ちょっとわかっていません)など、とても直観的な変数寄与度です。
**GBDT + 線形分類器にした理由は上記の変数を組み合わせた非線形性を考慮したかったから**です。例えば、(労働時間/週, 投資利益)の二つを組み合わせた時、労働時間は短いが、投資利益が高い人は年収が高い傾向にあるというのは容易に想像できますよね。
余談ですが、capital-lossが重要なfeatureなのは勉強になりますね。何かを得るためには常に手を出しつつけろ、というメッセージでしょうか(笑

### 7.2.2 : Xgboost
![sk-xgb-grid.png](https://qiita-image-store.s3.amazonaws.com/0/96204/23ae3675-535f-ccdf-3c93-0c24b611f1fd.png)

GBDTと基本的には変わりませんので、まあ大体同じ結果。Xgboostについては、[Gradient Boostingについて - 正則化編・Xgboost -](http://goo.gl/jPMVES "Gradient Boostingについて - 正則化編・Xgboost -")を参照。

### 7.2.3 : Random Forest
![sk-rf-grid.png](https://qiita-image-store.s3.amazonaws.com/0/96204/4e0b19bf-8cc6-6e38-ad49-d7f0c81a20ec.png)

左がFeature Importance、右がsklearnのDecision_FunctionをPDF・CDFプロットしたものです。
Gradient Boosting系とは違い、夫・独身などが重要な要素として抽出されています。このあたりが同じアンサンブル学習器ではあるけれども、異なるモデルの結果です。ちなみに、Random Forestは勉強したことがないので、コメントできません・・・。
ただ、モデルのアンサンブル学習をする上では、性質が異なるモデルを足し上げることも重要で、最終結果に寄与するか否かを確認できる(7.1 2-Levelのグラフのように)ので、まあ有名どころは使えばよいのではないでしょうか。

# 8. まとめ
内容としては、

- StackingやBlendingをテンプレート化(コードです。https://goo.gl/x2qc0X)
- 1-LevelにGBDT+線形分類器の組み合わせを新たなに追加
- テンプレートを使って計算

をまとめました。
特にテンプレート化できたことで、再利用しやすくなった事が良かったなと思います。
また、中間出力を可視化することで、振り返りがし易くなりました。振り返る場合は2-Level -> 1-Level -> 0-Levelの順に振り返る方が良いでしょう。どのモデルが悪かったのか、モデルのどの部分が悪かったのか、順に粒度を下げて振り返りが出来るからです。
ということでKaggleに突撃したいと思っているのですが、その前にCoupon PurchaseとOttoで勉強したいと思います。