取り急ぎ実行してみる「Pythonアンサンブル学習」
(バギング・ブースティング・スタッキング)

取り急ぎ実行してみる「Pythonアンサンブル学習」(バギング・ブースティング・スタッキング)

アンサンブル学習は、複数の機械学習モデルを組み合わせることで、単一のモデルよりも高い予測精度を達成する手法です。

アンサンブル学習は、現実世界の様々な問題に適用され、機械学習コンペティションでも常に上位を占める手法として知られています。

アンサンブル学習の主要な手法には、バギング、ブースティング、スタッキングがあります。

これらの手法は、それぞれ異なる原理に基づいていますが、いずれも複数のモデルを組み合わせることで、モデルの一般化性能を向上させることを目的としています。

以下の記事で若干整理し言及しています。

第381話|アンサンブル学習でモデルの性能を向上させよう! ビジネスに活かす機械学習テクニック

Pythonには、アンサンブル学習を実装するための優れたライブラリが複数存在します。代表的なものとして、scikit-learnXGBoostLightGBMが挙げられます。

バギングのPython実装

バギング(Bootstrap Aggregating)は、複数の弱学習器を組み合わせることで、より強力な学習器を作成する手法です。

バギングでは、元のデータからブートストラップサンプリングによって複数のサブセットを作成し、各サブセットに対して独立に学習器を訓練します。最終的な予測は、各学習器の予測を平均化(回帰の場合)または多数決(分類の場合)することで行います。

バギングの代表的な例として、ランダムフォレストが挙げられます。

ランダムフォレストは、複数の決定木を組み合わせたアンサンブル学習モデルです。各決定木は、元のデータからブートストラップサンプリングによって作成されたサブセットに対して訓練されます。さらに、各ノードでの分割に使用する特徴量もランダムに選択されます。

以下は、scikit-learnを使ったランダムフォレストの実装例です。

from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# Irisデータセットの読み込み
iris = load_iris()
X, y = iris.data, iris.target

# データを訓練用とテスト用に分割
X_train, X_test, y_train, y_test = train_test_split(
    X, 
    y, 
    test_size=0.2, 
    random_state=42)

# ランダムフォレストのインスタンスを生成
rf = RandomForestClassifier(
    n_estimators=100, 
    max_depth=5, 
    random_state=42)

# モデルの学習
rf.fit(X_train, y_train)

# テストデータに対する予測
rf_pred = rf.predict(X_test)

# 精度の評価
print(f"Random Forest Accuracy: {accuracy_score(y_test, rf_pred)}")

 

このコードについて説明します。

ライブラリのインポート
必要なライブラリと関数をインポートしています。これにはデータセットのロード、ランダムフォレストのための関数、データの分割、そして精度の計算のための関数が含まれます。

データの準備
アヤメデータセット(iris)をロードし、特徴量(X)と目的変数(y)に分けています。その後、訓練用とテスト用にデータを分割しています。テストデータは全体の20%とされています。

ランダムフォレストモデルの生成
RandomForestClassifierのインスタンスを作成しています。引数であるn_estimatorsは決定木の数、max_depthは決定木の深さ、random_stateはランダムシードの指定になります。

モデルの学習
訓練データを使ってランダムフォレストモデルの学習を行います。

テストデータに対する予測
学習したモデルを使ってテストデータの予測を行い、その結果をrf_predに保存します。

モデルの評価
accuracy_score関数を用いてテストデータの正解ラベル(y_test)と予測結果(rf_pred)の間で精度を計算し、表示しています。この結果、このモデルの精度は100%(1.0)と非常に高いことが確認できます。

 

以下、実行結果です。

Random Forest Accuracy: 1.0

 

ランダムフォレストは、高い汎化性能と特徴量の重要度の評価が可能という特徴があります。

特徴量の重要度は、各特徴量がどの程度モデルの予測に寄与しているかを示す指標で、以下のように計算できます。

以下、コードです。

importances = rf.feature_importances_
feature_names = iris.feature_names
for name, importance in zip(feature_names, importances):
    print(f"{name}: {importance}")

 

このコードについて説明します。

特徴量の重要度の取得
rf.feature_importances_は、ランダムフォレストモデルの各特徴量の重要度を取得するプロパティです。これにより、各特徴量がモデルの予測にどれだけ影響を及ぼしているかを理解することができます。

特徴名の取得
Irisデータセットのfeature_namesプロパティから特徴量の名前を取得します。

特徴量の名前と重要度の出力
zip関数を使用して特徴名とその重要度を対応付け、それぞれの特徴名とその重要度を出力します。

 

以下、実行結果です。

sepal length (cm): 0.10591251332926463
sepal width (cm): 0.028037354433767713
petal length (cm): 0.44525709279812015
petal width (cm): 0.42079303943884766

 

出力結果を見ると、petal length (cm)petal width (cm)の2つの特徴量が最も重要であることがわかります。この2つの特徴量がランダムフォレストモデルの予測における主要な要素であると言えます。

 

ブースティングのPython実装

ブースティングは、弱学習器を逐次的に訓練し、それらを組み合わせることで強学習器を作成する手法です。

各学習器は、前の学習器が誤って分類したサンプルに重点を置いて訓練されます。最終的な予測は、各学習器の予測を重み付き多数決で決定します。

ブースティングの代表的な手法として、AdaBoost勾配ブースティング決定木(GBDT)があります。

 

 AdaBoost

AdaBoostは、各学習器の重みを適応的に調整しながら学習を進める手法です。

scikit-learnを使って以下のように実装できます。

from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 乳がんデータセットの読み込み
cancer = load_breast_cancer()
X, y = cancer.data, cancer.target

# データを訓練用とテスト用に分割
X_train, X_test, y_train, y_test = train_test_split(
    X, 
    y, 
    test_size=0.2, 
    random_state=42)

# AdaBoostのインスタンスを生成
ada = AdaBoostClassifier(
    n_estimators=100, 
    random_state=42)

# モデルの学習
ada.fit(X_train, y_train)

# テストデータに対する予測
ada_pred = ada.predict(X_test)

# 精度の評価
print(f"AdaBoost Accuracy: {accuracy_score(y_test, ada_pred)}")

 

このコードについて説明します。

ライブラリのインポート
必要なライブラリと関数をインポートしています。 sklearnライブラリからデータセットのロード、AdaBoostのためのクラス、データ分割用の関数、精度の計算用の関数などがインポートされています。

データの準備
乳がんデータセットをロードしており、特徴量(X)とターゲット変数(y)にデータを分けています。その後、train_test_split関数を使ってデータを訓練用とテスト用に20%のテストデータと共に分割しています。

AdaBoost モデルの生成
AdaBoostClassifierのインスタンスを作成しています。ここで設定しているパラメータは、n_estimators(学習器の数)とrandom_state(乱数のシード)です。

モデルの学習
訓練データを用いてAdaBoost モデルの学習を行っています。

テストデータに対する予測
学習したモデルを用いてテストデータの予測を行い、その結果をada_predに保管しています。

モデルの評価
accuracy_score関数を用いてテストデータの正解ラベルy_testと予測結果ada_predの間で精度を計算し、表示しています。

 

以下、実行結果です。

AdaBoost Accuracy: 0.9736842105263158

 

このAdaBoostモデルの精度は約97.4%となり、これはモデルが高い性能を持っていることを示しています。

 

 勾配ブースティング決定木(GBDT)

GBDTは、各学習器が前の学習器の残差を学習する手法です。

GBDTを実装するためには、XGBoostLightGBMといったライブラリを使用するのが一般的です。

以下は、XGBoostを使ったGBDTの実装例です。

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import xgboost as xgb

# ボストン住宅価格データセットの読み込み
boston = load_boston()
X, y = boston.data, boston.target

# データを訓練用とテスト用に分割
X_train, X_test, y_train, y_test = train_test_split(
    X, 
    y, 
    test_size=0.2, 
    random_state=42)

# XGBoostのデータ構造を作成
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)

# ハイパーパラメータの設定
params = {
    'objective': 'reg:squarederror',
    'max_depth': 5,
    'learning_rate': 0.1,
    'n_estimators': 100
}

# モデルの学習
model = xgb.train(params, dtrain)

# テストデータに対する予測
y_pred = model.predict(dtest)

# 平均二乗誤差(MSE)の評価
print(f"XGBoost MSE: {mean_squared_error(y_test, y_pred)}")

 

このコードについて説明します。

ライブラリのインポート
必要なライブラリと関数をインポートしています。データセットのロード、データの分割、平均二乗誤差(MSE)の計算のための関数、そしてXGBoostをインポートしています。

データの準備
ボストン住宅価格データセットをロードし、特徴量(X)と目的変数(y)に分けています。その後、訓練用とテスト用にデータを分割しています。テストデータは全体の20%とされています。

XGBoostのデータ構造作成
訓練データとテストデータをXGBoostのデータ構造、DMatrixに変換しています。

ハイパーパラメータの設定
XGBoostのハイパーパラメータを設定しています。ここでは目的関数(objective)を二乗誤差の回帰(reg:squarederror)、最大深度(max_depth)を5、学習率(learning_rate)を0.1、推定器の数(n_estimators)を100に設定しています。

モデルの学習
xgb.train関数を使ってモデルの学習を行っています。

テストデータに対する予測
学習したモデルを使ってテストデータの予測を行い、その結果をy_predに保存します。

モデルの評価
mean_squared_error関数を用いてテストデータの真の値(y_test)と予測(y_pred)との平均二乗誤差(MSE)を計算し、表示しています。

 

以下、実行結果です。

XGBoost MSE: 19.881643993639106

 

予測結果の「MSE: 19.881643993639106」はモデルの予測精度を示しています。MSEはエラー(実際の値と予測値との差)の二乗平均を表すため、0に近いほど予測精度が高いモデルであると言えます。

 

スタッキングのPython実装

スタッキングは、複数の異なる学習アルゴリズムを組み合わせて、より強力な予測モデルを作成する手法です。

スタッキングでは、複数の第1段階の学習器を訓練し、それらの予測結果を特徴量として使用して、第2段階の学習器(メタモデル)を訓練します。

スタッキングの流れは以下の通りです。

  1. 訓練データを、学習用とバリデーション用に分割する。
  2. 複数の第1段階の学習器を、学習用データで訓練する。
  3. 訓練済みの第1段階の学習器を使用して、バリデーション用データに対する予測を行う。
  4. バリデーション用データに対する予測結果を特徴量として、第2段階の学習器(メタモデル)を訓練する。
  5. テストデータに対して、第1段階の学習器で予測を行い、その予測結果をメタモデルに入力して最終的な予測を得る。

 

以下は、scikit-learnを使ったスタッキングの実装例です。

from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.ensemble import StackingClassifier

# 乳がんデータセットの読み込み
cancer = load_breast_cancer()
X, y = cancer.data, cancer.target

# データを訓練用とテスト用に分割
X_train, X_test, y_train, y_test = train_test_split(
    X, 
    y, 
    test_size=0.2, 
    random_state=42)

# 第1段階の学習器を定義
estimators = [
    ('rf', RandomForestClassifier(n_estimators=100, random_state=42)),
    ('svc', SVC(probability=True, random_state=42))
]

# スタッキングのインスタンスを生成(メタモデルはロジスティック回帰)
clf = StackingClassifier(
    estimators=estimators, 
    final_estimator=LogisticRegression())

# モデルの学習
clf.fit(X_train, y_train)

# テストデータに対する予測
y_pred = clf.predict(X_test)

# 精度の評価
print(f"Stacking Accuracy: {accuracy_score(y_test, y_pred)}")

 

このコードについて説明します。

ライブラリのインポート
必要なライブラリと関数をインポートしています。これにはデータセットのロード、ランダムフォレストとSVCの分類器、データの分割、精度の計算、そしてスタッキング分類器が含まれています。

データの準備
乳がんデータセットをロードし、特徴量(X)と目的変数(y)に分けています。その後、データを訓練用とテスト用に分割しています。テストデータは全体の20%です。

第1段階の学習器の定義
特徴量の学習に使用する2つの学習器(ランダムフォレストとSVC)を定義しています。

スタッキング分類器のインスタンス生成
第1段階の学習器と最終的なメタ学習器(ロジスティック回帰)を定義したスタッキング分類器のインスタンスを生成しています。

モデルの学習
訓練データを用いてスタッキング分類器の学習を行っています。

テストデータに対する予測
学習したモデルをテストデータに適用し、その結果をy_predに保存しています。

モデルの評価
accuracy_score関数を使用して、テストデータの真のラベルと予測ラベルの間で精度を計算し、表示しています。

 

以下、実行結果です。

Stacking Accuracy: 0.9736842105263158

 

最終的な精度は約97.4%であり、これはスタッキングがある程度うまく機能していることを示しています。

 

スタッキングは、異なる特性を持つ学習アルゴリズムを組み合わせることで、単一のアルゴリズムでは得られない高い予測精度を達成することができます。ただし、第1段階の学習器の選択やメタモデルの設計には経験と試行錯誤が必要となります。

また、スタッキングは、計算コストが高くなる傾向があります。第1段階の学習器を訓練し、それらの予測結果を特徴量として使用するため、訓練時間が長くなることがあります。

 

アンサンブル学習のハイパーパラメータ調整

アンサンブル学習モデルの性能は、ハイパーパラメータの設定に大きく依存します。

ハイパーパラメータは、モデルの構造や学習アルゴリズムを制御するパラメータで、手動で設定する必要があります。適切なハイパーパラメータを見つけることは、モデルの性能を最大限に引き出すために重要です。

ハイパーパラメータ調整には、グリッドサーチ、ランダムサーチ、ベイズ最適化などの手法があります。

ここでは、scikit-learnを使ったグリッドサーチランダムサーチの実装例を示します。

 

グリッドサーチは、指定したハイパーパラメータの組み合わせをすべて試す網羅的な探索手法です。

以下は、ランダムフォレストハイパーパラメータグリッドサーチで調整する例です。

from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

# Irisデータセットの読み込み
iris = load_iris()
X, y = iris.data, iris.target

# ハイパーパラメータの候補を指定
param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth': [None, 5, 10],
    'min_samples_split': [2, 5, 10]
}

# ランダムフォレストのインスタンスを生成
rf = RandomForestClassifier(random_state=42)

# グリッドサーチのインスタンスを生成
grid_search = GridSearchCV(
    estimator=rf, 
    param_grid=param_grid, 
    cv=5, 
    n_jobs=-1)

# グリッドサーチの実行
grid_search.fit(X, y)

# 最良のハイパーパラメータとスコアを表示
print(f"Best parameters: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_}")

 

このコードについて説明します。

ライブラリのインポート
必要なライブラリと関数をインポートしています。これにはデータセットのロード、ランダムフォレストのクラス、そしてグリッドサーチのクラスが含まれています。

データの準備
Irisデータセットをロードし、特徴量(X)と目的変数(y)に分けています。

ハイパーパラメータの候補の指定
ハイパーパラメータの候補となる値のリストを指定しています。ここでは決定木の数(n_estimators)、決定木の最大深度(max_depth)、ノードを分割するための最小サンプル数(min_samples_split)の3つのハイパーパラメータを調整しています。

ランダムフォレストのインスタンス生成
ランダムフォレストのインスタンスを生成しています。

グリッドサーチのインスタンス生成
GridSearchCVクラスを使ってグリッドサーチのインスタンスを生成しています。この時にランダムフォレストとハイパーパラメータの候補をを引数に渡しています。cvパラメータは交差検証の分割数を表し、n_jobsは並列実行するジョブの数です(-1を指定すると全てのプロセッサを使用します)。

グリッドサーチの実行
fitメソッドを使ってグリッドサーチを実行しています。これにより全てのハイパーパラメータの組み合わせでモデルが訓練され、ベストのハイパーパラメータが決定されます。

最良のハイパーパラメータとスコアの表示
最良のハイパーパラメータとその時のスコア(ここでは正解率)を表示しています。

以下、実行結果です。

Best parameters: {'max_depth': None, 'min_samples_split': 2, 'n_estimators': 50}
Best score: 0.9666666666666668

 

この結果、最良のハイパーパラメータはn_estimators: 50, max_depth: None, min_samples_split: 2であり、そのときのスコア(正解率)は約0.967であることがわかります。

 

ランダムサーチは、指定したハイパーパラメータの範囲からランダムにサンプリングして探索する手法です。

グリッドサーチに比べて、少ない計算量で効率的な探索が可能です。

以下は、ランダムフォレストのハイパーパラメータをランダムサーチで調整する例です。

from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint

# Irisデータセットの読み込み
iris = load_iris()
X, y = iris.data, iris.target

# ハイパーパラメータの候補を指定
param_dist = {
    'n_estimators': randint(50, 200),
    'max_depth': [None, 5, 10],
    'min_samples_split': randint(2, 10)
}

# ランダムフォレストのインスタンスを生成
rf = RandomForestClassifier(random_state=42)

# ランダムサーチのインスタンスを生成
random_search = RandomizedSearchCV(
    estimator=rf, 
    param_distributions=param_dist, 
    n_iter=20, 
    cv=5, 
    n_jobs=-1, 
    random_state=42)

# ランダムサーチの実行
random_search.fit(X, y)

# 最良のハイパーパラメータとスコアを表示
print(f"Best parameters: {random_search.best_params_}")
print(f"Best score: {random_search.best_score_}")

 

このコードについて説明します。

必要なライブラリのインポート
ここではデータセットのロード、ランダムフォレストの分類器、ランダムサーチのクラス、そして整数値の一様なランダム変数を生成する関数がインポートされています。

データの準備
Irisデータセットをロードし、特徴量(X)と目的変数(y)に分けています。

ハイパーパラメータの候補の指定
ハイパーパラメータとその探索範囲を指定しています。ここでは決定木の数(n_estimators)、決定木の最大深度(max_depth)、ノードを分割するための最小サンプル数(min_samples_split)を調整しています。パラメータn_estimatorsmin_samples_splitについては、50から200の範囲、2から10の範囲から無作為に値を選択することが指定されています。

ランダムフォレストのインスタンス生成
ランダムフォレスト分類器のインスタンスを生成しています。

ランダムサーチのインスタンス生成
RandomizedSearchCVクラスを用いてランダムサーチのインスタンスを生成しています。ランダムフォレストとハイパーパラメータの候補、探索するパラメータ設定の数(n_iter)、交叉検証の分割数(cv)、並列実行するジョブの数(n_jobs)、乱数生成のシード(random_state)を引数として渡しています。

ランダムサーチの実行
fitメソッドを使い、ランダムサーチを実行しています。その結果としてハイパーパラメータの最良の組み合わせが見つかります。

最良のハイパーパラメータとスコアの表示
最良のハイパーパラメータの組み合わせとその時のスコア(ここでは交差検証による正解率の平均)を出力しています。

 

以下、実行結果です。

Best parameters: {'max_depth': 10, 'min_samples_split': 5, 'n_estimators': 142}
Best score: 0.9666666666666668

 

最終的な結果として、最良のハイパーパラメータはmax_depth: 10, min_samples_split: 5, n_estimators: 142 であり、その時のスコア(正解率)は約0.967であることがわかります。

 

グリッドサーチランダムサーチは、探索範囲を指定する必要があります。探索範囲が広すぎると計算量が増大し、狭すぎると最適なハイパーパラメータが見つからない可能性があります。そのため、適切な探索範囲を設定することが重要です。

ベイズ最適化は、ベイズ的な手法を用いてハイパーパラメータを最適化する手法です。ベイズ最適化は、少ない試行回数で効率的に最適なハイパーパラメータを見つけることができますが、ここでは詳細は割愛します。

 

まとめ

今回は、アンサンブル学習の主要な手法であるバギング、ブースティング、スタッキングについて、Pythonでの実装方法を解説しました。これらの手法は、単一のモデルでは達成できない高い予測精度を実現し、機械学習のタスクにおいて広く使用されています。

また、アンサンブル学習モデルハイパーパラメータ調整についても簡単に説明しました。アンサンブル学習モデルの性能は、ハイパーパラメータの設定に大きく依存します。ハイパーパラメータ調整には、グリッドサーチ、ランダムサーチ、ベイズ最適化などの手法があります。

アンサンブル学習ビジネスに活用する際は、以下の点に留意することが重要です。

  • 問題の特性に適した手法の選択:バギング、ブースティング、スタッキングのそれぞれの特徴を理解し、問題の性質に合わせて適切な手法を選択する必要があります。
  • データの品質の確保:アンサンブル学習は、大量のデータから複雑なパターンを学習することができますが、データの品質が低い場合は予測精度が低下する可能性があります。データの前処理と特徴量エンジニアリングにも注意が必要です。
  • モデルの解釈性の重視:ビジネスの意思決定に機械学習モデルを活用する際は、モデルの予測根拠を説明できることが重要です。SHAPやLIMEなどの手法を用いて、モデルの解釈性を高めることが求められます。
  • 継続的なモデルの改善:ビジネス環境の変化に合わせて、モデルを継続的に改善していく必要があります。新しいデータが得られた際には、モデルを再学習させ、予測精度の維持・向上を図ることが重要です。

今後のアンサンブル学習の展望です。

  • 深層学習モデルとの組み合わせ:深層学習モデルとアンサンブル学習を組み合わせることで、より高度な予測が可能になると期待されます。例えば、複数の深層学習モデルをスタッキングすることで、単一のモデルでは達成できない性能を実現できる可能性があります。
  • 効率的なハイパーパラメータ最適化手法の開発:ベイズ最適化などの効率的なハイパーパラメータ最適化手法の発展により、より短時間で最適なハイパーパラメータを見つけることができるようになると期待されます。
  • 説明可能な人工知能(XAI)の発展:SHAPやLIMEに代表される説明可能な人工知能の手法が発展することで、アンサンブル学習モデルの予測根拠をより詳細に解釈できるようになると考えられます。

アンサンブル学習は、機械学習の分野で重要な役割を果たしており、今後もビジネスにおけるデータ活用の中核を担うことが期待されます。皆様がアンサンブル学習を理解し、活用する一助となれば幸いです。