【scikit-learn】RandomForestClassifierのパラメータからランダムフォレストを学ぶ

【scikit-learn】RandomForestClassifierのパラメータからランダムフォレストを学ぶ

ランダムフォレストの仕組みをscikit-learnのRandomForestClassifierのパラメータで学んでみました。

scikit-learnのRandomForestClassifierのデフォルトパラメータの値は下記の通りです。こちらから引用しています。

n_estimators : 構築する決定木の数

n_estimatorsは、構築する決定木の数を指定する。

ランダムフォレストは、訓練データに過剰適合しやすく、汎化性能が低くなりやすい決定木の欠点を補うため、複数の決定木を作成し、その結果の平均をとり、過剰適合を防ぐ。先輩エンジニアに聞いたところ、経験則では、変数の数×10くらいが設定目安とのこと。

criterion : 分割基準

決定木は「一番よく分割できる特徴量とその閾値をどのように選択するか?」が肝となる。criterionは、ランダムフォレストで構築する決定木のノード内のサンプルを2分割する際の分割基準を選択する。

分割基準は「'gini'(ジニ係数)」と「'entropy'(エントロピー)」の2つが指定できる。ジニ係数はノード内の不純度を表し、エントロピーはノード内の情報量の多さを表している。どちらもざっくり言うと、ノード内がどのくらいごちゃごちゃしているかを表し、値を最も減らすことのできる特徴量とその閾値の組み合わせを選択する。

ジニ係数とエントロピーの違いについては、パターン認識 第10章 決定木[入門]初心者の初心者による初心者のための決定木分析がわかりやすい。

ちなみに、決定木の代表的な手法には、ジニ係数を基準にノードを2分割していくCART法や、エントロピーを基準に多分割を許容するC4.5法がある。scikit-learnでは、最適化したCART法 (Classification and Regression Trees) を使用しているとのこと。

max_depth : 木の深さ

構築する木の深さの最大値を指定する。

決定木の過剰適合を防ぐには、ある条件で木の分割を止める「事前枝刈り」と、一旦決定木を全て構築した後に情報量の少ないノードを削除する「事後枝刈り」がある。scikit-learnでは「事前枝刈り」のみ設定できる。

min_samples_split : ノード分割するのに必要な最小サンプル数

ノードを分割する際に必要なサンプル数の最小値を指定する。「事前枝刈り」の方法の一つで、ノード内のサンプル数がこの値以下になった場合は、分割しない。

min_samples_leaf : 終端ノード(葉)内の最小サンプル数

終端ノード(葉)に必要な最小サンプル数。これも「事前枝刈り」の方法の一つで、終端ノード(葉)内のサンプル数がこの値未満にならないように、調整する。

指定方法は、最小サンプル数を「int型」で指定する方法と「float型」で全体に対する割合を指定する方法がある。

min_weight_fraction_leaf : 終端ノード(葉)内に必要な最小サンプル数率

min_samples_leafと同じく、終端ノード(葉)に必要な最小サンプル数を指定するが、min_weight_fraction_leafは全体に対する割合で指定する。「min_samples_leaf」と「min_weight_fraction_leaf * n_samples」のどちらか大きい方が終端ノード(葉)に必要な最小サンプル数として採用される。

サイトによって書いている情報が違うのと、min_samples_leafをfloat型で指定した場合と何が違うんだという話もあるため、使用する場合はより詳しく調べる必要がある。

(参考)scikit-learnのdiscussion

max_features : ノード分割する際にランダムで選択する特徴量数

ランダムフォレストでは、下記の手順で決定木を作成している。

① 構築する決定木ごとに、サンプル(データ)を復元抽出でランダムに選び、戻しを繰り返し、同じサンプル数(同じ行数)の新しいデータセットを作成する。(ブートストラップサンプリングと呼ばれる)

② ノードごとに、max_featuresで指定された数の特徴量をランダムに選択し、その中から「一番よく分割できる特徴量とその閾値」を調べる。

この時、max_featuresでは、各ノードを分割する際に使用する特徴量数を指定する。ただし、有効なノード分割方法が1つも見つからなかった場合は、指定した特徴量数以上の特徴を分割に使用する可能性がある。

指定の仕方は下記の通り。

  • int型(特徴量数そのものを指定)
  • float型(全体に対する割合で指定)
  • 'auto' (サンプル数の平方根)
  • 'sqrt' ('auto'と同じ)
  • 'log2' (2を底とするサンプル数の対数)
  • None (サンプル数をmax_featuresとする)

max_featuresが大きすぎると(サンプル数と近い値にすると)、全ての決定木が似た形になり、ランダムフォレストの効果(異なる決定木の平均をとることで過剰適合を防ぐ効果)が薄れてしまう。

逆に、max_featuresが小さすぎると、ノード分割方法の選択肢が少なくなり、木をかなり深くしないと、良い分類精度が得られない。例えば、max_features=1を指定すると、ノードごとに特徴量をランダム選択できるが、ある一つの特徴量に対する閾値操作のみでノードを分割していかなければならず、良い精度を得るには木を深くする必要があることが容易に想像できる。

max_leaf_nodes : 終端ノード(葉)の数の最大値

終端ノード(葉)の数を指定する。これも「事前枝刈り」の方法の一つで、終端ノード(葉)の数がこの値を超えないよう、ノードの分割(木の成長)を途中で止める。

min_impurity_decrease : ノード分割に必要な不純度減少量の最小値

ノード分割に必要な不純度の減少量の最小値を指定する。ノード分割する際にこの値以上に不純度が減少するように、ノードを分割する。

<不純度減少量の計算方法>

  • N : 全サンプル数
  • N_t : 現在のノード内のサンプル数
  • N_t_R : 右の子ノード内のサンプル数
  • N_t_L : 左の子ノード内のサンプル数
  • impurity : 現在のノード内の不純度
  • right_impurity : 右の子ノード内の不純度
  • left_impurity : 左の子ノード内の不純度

子ノードは分割先ノードを表し、不純度はcriterionで指定した方法(ジニ係数orエントロピー)で計算される値を表す。

min_impurity_split : ノード分割に必要な最小不純度数

現在のノードを分割するのに必要な最小不純度を指定する。これも「事前枝刈り」の一つで、現在のノードの不純度がこの値以上ならノード分割を続ける。逆にこの値以下であれば、ノード分割を止める。

bootstrap : ブートストラップサンプリングを実施するかどうか

ランダムフォレストでは、基本的に、構築する決定木ごとに、サンプルを復元抽出でランダムに選び、戻しを繰り返し、同じサンプル数(同じ行数)の新しいデータセットを作成する。これはブートストラップサンプリングと呼ばれる。これを実施せずに、全ての決定木に対して、そのまま同じサンプルを使用する場合、Falseを指定する。

ただ、Falseを指定した場合、決定木に使われるサンプルが同じものになり、特徴量選択の際のランダム性に頼ることになるため、過剰適合を防ぐランダムフォレストの効果が薄れてしまうことに注意する必要がある。

oob_score : 汎化性能を推定するためにOOBを使用するかどうか

ブートストラップサンプリングは復元抽出のため、構築する決定木ごとに、OOB (Out-of-Bag)と呼ばれる使用されないデータが全体のおよそ36%発生することになる。

【機械学習】OOB (Out-Of-Bag) とその比率

これを用いて、構築する決定木ごとにOOBに対する予測を行い、accuracyの平均値をoob_score_アトリビュートに返す。

n_jobs : 使用するCPUのコア数

fitとpredictメソッドに利用される同時実行ジョブ数(使用するCPUコア数)を指定する。n_jobs=-1だと全てのCPUコアが使用され、何も指定しないと、n_jobs=1となり、並行実行されない。

また、n_jobsに指定した値が、実行するPCのCPUコア数を超えると、PCが異常停止する原因となるので、注意が必要。特に、GridSearchCVを利用する際には、「GridSearchCVのn_jobs」×「RandomForestClassifierのn_jobs」が実行の際使用されるCPUコア数となり、どちらも"-1"を指定すると、異常停止する可能性が高い。

Pythonの scikit-learn の n_jobs の設定ミスによる動作不良

MacでのCPUコア数のCLIでの調べ方は下記の通り。

random_state : 乱数生成を制御するパラメータ(乱数を固定化する)

整数で指定する。Noneの場合は、numpy.randomが使用される。整数が指定された場合、下記二つのランダム性が制御され、複数回実行しても同じ結果が得られるようになる。

① bootstrap=Trueの場合、構築する決定木ごとにサンプルを復元抽出でランダムに選ぶ。この際のランダム性を制御する。

② max_features < n_featuresの場合、ノードで最適な分割を探すときに、max_featuresで指定された数の特徴量をランダムに選択する。この際のランダム性を制御する。

verbose : ログの出力をするかどうか

verbose=1の場合、経過時間とともに下記赤いログのみを取得する。verbose=2の場合、各ツリーの構築に関する残りの情報が出力される。

<ログ出力例>
building tree 1 of 500
building tree 2 of 500
...

[Parallel(n_jobs=-1)]: Done 1 out of 1 | elapsed: 16.7min finished

warm_start : 以前の結果を学習に再利用するかどうか

warm_start=Trueにすると、以前にトレーニングしたモデルの一部を保持し、新しいモデルの学習を高速化できる。異なるパラメーターを使用して同じデータで同じモデルを繰り返しトレーニングする場合、パラメーターを最適化するときに役立つ。例えば、最適なn_estimatorsの値が分からないとき、warm_start=Trueにして、徐々にn_estimatorsの数を増やしながら実行すると、小さな決定木の情報を使用して、大きな決定木をより迅速にトレーニングできる。

参考
https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html
https://pythondatascience.plavox.info/scikit-learn/scikit-learnで決定木分析
https://github.com/scikit-learn/scikit-learn/tree/master/sklearn