【Python】XGBoostの実装方法と特徴量重要度

Python
スポンサーリンク

XGBoostは簡単に実装できるわりに精度が高いので、ベースラインモデルとして優秀です。

import xgboost as xgb

# データ形式の変換
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)

# パラメータ設定
params = {
    "objective": "reg:squarederror" # 回帰

    #"objective": "binary:logistic" # 二値分類

    #"objective": "multi:softprob"  # 他クラス分類
    #"num_class": 3                 # 他クラス分類
    }

# 学習
model = xgb.train(
    params = params,
    dtrain = dtrain,
    evals = [(dtrain, "train"), (dtest, "test")],
)

似たモデルにLightGBMがあります。こっちの方がメモリ消費が小さいです。

一方、xgboostはGPUを使って学習を高速化できます。

GPUを使いたいなら、クラウドでGoogle Colaboratoryを利用するか、PCを自作するかしましょう。

回帰モデル

データセット

import pandas as pd
from sklearn.datasets import load_diabetes

data = load_diabetes()
df = pd.DataFrame(data["data"], columns = data["feature_names"])
df["target"] = data["target"]
df

“age” ~ “s6″までの特徴量を使って”target”を予測しましょう。

データ分割

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size = 0.1)
print(train.shape, test.shape)

# ========== output ==========
# (397, 11) (45, 11)

train_test_splitで学習用データと検証用データとにわけました。

test_size = 0.1にすると10%が検証用になります。

モデルの精度を評価する際は、答えの知らない検証用データを使いましょう。

features = [c for c in df.columns if c != "target"]
print(features)

#学習用データ
X_train = train[features]
y_train = train["target"].values

#検証用データ
X_test = test[features]
y_test = test["target"].values

print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)

# ========== output ==========
# ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']
# (397, 10) (397,)
# (45, 10) (45,)

Xがモデルに入れる特徴量、yが答えです。

モデル作成

import xgboost as xgb

# データ形式の変換
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)

# パラメータ設定
# regression: 回帰, squarederror: 二乗誤差
params = {"objective": "reg:squarederror"}

# 学習
model = xgb.train(
    params = params,
    dtrain = dtrain,
    evals = [(dtrain, "train"), (dtest, "test")],
)

xgboostは学習と検証を繰り返し、徐々に精度を上げていくモデルです。

なので、上図のように各回での学習データと検証データでの精度が出力されます。

rmse(平均二乗誤差の平方根)が改善されていることがわかりますね。

回帰モデルを作りたいなら”objective”は”reg:squarederror”にします。

パラメータの項目についての詳細は公式ドキュメントを見るといいですよ。

予測結果

pred = model.predict(dtest)
print(pred[:5])
print(y_test[:5])

# ========== output ==========
# [237.90706  84.91177  77.13369 219.08762 129.09122]
# [230.  97.  93. 220. 200.]

predictで予測できます。だいたい正解していそうですね。

誤差を計算してみましょう。

import numpy as np
from sklearn.metrics import mean_squared_error
print(np.sqrt(mean_squared_error(y_test, pred)))

# ========== output ==========
# 66.3967555643605

誤差はおおよそ66.4でした。

学習状況可視化

import xgboost as xgb
import matplotlib.pyplot as plt

# データ形式の変換
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)

# パラメータ設定
# regression: 回帰, squarederror: 二条誤差
params = {"objective": "reg:squarederror"}

# 履歴保存用の変数
history = {}

# 学習
model = xgb.train(
    params = params,
    dtrain = dtrain,
    evals = [(dtrain, "train"), (dtest, "test")],
    evals_result = history, # ここに渡す
)


plt.plot(history["train"]["rmse"], label = "train")
plt.plot(history["test"]["rmse"], label = "test")
plt.legend()
plt.show()

“evals_result”にカラの辞書型データを渡せば、学習の履歴を保存できます。

青が学習データでの結果なので、過学習していますね。

過学習抑制

import xgboost as xgb
import matplotlib.pyplot as plt

# データ形式の変換
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)

# パラメータ設定
params = {
    "objective": "reg:squarederror",
    "max_depth" : 1, # 決定木の深さ
    }

# 履歴保存用の変数
history = {}

# 学習
model = xgb.train(
    params = params,
    dtrain = dtrain,
    evals = [(dtrain, "train"), (dtest, "test")],
    evals_result = history,
    num_boost_round = 100, # 学習回数
)


plt.plot(history["train"]["rmse"], label = "train")
plt.plot(history["test"]["rmse"], label = "test")
plt.legend()
plt.show()

“max_depth”を小さくすると過学習を抑制できます。

“num_boost_round”を増やして学習回数も増やしています。

early_stopping_rounds

“num_boost_round”を増やせばいくらでも学習を繰り返せますが、どこかで頭打ちします。

なので、無駄な時間を作らないように、”early_stopping_rounds”を設定しましょう。

import xgboost as xgb
import matplotlib.pyplot as plt

# データ形式の変換
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)

# パラメータ設定
params = {
    "objective": "reg:squarederror",
    "max_depth" : 1, # 決定木の深さ
    }

# 履歴保存用の変数
history = {}

# 学習
model = xgb.train(
    params = params,
    dtrain = dtrain,
    evals = [(dtrain, "train"), (dtest, "test")],
    evals_result = history,
    num_boost_round = 100,      # 学習回数
    early_stopping_rounds = 10, # 打ち切り条件
)


plt.plot(history["train"]["rmse"], label = "train")
plt.plot(history["test"]["rmse"], label = "test")
plt.legend()
plt.show()

“early_stopping_rounds”を10にすると、10回検証データでの精度が改善されないと止まります。

50回目くらいで打ち切られていますね。

“num_boost_round”を大きめにして、”early_stopping_rounds”を設定すると無難です。

特徴量重要度

xgb.plot_importance(model)
plt.show()

plot_importanceに作成したモデルを入れると、特徴量の重要度を出してくれます。

ただデフォルトでは、どれだけ予測に寄与したかを示すわけではないようです。

予測への寄与度を知りたいなら、“importance_type”を”gain”に変えましょう。

xgb.plot_importance(model, importance_type = "gain")
plt.show()

どちらの結果にせよ、”s5″と”bmi”が重要みたいですね。

このデータセットは糖尿病に関するものらしいので、”bmi”が効くのも納得です。

二値分類モデル

データセット

imimport pandas as pd
from sklearn.datasets import load_breast_cancer

data = load_breast_cancer()
df = pd.DataFrame(data["data"], columns = data["feature_names"])
df["target"] = data["target"]
df

見切れていますが、30列の特徴量から”target”が0か1かを分類しましょう。

データ分割

from sklearn.model_selection import train_test_split

# stratifyに設定したデータが均一になるように分割
train, test = train_test_split(df, test_size = 0.1, stratify = df["target"])

features = [c for c in df.columns if c != "target"]
print(len(features))

#学習用データ
X_train = train[features]
y_train = train["target"].values

#検証用データ
X_test = test[features]
y_test = test["target"].values

print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)

# ========== output ==========
# 30
# (512, 30) (512,)
# (57, 30) (57,)

やっていることは回帰モデルとほとんど同じなので割愛します。

“train_test_split”の”stratify”を設定して、”target”が均一になるようにしています。

例えば学習用データに”target = 1″が入っていないと、すべてを0と予測するモデルになるからです。

モデル作成

import xgboost as xgb
import matplotlib.pyplot as plt

# データ形式の変換
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)

# パラメータ設定
# binary: 二値分類
params = {
    "objective": "binary:logistic",
    }

# 履歴保存用の変数
history = {}

# 学習
model = xgb.train(
    params = params,
    dtrain = dtrain,
    evals = [(dtrain, "train"), (dtest, "test")],
    evals_result = history,
    num_boost_round = 100,
    early_stopping_rounds = 10,
)

plt.plot(history["train"]["logloss"], label = "train")
plt.plot(history["test"]["logloss"], label = "test")
plt.legend()
plt.show()

主に回帰モデルと違うところは、パラメータ設定です。

“objective”を”binary:logistic”にしましょう。

予測結果

pred = model.predict(dtest)
print(pred[:5])
print(y_test[:5])

# ========== output ==========
# [9.9614751e-01 2.9195013e-04 9.9872178e-01 9.9986887e-01 4.2042270e-04]
# [1 0 1 1 0]

predictで予測すると、ラベルが1である確率を出力します。

なので、01のラベルとして結果を得るには、四捨五入したり、閾値を決める必要があります。

pred = model.predict(dtest).round()
print(pred[:5])
print(y_test[:5])

# ========== output ==========
# [1. 0. 1. 1. 0.]
# [1 0 1 1 0]

正解していそうですね。

正解率も計算してみましょう。

from sklearn.metrics import accuracy_score
print(accuracy_score(y_test, pred))

# ========== output ==========
# 0.9824561403508771

正解率は98%でした。

多クラス分類モデル

データセット

import pandas as pd
from sklearn.datasets import load_iris

data = load_iris()
df = pd.DataFrame(data["data"], columns = data["feature_names"])
df["target"] = data["target"]
df

“load_iris”は花の種類を0,1,2の3種類で分類するデータセットです。

データ分割

from sklearn.model_selection import train_test_split

# stratifyに設定したデータが均一になるように分割
train, test = train_test_split(df, test_size = 0.1, stratify = df["target"])

features = [c for c in df.columns if c != "target"]
print(features)

#学習用データ
X_train = train[features]
y_train = train["target"].values

#検証用データ
X_test = test[features]
y_test = test["target"].values

print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)

# ========== output ==========
# ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
# (135, 4) (135,)
# (15, 4) (15,)

今回も”train_test_split”で”stratify”を設定して分割しました。

モデル作成

import xgboost as xgb
import matplotlib.pyplot as plt

# データ形式の変換
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)

# パラメータ設定
# binary: 二値分類
params = {
    "objective" : "multi:softprob",
    "num_class": 3,
    }

# 履歴保存用の変数
history = {}

# 学習
model = xgb.train(
    params = params,
    dtrain = dtrain,
    evals = [(dtrain, "train"), (dtest, "test")],
    evals_result = history,
    num_boost_round = 100,
    early_stopping_rounds = 10,
)

plt.plot(history["train"]["mlogloss"], label = "train")
plt.plot(history["test"]["mlogloss"], label = "test")
plt.legend()
plt.show()

“objective”を”multi:softprob”にしましょう。

次に”num_class”にラベル数を渡します。今回は3ですね。

途中で過学習が起きて検証データでの精度が落ちています。

ただ、モデルは検証データで最も性能の良い状態を保存しているので、このままでOKです。

予測結果

pred = model.predict(dtest)
print(pred[:5])
print(y_test[:5])

# ========== output ==========
# [[0.00542975 0.97914153 0.01542874]
# [0.9911123  0.00577696 0.00311077]
# [0.0250621  0.15813866 0.8167992 ]
# [0.00334191 0.00430827 0.9923498 ]
# [0.00829999 0.05322199 0.938478  ]]
# [1 0 2 2 2]

predictで予測すると、各ラベルに該当する確率を出力します。

「データの行数 x 3」の配列で、左からラベル0,1,2の確率です。

pred = model.predict(dtest).argmax(axis = 1)
print(pred[:5])
print(y_test[:5])

# ========== output ==========
# [1 0 2 2 2]
# [1 0 2 2 2]

argmaxを使うと”値が最も大きいデータの位置”が出力されます。

正解していそうですね。

from sklearn.metrics import accuracy_score
print(accuracy_score(y_test, pred))

# ========== output ==========
# 0.9333333333333333

正解率を計算しました。だいたい93%くらいです。

まとめ

今回はXGBoostの実装方法を解説しました。

XGBoostが使えるようになったら、LightGBMも勉強するといいですよ。

>>【無料説明会あり】キカガクのAI人材育成コースで勉強する

コメント

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