St_Hakky’s blog

Data Science / Human Resources / Web Applicationについて書きます

Bagging(バギング)について調べたのでまとめた:Out-of-Bag(OOB) / Random Forest / Decision Jungles / Deep Forest(gcForest)

こんにちは。

Kaggleをやるにあたって(というかふつうに勉強したかったのもある)、アンサンブル学習の方法は勉強しておく必要があるようなーと思って、勉強してみました。

他のブースティングやスタッキング、アンサンブル学習全般については以下の記事をどうぞ。

st-hakky.hatenablog.com


それでは、調べた内容についてまとめていきたいと思います。

◯Bagging(バギング)とは

Bagging(バギング)は、bootstrap aggregatingの略です。名前から分かる通り、各学習器に使う学習用データをブースストラップサンプリングによって得て、その学習した学習器を予測に用いて最後アンサンブルするという方法になります。

あんまり区別がされている資料を見かけないんですが、これとよく似た方法にPastingというものもあります。Pastingは、重複有りのランダムなbootstrapとは違い、重複なしでデータをサンプリングして、そのサンプリングしたデータを用いて学習します。

Pastingに比べて、独立な学習をより行えるBaggingのほうがより良い精度を出すことが一般的に言われていますが、実際は交差検証などを行って確認するべきです。

○Hard VotingとSoft Voting

分類タスクの時に、各学習器が予測した結果からどのように最終的な結果を得るか、といった時大きく分けて2つの考え方があります。

一つが、Hard Voting。多数決によって最終的な出力を決める方法です。

もう一つが、Soft Votingで、これは各学習機の中で最も確率を高く予測した結果を用いるという方法です。この方法の性質上、Soft Votingを使う際は、すべての弱学習機がラベルだけではなく確率も出力できる必要があります。

scikit-learnでは、各弱学習器が確率を出力できる分類器であった場合はデフォルトでsoft votingになるようです。便利ですね。

○Out-of-Bag(OOB)

こちらの本を読んでいたら、ブートストラップによって選ばれなかったサンプルは36%くらい存在するようになり、そのデータを使ってテストをすることが可能で、これにより訓練用とテスト用であらかじめデータ・セットを分ける必要がないと書かれていました。

この本は割りと数式が出てこないので、ぱっと読んだ時に「どこからでてきたねんこの36%」と思いました笑。

なので、ちょっと試してみることにしました。

重複有りで、1~100の数字の中からランダムに数字を100個選び、その中で選ばれなかった数字の個数をカウントすることを1000回繰り返して、平均を取ってみます。言語はpythonです。

import numpy as np
result = 0
for _ in range(0, 1000):
  result += len(set(np.arange(1, 101)) - set(np.random.randint(1, 101, 100)))
print(result / 1000)

乱数を生成してやっているので、毎回結果がかわりますが、大体36.7くらいでした。

なんでやねんやろーって思って、記事を探していたら、こちらの記事をみつけました。私がここで何かを言うまでも無いくらいいい記事だったので、こちらを御覧ください。笑

qiita.com

ネイピア数のところの議論まで読むと、確かになるほど~となりました。

○Random Patches and Random Subspaces

これまではデータセットのサンプルをランダムに抽出することばかりはなしていましたが、特徴量(変数)の方もランダムにサンプリングしようというお話です。

Random Patchesというのが、特徴量もデータセットもそれぞれランダムにサンプリングすることをさし、Random Subspacesというのが、特徴量のみをランダムにサンプリングし、データセットはそのまま使用することを指します。

このようにランダムに特徴量を抽出することで、更に多様な学習器の種類が実現でき、アンサンブル学習がより効果的になることが期待できます。

○ランダムフォレスト(Random Forest)

弱学習器として決定木を用い、バギングしたものが、ランダムフォレストになります。

学習の流れとしては、以下の通り。すごくシンプル。

1. T個の決定木それぞれについて、以下を行う
  i. 学習データ集合からサンプリング(ブートストラップによるサンプリング)
  ii. サンプリングしたデータを用いて決定木の学習
2. すべての決定木の学習が終わったらランダムフォレストの完成

決定木の学習さえわかっていれば(わからない場合はこちらが参考になる)、上のやつはそれをサンプリングしたデータセットを複数個用意して、それぞれ学習させるだけでいいので、理解は難しくない。また、ランダムフォレストは並列処理が可能な構造になっているので、処理も高速になり、また決定木の数が大きくなってもスケールする。

■Feature Importance

一つ一つの決定木を見た時に、根ノードに近い変数ほど重要な変数であり、葉ノードに近いものが重要でない変数である可能性が高い。この性質を利用して、ランダムフォレスト全体で見た時に、各変数が各決定木においてどの程度の深さにあるかの平均値を見ることで、その変数の重要度を見ることができます。

scikit-learnではこれを自動で計算してくれているので、簡単に重要度を取得できる。ランダムフォレストを用いるとこういう情報を取得することができます。

○Deep Forest(gcForest)

ここでうだうだ解説するのもおこがましいくらい良い記事があるので、そっちをみてもらうのが良いかと。

とはいえ、簡単に説明。

最近Deep Learningは流行っていますが、gcForestはそのDeep learningと五角以上の性能を持つ決定木アンサンブル手法です。

Deep learningを自前で組むと嫌というほどわかりますが、どこをどうやってハイパーパラメータ-チューニングすれば良いのかみたいなのが本当にやっかいです。

そんな中で、このgcForestは異なるドメインのデータで適用された場合でも、ほとんど同じハイパーパラメータの設定で優れた性能を出す(らしい)。

また、当然アンサンブルな手法なのでアンサンブル手法特有のメリットである、分散学習が可能な点もあります。そして、決定木の性質もあるので、Deep Learningと違って解釈が容易な点や少ないデータセットでも学習が可能な点もあります。

構造とか色々説明し始めるとそれだけで一つの記事になりそうなので、割愛します笑(個人的には実験結果が若干フェアじゃないんじゃないかな疑惑が、、、笑)。

実装に関しては、Rで実装した人とか、Pythonで実装した人とかがいるらしく、これも参考になるかと思います。