【Python】アンサンブル学習を使って住宅価格を予測する

Python

はじめに

競馬の順位予測システムを作成しています。
順位予測の性能を向上させるためにアンサンブル学習を用いることを検討しています。
ただ、アンサンブル学習を扱ったことがないので、まずはカリフォルニアの住宅価格のデータセットを用いて、アンサンブル学習の実装を行ってみました。

アンサンブル学習を行うために使用した個別モデルは下記の3つです。

  1. XGBoost
  2. LightGBM
  3. CatBoost

実装したアンサンブル学習の手法は下記の5つです。

  1. 単純平均
  2. 重み付き平均
  3. スタッキング
  4. ボルティング
  5. ブレンディング

プログラムの概要

今回作成するプログラムは、以下のような流れで動作します。

  1. 必要なライブラリをインポートする
  2. システムのCPU数を設定する
  3. カリフォルニア住宅データセットを読み込み、前処理する
  4. 3つの異なる機械学習モデル(XGBoost、LightGBM、CatBoost)を訓練する
  5. 各モデルの予測結果を組み合わせて、より精度の高い予測を行う(アンサンブル学習)
  6. 結果を評価し、各特徴量の重要度を視覚化する

プログラムの実行環境

下記の環境でプログラム実行を確認しています。
CatBoostを使用するにあたり、numpyのバージョンは1.26.0をインストールしています。

項目 バージョン
OS Windows10,11
Python 3.12.5
numpy 1.26.0
CatBoost 1.2.5
LightGBM 4.5.0
XGBoost 2.1.1
scikit-learn 1.5.1

必要なライブラリのインストール

プログラムを実行する仮想環境に、下記コマンドで必要なライブラリをインストールします。

(.venv)> pip install numpy==1.26.0
(.venv)> pip install setuptools pandas matplotlib scikit-learn xgboost lightgbm catboost psutil japanize-matplotlib

コードのフロー図

 

コードの解説

1. set_loky_max_cpu_count()

システムのCPUコア数を設定します。並列処理を効率的に行うために設定します。

def set_loky_max_cpu_count():
    physical_cores = psutil.cpu_count(logical=False)
    logical_cores = psutil.cpu_count(logical=True)
    cpu_count = max(1, min(physical_cores, logical_cores))
    os.environ['LOKY_MAX_CPU_COUNT'] = str(cpu_count)
    print(f"LOKY_MAX_CPU_COUNT set to {cpu_count}")

物理コア数と論理コア数を取得し、その中の小さい方(ただし最低1)を選択します。これにより、システムに過度な負荷をかけることなく、効率的に並列処理を行うために実行します。

2. load_and_preprocess_data()

カリフォルニア住宅価格データセットを読み込み、前処理を行います。

def load_and_preprocess_data():
    housing = fetch_california_housing()
    X, y = housing.data, housing.target
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)
    return X_train_scaled, X_test_scaled, y_train, y_test, housing.feature_names

以下の処理を行っています。

  1. scikit-learnからカリフォルニア住宅データセットを読み込みます。
  2. データを特徴量(X)と目標値(y)に分けます。
  3. データを訓練用(80%)とテスト用(20%)に分割します。
  4. StandardScalerを使用して特徴量のスケーリング(標準化)を行います。
  5. 処理したデータと特徴量名を返します。

3. train_models()

3つの異なる機械学習モデル(XGBoost、LightGBM、CatBoost)を訓練します。

def train_models(X_train_scaled, y_train):
    xgb_model = xgb.XGBRegressor(random_state=42)
    lgb_model = lgb.LGBMRegressor(random_state=42)
    cb_model = cb.CatBoostRegressor(random_state=42, verbose=0)

    xgb_model.fit(X_train_scaled, y_train)
    lgb_model.fit(X_train_scaled, y_train)
    cb_model.fit(X_train_scaled, y_train)

    return xgb_model, lgb_model, cb_model

以下の処理を行っています。

  1. 3つのモデル(XGBoost、LightGBM、CatBoost)のインスタンスを作成します。
  2. 各モデルを訓練データで学習(fit)させます。
  3. 学習済みのモデルを返します。

4. ensemble_predictions()

訓練したモデルを使って予測を行い、さまざまなアンサンブル学習の手法を適用します。

def ensemble_predictions(xgb_model, lgb_model, cb_model, X_test_scaled, y_test):
    # 各モデルの予測
    xgb_pred = xgb_model.predict(X_test_scaled)
    lgb_pred = lgb_model.predict(X_test_scaled)
    cb_pred = cb_model.predict(X_test_scaled)

    # アンサンブル学習の各手法を適用
    # ... (詳細は後述)

    # 結果を評価・出力
    models = {
        'XGBoost': xgb_pred,
        'LightGBM': lgb_pred,
        'CatBoost': cb_pred,
        'Ensemble (Simple Avg)': ensemble_simple_avg,
        'Ensemble (Weighted Avg)': ensemble_weighted_avg,
        'Ensemble (Stacking)': ensemble_stacking,
        'Ensemble (Voting)': ensemble_voting,
        'Ensemble (Blending)': ensemble_blending
    }
    evaluate_models(models, y_test)

    # 特徴量重要度を表示
    display_feature_importance(xgb_model, lgb_model, cb_model, feature_names)

以下の処理を行っています。

  1. 各モデルでテストデータの予測を行います。
  2. さまざまなアンサンブル学習手法を適用します(詳細は後述)。
  3. 各モデルと各アンサンブル手法の結果を評価します。
  4. 各モデルの特徴量重要度を表示します。

5. create_meta_features()

スタッキングアンサンブル用のメタ特徴量を作成します。

def create_meta_features(xgb_model, lgb_model, cb_model, X_train_scaled, y_train):
    X_train_meta, X_val_meta, y_train_meta, y_val_meta = train_test_split(X_train_scaled, y_train, test_size=0.2,
                                                                          random_state=42)
    xgb_meta_train = xgb_model.predict(X_val_meta)
    lgb_meta_train = lgb_model.predict(X_val_meta)
    cb_meta_train = cb_model.predict(X_val_meta)
    meta_features_train = np.column_stack((xgb_meta_train, lgb_meta_train, cb_meta_train))
    return meta_features_train, y_val_meta

以下の処理を行っています。

  1. 訓練データをさらに分割し、メタモデル用のバリデーションセットを作成します。
  2. 各モデルでバリデーションセットの予測を行います。
  3. これらの予測を新たな特徴量(メタ特徴量)として結合します。
  4. メタ特徴量と対応する正解ラベルを返します。

6. blending_ensemble()

ブレンディングアンサンブルを実行します。

def blending_ensemble(xgb_model, lgb_model, cb_model, X_train_scaled, y_train, meta_features_test):
    xgb_meta = xgb_model.predict(X_train_scaled)
    lgb_meta = lgb_model.predict(X_train_scaled)
    cb_meta = cb_model.predict(X_train_scaled)
    meta_features_train = np.column_stack((xgb_meta, lgb_meta, cb_meta))
    X_blend, X_holdout, y_blend, y_holdout = train_test_split(meta_features_train, y_train, test_size=0.2,
                                                              random_state=42)
    blend_model = LinearRegression()
    blend_model.fit(X_blend, y_blend)
    return blend_model.predict(meta_features_test)

以下の処理を行っています。

  1. 訓練データ全体で各モデルの予測を行います。
  2. これらの予測を新たな特徴量として結合します。
  3. 結合したデータをさらに分割し、ブレンディング用のデータセットを作成します。
  4. 線形回帰モデルを使ってブレンディングを行います。
  5. テストデータに対する最終的な予測を返します。

7. evaluate_models()

各モデルとアンサンブル手法の性能を評価します。

def evaluate_models(models, y_test):
    for name, predictions in models.items():
        mse = mean_squared_error(y_test, predictions)
        rmse = np.sqrt(mse)
        r2 = r2_score(y_test, predictions)
        print(f"{name}:")
        print(f"  RMSE: {rmse:.4f}")
        print(f"  R2 Score: {r2:.4f}")
        print()

以下の処理を行っています。

  1. 各モデル/手法の名前と予測結果をループで処理します。
  2. 平均二乗誤差(MSE)とその平方根(RMSE)を計算します。
  3. 決定係数(R2スコア)を計算します。
  4. 結果を出力します。

8. normalize_importance()

特徴量重要度を正規化します。

def normalize_importance(importance):
    return importance / importance.sum()

与えられた重要度の値を合計が1になるように正規化します。異なるモデル間で特徴量重要度を比較しやすくなります。

9. display_feature_importance()

各モデルの特徴量重要度を表示します。

def display_feature_importance(xgb_model, lgb_model, cb_model, feature_names):
    # 正規化前の重要度を取得
    xgb_importance = pd.DataFrame({'feature': feature_names, 'importance': xgb_model.feature_importances_}).sort_values(
        'feature', ascending=True)
    lgb_importance = pd.DataFrame({'feature': feature_names, 'importance': lgb_model.feature_importances_}).sort_values(
        'feature', ascending=True)
    cb_importance = pd.DataFrame({'feature': feature_names, 'importance': cb_model.feature_importances_}).sort_values(
        'feature', ascending=True)

    print("\n正規化前の特徴量重要度 (XGBoost):")
    print(xgb_importance)
    print("\n正規化前の特徴量重要度 (LightGBM):")
    print(lgb_importance)
    print("\n正規化前の特徴量重要度 (CatBoost):")
    print(cb_importance)

    # 正規化後の重要度を取得
    xgb_importance['importance'] = normalize_importance(xgb_importance['importance'])
    lgb_importance['importance'] = normalize_importance(lgb_importance['importance'])
    cb_importance['importance'] = normalize_importance(cb_importance['importance'])

    print("\n正規化後の特徴量重要度 (XGBoost):")
    print(xgb_importance)
    print("\n正規化後の特徴量重要度 (LightGBM):")
    print(lgb_importance)
    print("\n正規化後の特徴量重要度 (CatBoost):")
    print(cb_importance)

    # 特徴量重要度の可視化
    plot_feature_importance(xgb_importance, lgb_importance, cb_importance)

以下の処理を行っています。

  1. 各モデルの特徴量重要度を取得します。
  2. 正規化前と後の重要度を出力します。
  3. 特徴量重要度を可視化する関数を呼び出します。

10. plot_feature_importance()

特徴量重要度をグラフで可視化します。

def plot_feature_importance(xgb_importance, lgb_importance, cb_importance):
    fig, ax = plt.subplots(figsize=(15, 8))
    ax.set_title('各モデルの特徴量重要度', fontsize=16)

    xgb_importance.plot(kind='bar', x='feature', y='importance', ax=ax, position=1, width=0.2, label='XGBoost',
                        color='#FF9999')
    lgb_importance.plot(kind='bar', x='feature', y='importance', ax=ax, position=2, width=0.2, label='LightGBM',
                        color='#66B2FF')
    cb_importance.plot(kind='bar', x='feature', y='importance', ax=ax, position=3, width=0.2, label='CatBoost',
                       color='#99FF99')

    # 各バーの上に数値ラベルを追加
    for container in ax.containers:
        ax.bar_label(container, fmt='%.2f', label_type='edge', fontsize=10)

    ax.set_xlabel('特徴量', fontsize=14)
    ax.set_ylabel('重要度', fontsize=14)
    ax.legend(fontsize=12)
    ax.tick_params(axis='x', rotation=45)

    plt.tight_layout()
    plt.show()

以下の処理を行っています。

  1. 3つのモデルの特徴量重要度を棒グラフで表示します
  2. 各バーの上に数値ラベルを追加します。
  3. グラフのタイトル、軸ラベル、凡例を設定します。
  4. グラフを表示します。

各モデルの簡単な解説

  • XGBoost:
    決定木を組み合わせた高性能な機械学習アルゴリズムです。
  • LightGBM:
    XGBoostと同様に決定木ベースですが、より軽量で高速です。
  • CatBoost:
    カテゴリ変数(文字列データなど)の扱いに強い決定木ベースのアルゴリズムです。

アンサンブル学習の解説と特徴量・予測の算出方法

アンサンブル学習とは、複数のモデルを組み合わせて予測精度を向上させる手法です。今回は以下の5つの方法を実装しています。

1.単純平均:
  各モデルの予測値の平均を取ります。

 ensemble_simple_avg = (xgb_pred + lgb_pred + cb_pred) / 3

2.重み付き平均:
  各モデルの性能(R2スコア)に応じて重みをつけて平均を取ります。

    xgb_r2 = r2_score(y_test, xgb_pred)
    lgb_r2 = r2_score(y_test, lgb_pred)
    cb_r2 = r2_score(y_test, cb_pred)
    total_r2 = xgb_r2 + lgb_r2 + cb_r2
    weights = [xgb_r2 / total_r2, lgb_r2 / total_r2, cb_r2 / total_r2]
    ensemble_weighted_avg = (weights[0] * xgb_pred + weights[1] * lgb_pred + weights[2] * cb_pred)

3.スタッキング:
  各モデルの予測値を新たな特徴量として、別のモデル(ここでは線形回帰)で学習します。

    meta_features_train, y_val_meta = create_meta_features(xgb_model, lgb_model, cb_model, X_train_scaled, y_train)
    meta_model = LinearRegression()
    meta_model.fit(meta_features_train, y_val_meta)
    meta_features_test = np.column_stack((xgb_pred, lgb_pred, cb_pred))
    ensemble_stacking = meta_model.predict(meta_features_test)

4.ボルティング:
  scikit-learnのVotingRegressorを使用して、各モデルの予測を組み合わせます

    voting_model = VotingRegressor([('xgb', xgb_model), ('lgb', lgb_model), ('cb', cb_model)])
    voting_model.fit(X_train_scaled, y_train)
    ensemble_voting = voting_model.predict(X_test_scaled)

5.ブレンディング:
  トレーニングデータの一部を使って各モデルの予測を作り、それを新たな特徴量として別のモデルで学習します。

meta_features_train = np.column_stack((xgb_meta, lgb_meta, cb_meta))
X_blend, X_holdout, y_blend, y_holdout = train_test_split(meta_features_train, y_train, test_size=0.2, random_state=42)
blend_model = LinearRegression()
blend_model.fit(X_blend, y_blend)
ensemble_blending = blend_model.predict(meta_features_test)

プログラムの実行結果

全コードをverification_ensemble_learning.pyとして保存し、仮想環境で実行します。引数の指定はありません。

(.venv)> python.exe verification_ensemble_learning.py

実行すると下記のような結果が標準出力されます。
各モデルのRMSE、R2の結果、特徴量重要度が出力されます。
また、XGBoost、LightGBM、CatBoostの特徴量重要度の比較図を表示します。

<各モデルのRMSE、R2の結果、特徴量重要度>

LOKY_MAX_CPU_COUNT set to 16
[LightGBM] [Info] Auto-choosing col-wise multi-threading, the overhead of testing was 0.000704 seconds.
You can set `force_col_wise=true` to remove the overhead.
[LightGBM] [Info] Total Bins 1838
[LightGBM] [Info] Number of data points in the train set: 16512, number of used features: 8
[LightGBM] [Info] Start training from score 2.071947
[LightGBM] [Info] Auto-choosing col-wise multi-threading, the overhead of testing was 0.000408 seconds.
You can set `force_col_wise=true` to remove the overhead.
[LightGBM] [Info] Total Bins 1838
[LightGBM] [Info] Number of data points in the train set: 16512, number of used features: 8
[LightGBM] [Info] Start training from score 2.071947
XGBoost:
  RMSE: 0.4718
  R2 Score: 0.8301

LightGBM:
  RMSE: 0.4599
  R2 Score: 0.8386

CatBoost:
  RMSE: 0.4460
  R2 Score: 0.8482

Ensemble (Simple Avg):
  RMSE: 0.4454
  R2 Score: 0.8486

Ensemble (Weighted Avg):
  RMSE: 0.4454
  R2 Score: 0.8486

Ensemble (Stacking):
  RMSE: 0.4912
  R2 Score: 0.8159

Ensemble (Voting):
  RMSE: 0.4454
  R2 Score: 0.8486

Ensemble (Blending):
  RMSE: 0.4930
  R2 Score: 0.8145


正規化前の特徴量重要度 (XGBoost):
      feature  importance
3   AveBedrms    0.025678
5    AveOccup    0.148580
2    AveRooms    0.043091
1    HouseAge    0.070058
6    Latitude    0.090290
7   Longitude    0.107952
0      MedInc    0.489623
4  Population    0.024728

正規化前の特徴量重要度 (LightGBM):
      feature  importance
3   AveBedrms         177
5    AveOccup         390
2    AveRooms         253
1    HouseAge         248
6    Latitude         657
7   Longitude         697
0      MedInc         376
4  Population         202

正規化前の特徴量重要度 (CatBoost):
      feature  importance
3   AveBedrms    1.920547
5    AveOccup   13.882773
2    AveRooms    3.547387
1    HouseAge    5.435226
6    Latitude   21.184530
7   Longitude   18.641824
0      MedInc   33.487350
4  Population    1.900363

正規化後の特徴量重要度 (XGBoost):
      feature  importance
3   AveBedrms    0.025678
5    AveOccup    0.148580
2    AveRooms    0.043091
1    HouseAge    0.070058
6    Latitude    0.090290
7   Longitude    0.107952
0      MedInc    0.489623
4  Population    0.024728

正規化後の特徴量重要度 (LightGBM):
      feature  importance
3   AveBedrms    0.059000
5    AveOccup    0.130000
2    AveRooms    0.084333
1    HouseAge    0.082667
6    Latitude    0.219000
7   Longitude    0.232333
0      MedInc    0.125333
4  Population    0.067333

正規化後の特徴量重要度 (CatBoost):
      feature  importance
3   AveBedrms    0.019205
5    AveOccup    0.138828
2    AveRooms    0.035474
1    HouseAge    0.054352
6    Latitude    0.211845
7   Longitude    0.186418
0      MedInc    0.334873
4  Population    0.019004

<XGBoost、LightGBM、CatBoostの特徴量重要度の比較図>

実行結果の考察

個別のモデル性能の実行結果をまとめると下記のようになります。

モデル RMSE R2 Score
XGBoost 0.4718 0.8301
LightGBM 0.4599 0.8386
CatBoost 0.4460 0.8482

アンサンブル学習の性能の実行結果をまとめると下記のようになります。

モデル RMSE R2 Score
単純平均 0.4454 0.8486
重み付き平均 0.4454 0.8486
スタッキング 0.4912 0.8159
ボルティング 0.4454 0.8486
ブレンディング 0.4930 0.8145
  • RMSE:
    0に近いほど良い。予測値と実際の値の差が小さいことを意味します。
  • R2 Score:
    1に近いほど良い。モデルがデータの変動をよく説明できていることを意味します。

個別モデルの中では、「CatBoost」が最も高い性能を示しています。

アンサンブル学習では、単純平均、重み付け平均、ボルティングが同じ結果を示し、最も高い性能を示しています。

スタッキングとブレンディングは、今回のケースでは個別のモデルよりも性能が低くなっています。

アンサンブル学習をすることで、必ずしも個別モデルよりも良い結果を生み出すわけではないことを確認することができます。

各モデルの特徴量重要度を見ると、以下のような傾向が分かります。

  1. XGBoost:
    MedInc(中央所得)が最も重要(約49%)
    AveOccup(平均占有率)、Longitude(経度)も比較的重要
  2. LightGBM:
    Longitude(経度)とLatitude(緯度)が最も重要
    MedInc(中央所得)、AveOccup(平均占有率)も重要
  3. CatBoost:
    MedInc(中央所得)が最も重要(約33%)
    Latitude(緯度)、Longitude(経度)も重要

全体的に、中央所得(MedInc)と地理的位置(緯度・経度)が住宅価格の予測に大きく影響していることが分かります。

まとめ

アンサンブル学習を行うことで、必ずしも個別モデルより性能を上回るわけではないことを把握することができました。

今回検証した個別モデルは、全て決定木モデルです。個別モデルに線形回帰モデルやニューラルネットワークモデルを含めることで、性能向上の可能性があります。

次回以降は、競馬予測システムの改修に行っていきます。予測モデルを作成する際は、アンサンブル学習も含めて検証してみます。

プログラムの全コード

import os
import psutil
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import VotingRegressor
import xgboost as xgb
import lightgbm as lgb
import catboost as cb


# システムのCPUコア数を設定する関数
def set_loky_max_cpu_count():
    physical_cores = psutil.cpu_count(logical=False)
    logical_cores = psutil.cpu_count(logical=True)
    cpu_count = max(1, min(physical_cores, logical_cores))
    os.environ['LOKY_MAX_CPU_COUNT'] = str(cpu_count)
    print(f"LOKY_MAX_CPU_COUNT set to {cpu_count}")


# データの読み込みと前処理を行う関数
def load_and_preprocess_data():
    housing = fetch_california_housing()
    X, y = housing.data, housing.target
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)
    return X_train_scaled, X_test_scaled, y_train, y_test, housing.feature_names


# モデルを定義し、訓練を行う関数
def train_models(X_train_scaled, y_train):
    xgb_model = xgb.XGBRegressor(random_state=42)
    lgb_model = lgb.LGBMRegressor(random_state=42)
    cb_model = cb.CatBoostRegressor(random_state=42, verbose=0)

    xgb_model.fit(X_train_scaled, y_train)
    lgb_model.fit(X_train_scaled, y_train)
    cb_model.fit(X_train_scaled, y_train)

    return xgb_model, lgb_model, cb_model


# アンサンブル学習の各手法を実装し、結果を出力する関数
def ensemble_predictions(xgb_model, lgb_model, cb_model, X_test_scaled, y_test):
    # 各モデルの予測
    xgb_pred = xgb_model.predict(X_test_scaled)
    lgb_pred = lgb_model.predict(X_test_scaled)
    cb_pred = cb_model.predict(X_test_scaled)

    # ボルティングモデルの定義と訓練
    voting_model = VotingRegressor([('xgb', xgb_model), ('lgb', lgb_model), ('cb', cb_model)])
    voting_model.fit(X_train_scaled, y_train)
    ensemble_voting = voting_model.predict(X_test_scaled)

    # アンサンブル予測(単純平均)
    ensemble_simple_avg = (xgb_pred + lgb_pred + cb_pred) / 3

    # アンサンブル予測(重み付き平均)
    xgb_r2 = r2_score(y_test, xgb_pred)
    lgb_r2 = r2_score(y_test, lgb_pred)
    cb_r2 = r2_score(y_test, cb_pred)
    total_r2 = xgb_r2 + lgb_r2 + cb_r2
    weights = [xgb_r2 / total_r2, lgb_r2 / total_r2, cb_r2 / total_r2]
    ensemble_weighted_avg = (weights[0] * xgb_pred + weights[1] * lgb_pred + weights[2] * cb_pred)

    # スタッキングアンサンブル
    meta_features_train, y_val_meta = create_meta_features(xgb_model, lgb_model, cb_model, X_train_scaled, y_train)
    meta_model = LinearRegression()
    meta_model.fit(meta_features_train, y_val_meta)
    meta_features_test = np.column_stack((xgb_pred, lgb_pred, cb_pred))
    ensemble_stacking = meta_model.predict(meta_features_test)

    # ブレンディングアンサンブル
    ensemble_blending = blending_ensemble(xgb_model, lgb_model, cb_model, X_train_scaled, y_train, meta_features_test)

    # 結果を評価・出力
    models = {
        'XGBoost': xgb_pred,
        'LightGBM': lgb_pred,
        'CatBoost': cb_pred,
        'Ensemble (Simple Avg)': ensemble_simple_avg,
        'Ensemble (Weighted Avg)': ensemble_weighted_avg,
        'Ensemble (Stacking)': ensemble_stacking,
        'Ensemble (Voting)': ensemble_voting,
        'Ensemble (Blending)': ensemble_blending
    }
    evaluate_models(models, y_test)

    # 特徴量重要度を表示
    display_feature_importance(xgb_model, lgb_model, cb_model, feature_names)


# メタモデルのための特徴量を作成する関数
def create_meta_features(xgb_model, lgb_model, cb_model, X_train_scaled, y_train):
    X_train_meta, X_val_meta, y_train_meta, y_val_meta = train_test_split(X_train_scaled, y_train, test_size=0.2,
                                                                          random_state=42)
    xgb_meta_train = xgb_model.predict(X_val_meta)
    lgb_meta_train = lgb_model.predict(X_val_meta)
    cb_meta_train = cb_model.predict(X_val_meta)
    meta_features_train = np.column_stack((xgb_meta_train, lgb_meta_train, cb_meta_train))
    return meta_features_train, y_val_meta


# ブレンディングアンサンブルを実行する関数
def blending_ensemble(xgb_model, lgb_model, cb_model, X_train_scaled, y_train, meta_features_test):
    xgb_meta = xgb_model.predict(X_train_scaled)
    lgb_meta = lgb_model.predict(X_train_scaled)
    cb_meta = cb_model.predict(X_train_scaled)
    meta_features_train = np.column_stack((xgb_meta, lgb_meta, cb_meta))
    X_blend, X_holdout, y_blend, y_holdout = train_test_split(meta_features_train, y_train, test_size=0.2,
                                                              random_state=42)
    blend_model = LinearRegression()
    blend_model.fit(X_blend, y_blend)
    return blend_model.predict(meta_features_test)


# モデルの評価結果を表示する関数
def evaluate_models(models, y_test):
    for name, predictions in models.items():
        mse = mean_squared_error(y_test, predictions)
        rmse = np.sqrt(mse)
        r2 = r2_score(y_test, predictions)
        print(f"{name}:")
        print(f"  RMSE: {rmse:.4f}")
        print(f"  R2 Score: {r2:.4f}")
        print()


# 特徴量重要度を正規化する関数
def normalize_importance(importance):
    return importance / importance.sum()


# 各モデルの特徴量重要度を表示する関数
def display_feature_importance(xgb_model, lgb_model, cb_model, feature_names):
    # 正規化前の重要度を取得
    xgb_importance = pd.DataFrame({'feature': feature_names, 'importance': xgb_model.feature_importances_}).sort_values(
        'feature', ascending=True)
    lgb_importance = pd.DataFrame({'feature': feature_names, 'importance': lgb_model.feature_importances_}).sort_values(
        'feature', ascending=True)
    cb_importance = pd.DataFrame({'feature': feature_names, 'importance': cb_model.feature_importances_}).sort_values(
        'feature', ascending=True)

    print("\n正規化前の特徴量重要度 (XGBoost):")
    print(xgb_importance)
    print("\n正規化前の特徴量重要度 (LightGBM):")
    print(lgb_importance)
    print("\n正規化前の特徴量重要度 (CatBoost):")
    print(cb_importance)

    # 正規化後の重要度を取得
    xgb_importance['importance'] = normalize_importance(xgb_importance['importance'])
    lgb_importance['importance'] = normalize_importance(lgb_importance['importance'])
    cb_importance['importance'] = normalize_importance(cb_importance['importance'])

    print("\n正規化後の特徴量重要度 (XGBoost):")
    print(xgb_importance)
    print("\n正規化後の特徴量重要度 (LightGBM):")
    print(lgb_importance)
    print("\n正規化後の特徴量重要度 (CatBoost):")
    print(cb_importance)

    # 特徴量重要度の可視化
    plot_feature_importance(xgb_importance, lgb_importance, cb_importance)


# 特徴量重要度をプロットし、数値ラベルを追加する関数
def plot_feature_importance(xgb_importance, lgb_importance, cb_importance):
    fig, ax = plt.subplots(figsize=(15, 8))
    ax.set_title('各モデルの特徴量重要度', fontsize=16)

    xgb_importance.plot(kind='bar', x='feature', y='importance', ax=ax, position=1, width=0.2, label='XGBoost',
                        color='#FF9999')
    lgb_importance.plot(kind='bar', x='feature', y='importance', ax=ax, position=2, width=0.2, label='LightGBM',
                        color='#66B2FF')
    cb_importance.plot(kind='bar', x='feature', y='importance', ax=ax, position=3, width=0.2, label='CatBoost',
                       color='#99FF99')

    # 各バーの上に数値ラベルを追加
    for container in ax.containers:
        ax.bar_label(container, fmt='%.2f', label_type='edge', fontsize=10)

    ax.set_xlabel('特徴量', fontsize=14)
    ax.set_ylabel('重要度', fontsize=14)
    ax.legend(fontsize=12)
    ax.tick_params(axis='x', rotation=45)

    plt.tight_layout()
    plt.show()


# プログラムの実行
if __name__ == "__main__":
    set_loky_max_cpu_count()
    X_train_scaled, X_test_scaled, y_train, y_test, feature_names = load_and_preprocess_data()
    xgb_model, lgb_model, cb_model = train_models(X_train_scaled, y_train)
    ensemble_predictions(xgb_model, lgb_model, cb_model, X_test_scaled, y_test)

以上です。

コメント

タイトルとURLをコピーしました