2024/6 Google ColaboratoryでLightGBMを実践してみた

児山
本社

こんにちは!児山です。

今回はタイトルの通りColaboratory上でLightGBMを動かしてみました。 モデルの学習から予測値を出すところまでやったことを備忘録も兼ねて残しておきます。

やることの流れ

データの準備

ライブラリのインポート

今回は以下のライブラリを利用します。

#!pip install lightgbm #Google Colaboratoryでは最初からインストールされているので不要
!pip install japanize-matplotlib 

import lightgbm as lgb
import matplotlib.pyplot as plt, japanize_matplotlib
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.metrics import roc_curve, auc
from sklearn.model_selection import KFold
from sklearn.preprocessing import LabelEncoder

使用するデータセット

seabornライブラリからダウンロードできるtitanicのデータを利用します。

import seaborn as sns
df = sns.load_dataset("titanic")
df

▼ 出力結果 titanic_output

目的変数と説明変数

以下の通りとします。

# 目的変数
y = df['survived']

# 説明変数
X = df.loc[:,(df.columns!='survived')&(df.columns!='alive')]

本来は特徴量として使用する変数を精査しますが、 今回はLightGBMの実装を一通り行うことを目的にしているため、一旦上記の説明変数全てを特徴量として使用します。

欠損値の取り扱いとカテゴリカル変数の明示

今回は以下のような前処理を行います。

1.特徴量のうち、カテゴリカル変数であるカラムを洗い出します。

cat_features = X.select_dtypes(["object", "category"]).columns.to_list()

2.カテゴリ変数に対してLabelEncodingを行います。

# LabelEncoderをインスタンス化
le = LabelEncoder()

# カテゴリカル変数に対してLabel Encodingを適用
for col in cat_features:
    X[col] = le.fit_transform(X[col])

3.後ほどモデルにfitさせる際にcat_featuresを引数として指定します(ここの実装は後述します)

学習データとテストデータの分割

後ほど機械学習モデルを作成した後に、学習に使ったデータセット以外の未知のデータに対する性能や予測能力(これを汎化性能といいます)を検証するためにk-fold交差検証を行います。

予め学習を行うための学習データと未知のデータに適用したときのモデルを評価するためのテストデータに分割します。

X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.3, random_state = 0)

モデルの設定

モデルの実装

まずは実装したコードを一旦記載します。コードが長いので、実装内容の詳細と実行結果はこの後解説します。

#モデルの設定
param = {"objective": "binary"}

best_iterations = {}
models = {}
scores = []

# k-Fold交差検証を実行
kf = KFold(n_splits = 3, shuffle=True, random_state=0)

for curr_fold_num, (train_idx, test_idx) in enumerate(kf.split(X, y)):
    # 学習データとテストデータを取得
    X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
    y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]

    train_data = lgb.Dataset(X_train, label=y_train)
    valid_data = lgb.Dataset(X_test, label=y_test)

    # 評価指標の格納先
    evals = {}

    # モデルを学習
    bst = lgb.train(param,
      train_data,
      valid_sets=[valid_data],
      callbacks=[lgb.record_evaluation(evals), lgb.early_stopping(20)],
      categorical_feature=cat_features,
      )

    # テストデータで予測
    oof_preds = bst.predict(X_test, num_iteration = bst.best_iteration)
    
    # AUCの算出
    fpr, tpr, threshold = roc_curve(y_test, oof_preds)
    scores.append(auc(fpr, tpr))
        
    # 最良の予測器が得られたイテレーション数
    best_iterations[curr_fold_num] = bst.best_iteration
    
    # 損失関数をプロット
    lgb.plot_metric(evals)
    
    # 特徴量の重要度をプロット
    lgb.plot_importance(bst)
    
    # モデルを格納
    models[curr_fold_num] = bst

モデルの学習

以下の箇所で学習データとテストデータの取得〜学習までを行っています。

    # 学習データとテストデータを取得
    X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
    y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]

    train_data = lgb.Dataset(X_train, label=y_train)
    valid_data = lgb.Dataset(X_test, label=y_test)

    # 評価指標の格納先
    evals = {}

    # モデルを学習
    bst = lgb.train(param,
      train_data,
      valid_sets=[valid_data],
      callbacks=[lgb.record_evaluation(evals), lgb.early_stopping(20)],
      categorical_feature = cat_features,
      )

予測の実施

# テストデータで予測
oof_preds = bst.predict(X_test, num_iteration = bst.best_iteration)
# モデルを格納
models[curr_fold_num] = bst

モデルの評価

AUCを使ったモデルの評価

先ほどの実装の以下の部分で

を行っています。

# AUCの算出
fpr, tpr, threshold = roc_curve(y_test, oof_preds)
scores.append(auc(fpr, tpr))

scoresに格納している理由は後述します。

k-fold交差検証

下の図のようにデータをいくつかに分解し、分割したデータすべてが1回ずつテストデータになるように学習を行なった後に精度の平均をとることでモデル精度を評価します。

k-fold_cross_validation

今回の実装上ではデータを3つに分割してループを回しています。

# k-Fold交差検証を実行
kf = KFold(n_splits = 3, shuffle=True, random_state=0)

for curr_fold_num, (train_idx, test_idx) in enumerate(kf.split(X, y)):
  #ループの配下でモデルの学習・予測、モデル性能の評価を行う

モデルの精度は先述した通りAUCを使って評価しています。

ループを1回回すごとにAUCの面積をscoresというリストに格納しているため、 以下のようにscoresの平均を出すことでAUCの平均値を求めることができます。

print(f"""
Average AUC: {np.mean(scores)}
""")

実行してみるとAUCの平均は0.867という結果になりました。

AUC出力結果

まとめ

今回はLightGBMの機械学習モデルを実装してみました。

注意が必要なのは今回作成したモデルがあくまでこのモデルに用いた特徴量を用意できれば AUC0.867の精度で生存するか生存しないかの予測ができる、という点です。

つまり、タイタニック号と同時代・同規模の客船であればそれなりの精度で予測ができるかもしれませんが現代の一般的な客船や貨物船にも適用できるような外的妥当性はありません。

もし現代の貨客船でも通用するような特徴量を使ってモデルを構築する場合は 例えば、現代の客船のデータを元にモデルを構築する、運賃を現代価格に置き換えるなど様々な条件をそろえた上でモデルを作成することが重要になります。

今度は別のデータを使って実装してみたいですね。

以上お読みいただきありがとうございました。

以降はおまけになります。

おまけ:検証の可視化

その1:損失関数の確認

先ほどモデルの学習時にevalsへ格納した評価指標をプロットします。

今回は2値分類を指定しているので、評価指標にはbinary_loss(ざっくりいうとモデルの予測と実際のテストデータの結果のずれを対数の形でとったもの)がデフォルトで入ってます。

    # 損失関数をプロット
    lgb.plot_metric(evals)

▼実行結果 loss_func_1st loss_func_2nd loss_func_3rd

今回はk-fold交差検証により3回に分けて学習しているので3つグラフが表示されます。 学習時の訓練回数が20回を超えたあたりから徐々に精度が落ちており、過学習を起こしている可能性があります。

サンプルを増やす、学習回数を調整する、学習時に作成する決定木の深さを調整するなどパラメータ調整の余地がありそうです。

その2:特徴量の重要度の可視化

   # 特徴量の重要度をプロット
   lgb.plot_importance(bst)

▼実行結果 feature_importance_1st feature_importance_2nd feature_importance_3rd

このモデルではfare(運賃)とage(年齢)の影響を重視しているようです。

モデルが何の特徴量を重視したかを可視化することの意義

最後までお読みいただきありがとうございました。

こんな記事も読まれています