search
LoginSignup
3

posted at

updated at

Bagging や Blending で予測の分散を考慮する

回帰モデルで予測の分散を考慮できるモデルとして、ガウス過程回帰があります。でもあれはあれでけっこうクセがあると思ってて、他の回帰モデルでも予測の分散を考慮できたらなと思ってました。そこで、アンサンブル手法として知られる Bagging や Blending を使えば良いと思ったので試してみました。

予測したい関数

ここでは、単純のため以下のような1変数関数を用います。

import numpy as np

f = lambda x: 0.05*x+0.2*np.sin(x) + 0.3*np.cos(2.1*x) + 0.5*np.sin(0.61*x) + 0.2*np.sin(x/3.1) + 0.1*np.sin(5.1*x)

図示したらこんな感じ。

import matplotlib.pyplot as plt

x_domain = np.linspace(-30, 30, 500) # 関数の範囲
plt.plot(x_domain, f(x_domain))
plt.show()

Bagging_や_Blending_で予測の分散を考慮する_3_0.png

下記のように訓練データを取得して、それを学習させてみたいと思います。

x_train = np.linspace(-20, 20, 40) # 訓練データ

plt.scatter(x_train, f(x_train), label="training data")
plt.plot(x_domain, f(x_domain), label="true function")
plt.legend()
plt.show()

Bagging_や_Blending_で予測の分散を考慮する_5_0.png

ガウス過程回帰

GaussianProcessRegressor を用いて、予測および予測の分散を求めます。

from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF

kernel = 1.0 * RBF(length_scale=1.0, length_scale_bounds=(1e-1, 10.0))
model = GaussianProcessRegressor(kernel=kernel, random_state=0)
model.fit(x_train.reshape(-1, 1), f(x_train))
y_pred, y_std = model.predict(x_domain.reshape(-1, 1), return_std=True)

結果の図示

plt.scatter(x_train, f(x_train), label="training data")
plt.plot(x_domain, f(x_domain), label="true function")
plt.plot(x_domain, y_pred, label="mean prediction")
plt.fill_between(x_domain, y_pred-y_std, y_pred+y_std, alpha=0.5, label="prediction std")
plt.legend()
plt.show()

Bagging_や_Blending_で予測の分散を考慮する_9_0.png

こんな感じで、ガウス過程って、けっこう過剰適合(過学習)しがちで、そのくせ内挿は自信過剰で、外挿はテキトーすぎる印象があるんですよねー(個人の感想です)。

アンサンブル学習器から予測の分散を求める関数

scikit-learnには数多くのアンサンブル学習器が用意されていて、平素より大変お世話になっておりますが、こんな関数が用意されてるともっと嬉しい気がするんです。

def predict_std(model, X):
    y_pred = []
    for base_model in model.estimators_:
        y_pred.append(base_model.predict(X))
    
    return np.array(y_pred).std(axis=0)

Blending

機械学習界隈で「Blending」と呼ばれるアンサンブルは VotingRegressor として実装されています。それを使って、たとえば MLPRegressor をアンサンブルしてみます。

from sklearn.ensemble import VotingRegressor
from sklearn.neural_network import MLPRegressor

model = VotingRegressor(
    estimators = [(str(x), 
                   MLPRegressor(
                       hidden_layer_sizes=[100 for x in range(10)],
                       max_iter=1000,
                       #early_stopping=True
                       )
    ) for x in range(100)]
)

model.fit(x_train.reshape(-1, 1), f(x_train))
y_pred = model.predict(x_domain.reshape(-1, 1))
y_std = predict_std(model, x_domain.reshape(-1, 1))

図示すると次のようになります。うーん、まだ不十分な感じだけど、ガウス過程回帰よりは、良いんじゃないですかね。

plt.scatter(x_train, f(x_train), label="training data")
plt.plot(x_domain, f(x_domain), label="true function")
plt.plot(x_domain, y_pred, label="mean prediction")
plt.fill_between(x_domain, y_pred-y_std, y_pred+y_std, alpha=0.5, label="prediction std")
plt.legend()
plt.show()

Bagging_や_Blending_で予測の分散を考慮する_16_0.png

MLPRegressorには、過剰適合を防ぐ early_stopping=True というオプションがあるのですが、それを入れるとさらにザックリとした予測になります。

Bagging

次は Bagging です。

from sklearn.ensemble import BaggingRegressor
from sklearn.neural_network import MLPRegressor

model = BaggingRegressor(
    base_estimator=MLPRegressor(
        hidden_layer_sizes=[100 for x in range(10)],
        max_iter=1000,
        #early_stopping=True
        ),
    n_estimators=100
    )

model.fit(x_train.reshape(-1, 1), f(x_train))
y_pred = model.predict(x_domain.reshape(-1, 1))
y_std = predict_std(model, x_domain.reshape(-1, 1))

結果の図示

plt.scatter(x_train, f(x_train), label="training data")
plt.plot(x_domain, f(x_domain), label="true function")
plt.plot(x_domain, y_pred, label="mean prediction")
plt.fill_between(x_domain, y_pred-y_std, y_pred+y_std, alpha=0.5, label="prediction std")
plt.legend()
plt.show()

Bagging_や_Blending_で予測の分散を考慮する_20_0.png

うん、このくらいが一番良い気がしますね(個人の感想です)。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
3