はじめに
RandomForestには、feature_importances_という、特徴量の重要度を返す機能があります。「どの列が予測に効いているか」を手軽に調べられるため、よく使われます。では、この重要度は、そのまま信用してもいいのでしょうか。
この記事では、予測に無関係なランダム列を混ぜて、次の3点を確かめます。
- 標準の重要度が、無関係な列を最重要と答えること
- その原因が、重要度の計算方法のどこにあるのか
- 信用できる重要度の調べ方
実験の設定
1000行のデータを作ります。ラベルが1になる確率は、次の2つの列が大きいほど高くなります。
- binary:0か1の二値の列(効く)
- level5:0~4の5段階の列(効く)
また、ラベルと無関係な列として、random_idを加えます。 0~1の乱数で、全行で値が違います。
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
from sklearn.model_selection import train_test_split
rng = np.random.default_rng(0)
n = 1000
b = rng.integers(0, 2, n) # 効く二値の列
c = rng.integers(0, 5, n) # 効く5段階の列
p = 0.15 + 0.3 * b + 0.08 * c # ラベルが1になる確率
y = (rng.uniform(size=n) < p).astype(int)
rnd = rng.uniform(size=n) # 無関係なID風の列
X = np.column_stack([b, c, rnd])
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0)
rf = RandomForestClassifier(n_estimators=300, random_state=0).fit(Xtr, ytr)
print(rf.feature_importances_) # 標準の重要度
pi = permutation_importance(rf, Xte, yte, n_repeats=20, random_state=0)
print(pi.importances_mean) # permutation importance
結果:無関係な列が、重要度の8割を占める
データの乱数を変えて、20回繰り返した平均です。
| 列 | 実際の関係 | 標準の重要度 | permutation importance |
| binary | 効く | 0.092 | +0.034 |
| level5 | 効く | 0.094 | +0.012 |
| random_id | 無関係 | 0.814 | -0.017 |
標準の重要度では、無関係なrandom_idが重要度の約8割を占め、20回すべてで1位でした。効いている2つの列は、合わせても2割に届きません。
一方、permutation importanceでは、random_idは20回のうち1位になった回がなく、平均は負の値になりました。効く列の方が、高い値になっています。
原因:訓練データで計算しているから
標準の重要度は、木が分割するたびに減った「不純度」(ラベルのばらつき)を、訓練データで合計したものです。
値の種類が多い列は、分割の候補が多くなります。random_idは全行で値が違うため、たまたまラベルをうまく切り分けられる分割点を、いくらでも見つけられます。木は深く成長するので、この偶然の分割も、そのまま学習します。この実験では、訓練データでの正解率が1.0になりました。
つまり、標準の重要度は、「訓練データにどれだけ当てはまったか」を測っています。予測に本当に効くかは測れていません。
この現象は、値の種類が多い列と、深く成長した木でおきやすいです。min_samples_leafを大きくするなどで軽くなる場合もあります。
対処法:テストデータで、シャッフルして測る
permutation importanceは、テストデータのある列の値をシャッフルして、予測の精度がどれだけ下がるかを測ります。無関係な列は、シャッフルしても精度が変わらないので、重要度がほぼ0になります。
もう一つ、無関係な乱数の列を1本混ぜておく方法があります。その列の重要度より低い特徴量は、信用できません。今回の実験では、permutation importanceで、効く2つの列がrandom_idより上に並びました。
まとめ
- 標準の重要度は、無関係なランダム列を、重要度の約8割、20回すべてで1位と答えた
- 原因は、訓練データで計算しているため。値の種類が多い列は偶然の分割で得をする
- permutation importanceでは、random_idが1位になる回はなかった。テストデータで測る方が、効く列を正しく並べられる
