ブースティングの相対的な変数の重要性


33

Gradient Boosted Treesで相対的な変数の重要度がどのように計算されるかについての説明を探しています。

メジャーは、変数が分割用に選択された回数に基づいており、各分割の結果としてモデルに対する2乗改善によって重み付けされ、すべてのツリーで平均されます。[ Elith et al。2008年、回帰ツリーをブーストするためのワーキングガイド ]

そして、それは以下よりも抽象的ではありません:

Ij2^(T)=t=1J1it2^1(vt=j)

合計がJ末端ノードツリーTの非末端ノードに対するものであり、v tはノードtに関連付けられた分割変数であり、^ i 2 tは、定義された分割の結果としての二乗誤差の対応する経験的改善ですas i 2R lR r= w l w rtJTvttit2^ ¯ のY L ¯ Y Rはそれぞれ左右娘応答手段であり、WLWR重みの対応する和です。i2(Rl,Rr)=wlwrwl+wr(yl¯yr¯)2yl¯,yr¯wl,wr[フリードマン2001、グリーディ関数近似:勾配ブースティングマシン]

最後に、関連するセクション(10.13.1ページ367)が上記の2番目のリファレンス(説明される可能性がある)と非常に似ているため、統計学習の要素(Hastie et al。2008)がここで非常に役立つとは思いませんでしたフリードマンが本の共著者であるという事実によって)。

PS:相対変数の重要度の測定値は、gbm Rパッケージのsummary.gbmによって提供されることを知っています。ソースコードを調べようとしましたが、実際の計算がどこで行われているのか見つけることができないようです。

ブラウニーポイント:これらのプロットをRで取得する方法を知りたい


私は役に立つかもしれクラスで変数の重要度をプロットする方法についての関連質問への新しい答えを追加stackoverflow.com/a/51952918/3277050
see24

回答:


55

sklearnコードを使用しRます。これは一般にコードよりもずっときれいだからです。

GradientBoostingClassifierの feature_importancesプロパティの実装を次に示します(概念的なものを邪魔するコードの行をいくつか削除しました)

def feature_importances_(self):
    total_sum = np.zeros((self.n_features, ), dtype=np.float64)
    for stage in self.estimators_:
        stage_sum = sum(tree.feature_importances_
                        for tree in stage) / len(stage)
        total_sum += stage_sum

    importances = total_sum / len(self.estimators_)
    return importances

これは非常に理解しやすいです。 self.estimators_は、ブースター内の個々のツリーを含む配列なので、forループは個々のツリーを反復処理します。との接続が1つあります

stage_sum = sum(tree.feature_importances_
                for tree in stage) / len(stage)

これは、非バイナリ応答のケースを処理しています。ここでは、各段階で複数のツリーを1対すべての方法で適合させます。バイナリケースに焦点を当てるのが概念的に最も簡単なのは、合計に1つの被加数があり、これがちょうどである場合tree.feature_importances_です。したがって、バイナリの場合、これをすべて次のように書き換えることができます。

def feature_importances_(self):
    total_sum = np.zeros((self.n_features, ), dtype=np.float64)
    for tree in self.estimators_:
        total_sum += tree.feature_importances_ 
    importances = total_sum / len(self.estimators_)
    return importances

つまり、言葉で、個々のツリーの特徴の重要度を合計し、ツリーの総数で割ります。単一のツリーの機能の重要度を計算する方法はまだ残っています。

ツリーの重要度の計算は、cythonレベルで実装されますが、それでも引き続き追跡可能です。ここにコードのクリーンアップされたバージョンがあります

cpdef compute_feature_importances(self, normalize=True):
    """Computes the importance of each feature (aka variable)."""

    while node != end_node:
        if node.left_child != _TREE_LEAF:
            # ... and node.right_child != _TREE_LEAF:
            left = &nodes[node.left_child]
            right = &nodes[node.right_child]

            importance_data[node.feature] += (
                node.weighted_n_node_samples * node.impurity -
                left.weighted_n_node_samples * left.impurity -
                right.weighted_n_node_samples * right.impurity)
        node += 1

    importances /= nodes[0].weighted_n_node_samples

    return importances

これは非常に簡単です。ツリーのノードを反復処理します。リーフノードにいない限り、このノードでの分割からノード純度の重み付き減少を計算し、分割されたフィーチャに帰属させます

importance_data[node.feature] += (
    node.weighted_n_node_samples * node.impurity -
    left.weighted_n_node_samples * left.impurity -
    right.weighted_n_node_samples * right.impurity)

次に、完了したら、データの総重み(ほとんどの場合、観測値の数)ですべてを分割します。

importances /= nodes[0].weighted_n_node_samples

不純物は、ツリーを成長させるときに分割するものを決定するときに使用するメトリックの一般的な名前であることを思い出してください。その観点から、各フィーチャでの分割がどれだけツリー内のすべての分割で不純物を削減できるかを単純に要約しています。

勾配ブースティングのコンテキストでは、これらのツリーは常に損失ツリーの勾配に適合する回帰ツリー(貪欲に二乗誤差を最小化)です。


この非常に詳細な回答をありがとう。私はそれを受け入れる前に注意深くそれを通過する時間をさせてください。
アントワーヌ

4
さまざまな不純物基準を使用できるように見えますが、Giniインデックスはフリードマンが使用した基準ではありませんでした。私の質問と3番目のリンクの878行で述べたように、Friedman は改善スコア付きの平均二乗誤差不純性基準を使用しました。回答のこのセクションを更新できれば、それは素晴らしいことです。そして、はい、あなたは正しいです、それは重みが確かに観測の数であるようです。
アントワーヌ

3
または、GiniインデックスとFriedmanの元の基準の両方の部分を保持し、最初の分類を分類に使用し、2番目の分類を回帰に使用することを強調すると、答えがさらに良くなりますか?
アントワーヌ

アントワーヌ、このアップデートをありがとう。平均二乗誤差が回帰木に使用される改善基準であることを知ることは本当に役立ちます。それがどのように分類に使用されるかは明らかではありませんでした。ただし、分類の勾配ブースティングでも、分類ツリーとは対照的に、回帰ツリーはまだ使用されていると思います。少なくともpythonでは、各ブースティング段階で現在のエラーに対して回帰分析が行われています。
かなりオタク

皆さんは回帰木について正しいです。
マシュードゥルーリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.