Xgboost: XGBoostは、マルチクラス分類問題でクラス確率をどのように生成しますか?

作成日 2016年11月08日  ·  15コメント  ·  ソース: dmlc/xgboost

トレーニング済みのXGBoostツリーをダンプしようとすると、次の形式で取得されます。

0:[f37<39811] 
    1:[f52<199.5] 
        3:leaf=-0.021461
        4:[f2<0.00617284] 
            9:leaf=-0.0118755
            10:[f35<-83.548] 
                19:[f13<0.693844] 
                    23:[f37<1831]
                        29:leaf=-0
                        30:leaf=0.123949
                    24:leaf=0.0108628
                20:[f35<-74.6198] 
                    25:leaf=-0.0175897
                    26:leaf=0.051898
    2:leaf=-0.0239901

ツリー構造が何であるかは明らかですが、葉の値をどのように解釈するかは明確ではありません。 _バイナリ_分類問題と対数損失コスト関数の場合、リーフ値はロジスティック関数1 /(1 + exp(value))を使用してクラス確率に変換できます。 ただし、 _multiclass_分類問題の場合、それらの値がどのクラスに属するかについての情報はなく、その情報がないと、クラス確率を計算する方法が明確になりません。

何か案は? または、訓練された木からそれらの情報を取得する他の機能はありますか?

最も参考になるコメント

それは私が前に気づかなかった非常に良い観察です。 確かに、モデル内のツリーの数はn_estimator xn_classです。 ただし、木の順序を理解するために、次のおもちゃの例を使用しました。

x = np.concatenate([np.ones([10,1])*np.array([1,0,0]),np.ones([10,1])*np.array([0,1,0]),np.ones([10,1])*np.array([0,0,1])], axis=0)
y = np.array([['a']*10+['c']*10+['b']*10]).reshape([30,1])
model = xgb.XGBClassifier(n_estimators=2, objective='mlogloss').fit(x, y)
model.booster().dump_model('trees.txt')

これは基本的に、[1,0,0]が「a」にマップされ、[0,1,0]が「c」にマップされ、[0,0,1]が「b」にマップされるトレーニングデータセットを作成します。 ツリーをダンプする前に、xgboostがラベルに基づいてクラスをソートするかどうかを確認するために、意図的に「b」と「c」の順序を切り替えました。

結果のダンプされたモデルは次のとおりです。

booster[0]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[1]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[2]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[3]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[4]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[5]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941

そしてここに結論があります:

  1. おっしゃるように、
  2. ツリーが使用する機能の順序を見ると、最初のツリーは最初のクラスに属し、2番目のツリーは2番目のクラスに属し、以下同様に最後のクラスに属しているように見えます。 次に、すべての推定量がカバーされるまで同じパターンが繰り返されます。
  3. クラスの順序は、 numpy.unique()が返すものと一致しているようです。ここではアルファベット順であるため、最初のツリーはf0を使用し、2番目のツリーはf2を使用しています。

これらの情報がxgboostのドキュメントに追加されていると便利です。

全てのコメント15件

私も同じ質問があります。 私が気付いたのは、20のクラスがあり、推定量の数を100に設定すると、モデルに20 * 100 = 2000の木が印刷されるということだけです。 今、私の推測では、最初の100人の推定量がファーストクラスと他のクラスを分類しています。 次の100の推定量は、2番目のクラスと他のクラスなどを分類します。

確認できませんが、こんな感じかもしれませんね。

それは私が前に気づかなかった非常に良い観察です。 確かに、モデル内のツリーの数はn_estimator xn_classです。 ただし、木の順序を理解するために、次のおもちゃの例を使用しました。

x = np.concatenate([np.ones([10,1])*np.array([1,0,0]),np.ones([10,1])*np.array([0,1,0]),np.ones([10,1])*np.array([0,0,1])], axis=0)
y = np.array([['a']*10+['c']*10+['b']*10]).reshape([30,1])
model = xgb.XGBClassifier(n_estimators=2, objective='mlogloss').fit(x, y)
model.booster().dump_model('trees.txt')

これは基本的に、[1,0,0]が「a」にマップされ、[0,1,0]が「c」にマップされ、[0,0,1]が「b」にマップされるトレーニングデータセットを作成します。 ツリーをダンプする前に、xgboostがラベルに基づいてクラスをソートするかどうかを確認するために、意図的に「b」と「c」の順序を切り替えました。

結果のダンプされたモデルは次のとおりです。

booster[0]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[1]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[2]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0674157
    2:leaf=0.122449
booster[3]:
0:[f0<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[4]:
0:[f2<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941
booster[5]:
0:[f1<0.5] yes=1,no=2,missing=1
    1:leaf=-0.0650523
    2:leaf=0.10941

そしてここに結論があります:

  1. おっしゃるように、
  2. ツリーが使用する機能の順序を見ると、最初のツリーは最初のクラスに属し、2番目のツリーは2番目のクラスに属し、以下同様に最後のクラスに属しているように見えます。 次に、すべての推定量がカバーされるまで同じパターンが繰り返されます。
  3. クラスの順序は、 numpy.unique()が返すものと一致しているようです。ここではアルファベット順であるため、最初のツリーはf0を使用し、2番目のツリーはf2を使用しています。

これらの情報がxgboostのドキュメントに追加されていると便利です。

私は今この問題を閉じています。

ここにまだコメントがあります。 マルチクラスの場合のツリーの構造に同意します。 しかし、葉の値はどのように確率に変換されますか?

バイナリの場合は1 /(1 + exp(value))を適用する必要がありますが、マルチクラスの場合は1 /(1 + exp(-value))を適用する必要があるようです。

例を見ると、それは本当のようです。 ヒントをありがとう!

しかし、バイナリとマルチクラスの場合でこれらが異なって定義されている理由がわかりません。

はい、確かに、控えめに言っても非常に奇妙で混乱しています。

私は、UCI Car Dataset(車を許容できない、許容できる、良い、または非常に良いに分類する)でテストすることにより、その結論に達しました。

ツリーをsklearnツリーなどに変換できる追加機能があると便利です(現在、これらのxgbツリーとsklearnツリーを独自に定義した決定木に変換できるコードベースがあります)。 後者のsklearnツリーのうち、少なくとも100%はこれらが正しく変換されていると確信しています。 xgbツリーについては、疑問が残ります。

それは素晴らしい機能になることに同意します。

@GillesVandewiele @sosata :そのロジスティックでは(たとえば、クラスi )[ 1/(1+exp(-value)) ]、 valueは、その特定のクラスのツリーに対応するすべての葉のスコアの合計です。サンプルで?

私の初期のコメントの例では、[1 00]のクラス確率を予測しようとすると次のようになります。

print(model.predict_proba(np.array([[1,0,0]])))

次の結果が生成されます。

[[ 0.41852772  0.29073614  0.29073614]]

1/(1+exp(-value))がこれを生成する方法はありません。 唯一の方法は、これらの確率がクラス全体の合計値の_softmax_関数によって生成されることです。

p[i] = exp(val[i])/(exp(val[1])+exp(val[2])+...+exp(val[N]))

ここで、iは(N個のクラスのうちの)ターゲットクラスであり、val [i]はそのクラスに属するツリーから生成されたすべての値の合計です。
この例では:

print(np.exp(+0.122449+0.10941)/(np.exp(+0.122449+0.10941)+np.exp(-0.0674157-0.0650523)+np.exp(-0.0674157-0.0650523)))
print(np.exp(-0.0674157-0.0650523)/(np.exp(+0.122449+0.10941)+np.exp(-0.0674157-0.0650523)+np.exp(-0.0674157-0.0650523)))
print(np.exp(-0.0674157-0.0650523)/(np.exp(+0.122449+0.10941)+np.exp(-0.0674157-0.0650523)+np.exp(-0.0674157-0.0650523)))

生成されます:

0.418527719063
0.290736140469
0.290736140469

これはまさにpredict_proba()関数が私たちに与えたものです。

それは正しいようです@sosata

私の場合、各ツリーを個別に変換したかったのです(したがって、他のツリーの葉の値は使用しませんでした)。 そこでは、シグモイド関数がその役割を果たしているように見えました。

@sosataとにかく、クラスごとに特徴の重要性を取得することはできますか? 現在の実装では、すべてのクラスのすべての貢献を合計するだけだと思います。 ただし、R APIにはそのようなパラメーターがあるようです(https://github.com/CodingCat/xgboost/commit/e9405236a01715be1550a7e3809f36fc69ad4e8aを参照)。

@mlxai私も

ただし、私の意見では、XGBoostでの機能の重要性の定義は、その機能の分割の総数として少し単純化されています。 個人的には、特徴セットからその特徴を削除し、その特徴なしでモデルをトレーニングおよびテストしたときに、結果として生じる精度の低下(または増加)を計算することによって、特徴の重要性を計算します。 私はすべての機能に対してこれを行い、「機能の重要性」を精度の低下(またはマイナスの増加)の量として定義します。 トレーニングとテストは、特徴の削除ごとに異なるトレーニング/テストサブセットを使用して複数回実行できるため、特徴の重要性の信頼区間を推定することもできます。 さらに、クラスごとに個別に重要度を計算することで、クラスごとに機能の重要度を取得できます。 これは、分割の数を数えるよりも、私のプロジェクトでより意味のある結果を生み出しました。

@sosataあなたの例は非常に直感的ですが、各クラスのスコアをどのように考え出すかはまだわかりません。少し説明していただけますか? ありがとう!

@chrisplyn特定のクラスに属するアンサンブル内のすべての決定木を評価し、結果のスコアを合計します。

@sosataが前に正しく述べたように、アンサンブル内のツリーの数はn_esimators * n_classes等しくなります

@sosataあなたの例は非常に明確です。 どうもありがとうございました!

@chrisplyn予測インスタンス[1,0,0]は、ブースター[0〜5]によって分類され、リーフ値[0.122449、-0.0674157、-0.0674157、0.10941、-0.0650523、-0.0650523]をそれぞれ取得します。
booster [0、3]はクラス0に属し、booster [1、4]はクラス1に属し、booster [2、5]はクラス2に属するため、val [0] =(0.122449 + 0.10941)、val [1 ] =(-0.0674157 + -0.0650523)、val [2] =(-0.0674157 + -0.0650523)。
もっと明確ですか?

このページは役に立ちましたか?
0 / 5 - 0 評価