scikit-learn(またはその他のPythonフレームワーク)を使用したさまざまな種類のリグレッサのアンサンブル


27

回帰タスクを解決しようとしています。LassoLARS、SVR、およびGradient Tree Boostingの3つのモデルがデータのさまざまなサブセットに対してうまく機能していることがわかりました。これら3つのモデルすべてを使用して予測を行い、次に「真の出力」と3つのモデルの出力のテーブルを作成すると、少なくとも1つのモデルが真の出力に実際に近いことがわかります。比較的遠く離れている可能性があります。

最小限のエラーを計算すると(各テスト例の「最良の」予測子から予測を取得した場合)、モデルのみのエラーよりもはるかに小さいエラーが発生します。そこで、これら3つの異なるモデルの予測を何らかのアンサンブルに結合しようと考えました。質問は、これを適切に行う方法ですか?3つのモデルはすべてscikit-learnを使用して構築および調整されていますが、アンサンブルにモデルをパックするために使用できる何らかの方法を提供していますか?ここでの問題は、3つのモデルすべてからの予測を単に平均化するのではなく、特定の例のプロパティに基づいて重み付けを決定する必要がある重み付けでこれを実行することです。

scikit-learnがそのような機能を提供しない場合でも、誰かがこのタスクに対処する方法を知っていれば、データ内の各例の各モデルの重みを把握するのがいいでしょう。これらの3つのモデルすべての上に構築された個別のリグレッサーによって実行される可能性があると思いますが、3つのモデルのそれぞれに最適な重みを出力しようとしますが、これがこれを行う最善の方法であるかどうかはわかりません。

回答:


32

実際にscikit-learnは、このような機能を提供しますが、実装するのは少し難しいかもしれません。以下は、3つのモデルの上に構築された平均的なリグレッサーの完全な実例です。まず、必要なすべてのパッケージをインポートしましょう。

from sklearn.base import TransformerMixin
from sklearn.datasets import make_regression
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import LinearRegression, Ridge

次に、3つのリグレッサモデルをトランスフォーマーに変換する必要があります。これにより、以下を使用して、それらの予測を単一の特徴ベクトルにマージできますFeatureUnion

class RidgeTransformer(Ridge, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)


class RandomForestTransformer(RandomForestRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)


class KNeighborsTransformer(KNeighborsRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)

それでは、フランケンシュタインモデルのビルダー関数を定義しましょう。

def build_model():
    ridge_transformer = Pipeline(steps=[
        ('scaler', StandardScaler()),
        ('poly_feats', PolynomialFeatures()),
        ('ridge', RidgeTransformer())
    ])

    pred_union = FeatureUnion(
        transformer_list=[
            ('ridge', ridge_transformer),
            ('rand_forest', RandomForestTransformer()),
            ('knn', KNeighborsTransformer())
        ],
        n_jobs=2
    )

    model = Pipeline(steps=[
        ('pred_union', pred_union),
        ('lin_regr', LinearRegression())
    ])

    return model

最後に、モデルを適合させましょう:

print('Build and fit a model...')

model = build_model()

X, y = make_regression(n_features=10, n_targets=2)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model.fit(X_train, y_train)
score = model.score(X_test, y_test)

print('Done. Score:', score)

出力:

Build and fit a model...
Done. Score: 0.9600413867438636

なぜこのように物事を複雑にするのですか?さて、このアプローチによりscikit-learnGridSearchCVやなどの標準モジュールを使用してモデルハイパーパラメーターを最適化できますRandomizedSearchCV。また、事前に訓練されたモデルをディスクから簡単に保存およびロードできるようになりました。


このアプローチを使用する場合、各アルゴリズムのいつ/どの部分が使用されているかを抽出する簡単な方法はありますか?
デビッドヘイガン

おそらく、結果の線形モデル(model.named_steps['lin_regr'].coef_)の係数を見ると、アンサンブルの各モデルが最終的なソリューションにどの程度貢献しているかについての洞察が得られます。
構築

@constt基本モデルでcross_val_predictを使用する必要はありませんか?これは現在実装されているため、最上位モデルはベースモデルから楽観的な信号を取得するようです。
ブライアンビエン

1
これは概念実証の例に過ぎず、ここではモデルの選択については触れませんでした。そのようなモデルは、全体として最適化する必要があると思います。つまり、相互検証アプローチを使用して、すべての組み込みモデルのハイパーパラメーターを同時に最適化する必要があります。
-constの

n_targets = 1を指定X, y = make_regression(n_features=10, n_targets=1)すると、ディメンションエラーが発生します。誰でも何をすべきか説明できますか?
Mohit Yadav

9

OK、グーグルに時間を費やした後、私はscikit-learnを使ってもpythonで重み付けを行う方法を見つけました。以下を考慮してください。

一連の回帰モデルをトレーニングします(SVR、LassoLars、GradientBoostingRegressorに言及)。次に、すべてをトレーニングデータ(これらの3つの各リグレッサーのトレーニングに使用された同じデータ)で実行します。各アルゴリズムの例の予測を取得し、これらの3つの結果を「predictedSVR」、「predictedLASSO」、「predictedGBR」の列を使用してpandasデータフレームに保存します。そして、実際の予測値である「予測」と呼ぶこのデータフランに最後の列を追加します。

次に、この新しいデータフレームで線形回帰をトレーニングします。

 #df - dataframe with results of 3 regressors and true output

 from sklearn linear_model
 stacker= linear_model.LinearRegression()
 stacker.fit(df[['predictedSVR', 'predictedLASSO', 'predictedGBR']], df['predicted'])

したがって、新しい例を予測したい場合は、3つのリグレッサを個別に実行してから実行します。

 stacker.predict() 

3つのリグレッサーの出力。そして結果を得る。

ここでの問題は、平均して回帰変数の最適な重みを見つけていることです。重みは、予測しようとする各例で同じになります。

現在の例の機能を使用してスタッキング(重み付け)を行う方法についてアイデアをお持ちの方は、ぜひ聞いてみてください。


うわー、私はこのアプローチがとても好きです!しかし、なぜモデルのLinearRegression()代わりに使用したの LogisticRegression()ですか?
harrison4

1
@ harrison4私は分類タスクではなく回帰を行っていたのですか?そこで、各モデルからの出力を「重み付け」したかったのです。とにかく、これは良いものがここで説明されて、悪いアプローチである:stackoverflow.com/a/35170149/3633250
マクシムKhaitovich

ええ、ごめんね!リンクを共有してくれてありがとう!
ハリソン4

5

データに明らかなサブセットがある場合、k-meansのようなクラスタリングアルゴリズムを実行してから、各分類子をパフォーマンスの高いクラスターに関連付けることができます。新しいデータポイントが到着したら、それがどのクラスターにあるかを判別し、関連する分類器を実行します。

また、重心からの逆距離を使用して、各分類子の重みのセットを取得し、すべての分類子の線形結合を使用して予測することもできます。


この状態をテストした論文(いくつかの同様のアイデアの比較とともに)を見つけました:論文
-anthonybell

興味深いアイデアですが、それを満足させるには多くの作業が必要です。紙をありがとう!
マクシムKhaitovich

1

すべてのモデルが完全にトレーニングされ、パフォーマンスが向上したら、次のようにして、ある種の重み付けを行います。

  1. 目に見えないテストデータの大規模なセットですべてのモデルを実行する
  2. 各モデルの各クラスのテストセットにf1スコアを保存する
  3. アンサンブルで予測する場合、各モデルは最も可能性の高いクラスを提供するため、そのクラスのモデルのf1スコアで信頼度または確率を重み付けします。距離を処理している場合(SVMなど)、一般的な信頼を得るために距離を正規化してから、クラスごとのf1重み付けを続行します。

しばらくの間、正しい割合を測定することで、アンサンブルをさらに調整できます。非常に大きな新しいデータセットのスコアリングが完了したら、たとえば、しきい値を使用してスコア付けする場合の正解率に対して、0.1のステップでしきい値をプロットして、たとえば95%の正解率を得ることができます。クラス1など。新しいデータが入ってくると、テストセットとf1スコアを更新し続け、ドリフトを追跡し、しきい値または精度が低下したときにモデルを再構築できます。


1
それは興味深いですが、回帰タスクを解決しようとしている間は、分類タスクでのみ機能します。したがって、F1スコアを計算できません。
マクシムKhaitovich
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.