21
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

XGBoostをoptunaでチューニングしてtitanicを解く

Last updated at Posted at 2019-02-16

はじめに

titanic号の乗客のうち、生き残るのは誰かをXGBoostを使って予測した。
その際、ハイパーパラメータはoptunaで自動チューニングした。
titanic号の乗客のデータセットはKaggleからダウンロードしました。

実行環境

  • python 3.6.6
  • XGBoost 0.81
  • optuna 0.6.0

実装

方針

  1. xgboostだけ。自分で決める必要があるパラメータは適当に決める
  2. optunaで(1)でやったパラメータをチューニングする
  3. パラメータを増やす
    データは891人分あるので、(学習データ:テストデータ)=(791:100)でやる。

ライブラリ

import
import pandas as pd
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

データ前処理

preprocess
def preprocess(df):
    df['Fare'] = df ['Fare'].fillna(df['Fare'].mean())
    df['Age'] = df['Age'].fillna(df['Age'].mean())
    df['Embarked'] = df['Embarked'].fillna('Unknown')
    df['Sex'] = df['Sex'].apply(lambda x: 1 if x == 'male' else 0)
    df['Embarked'] = df['Embarked'].map( {'S':0,'C':1,'Q':2,'Unknown':3} ).astype(int)
    df = df.drop(['Cabin','Name','PassengerId','Ticket'],axis=1)
    return df

引数dfのデータセットを正規化して返す関数です。
学習に利用したデータは

  • Survived (生存したかどうか)
  • Pclass (乗客の階級)
  • sex
  • Age
  • SibSp (兄弟、配偶者の数)
  • Parch (両親、子供の数)
  • Fare (乗船料金)
  • Embarked (乗船した港)
    です。
    使わないデータはdropしています。
    必要なデータの欠損データは平均で埋めて、型はINT型に直してます。

方針1(xgboostのみ)

学習(xgboost)

まずはxgboostだけのもの

train
def train(df,max_depth,learning_rate,num_round):
    train_x = df.drop('Survived',axis=1)
    train_y = df.Survived
    dtrain = xgb.DMatrix(train_x,label=train_y)
    param = { 'max_depth':max_depth,'learning_rate':learning_rate,'objective':'reg:logistic' }
    bst = xgb.train(param,dtrain,num_round)
    return bst

学習モデルbstを作成して返す関数です。
引数の説明

  • df : 学習用データセット
  • max_depth : ツリーの最大の深さ
  • learning_rate : 学習率
  • num_round : ブースティングを行う回数

Paramのobjectiveで最小化させる損失関数を指定します。今回は、reg:logistic(logistic回帰)を指定しています。

学習モデルの予測値を返す関数

predict
def predict(bst,df):
    return bst.predict(xgb.DMatrix(df))

名前の通りです。

main関数

main.py
def main():
    df_original = pd.read_csv("/Users/pc1013/Desktop/first/XGBoost_optuna/df/train.csv")
    df_test = preprocess(df_original.tail(100))
    df_train = preprocess(df_original.head(791))
    y = df_test['Survived']
    df_test = df_test.drop('Survived',axis=1)
        
    max_depth = 3
    learning_rate = 0.6
    round_num = 5
    
    bst = train(df_train,max_depth,learning_rate,round_num)
    answer = predict(bst,df_train.drop('Survived',axis=1)).round().astype(int)
    print('train score :',accuracy_score(answer.round(),df_train['Survived']))
    answer = predict(bst,df_test).round().astype(int)
    xgb.plot_importance(bst)
    print('test score :',accuracy_score(answer.round(),y))

main関数です。
とりあえず、ハイパーパラメータは

  • max_depth = 3
  • learning_rate = 0.6
  • num_round = 5

としてみます。

方針2(xgboost+optuna)

optunaで自動チューニングする時に必要なのが、

  • 目的関数を最適化するobjective()関数
  • optunaの準備とobjective()の実行(main関数の変更)

objective()について

objective
def objective(df, df_test, y,trial):
    #目的関数
    max_depth = trial.suggest_int('max_depth',1,30)
    learning_rate = trial.suggest_uniform('learning_rate',0.0,1)
    round_num = trial.suggest_int('round_num',1,30)

    bst = train(df,max_depth,learning_rate,round_num,gamma,min_childe_weigh,subsample,colsample_bytree,alpha,lamb)
    answer = predict(bst,df_test).round().astype(int)
    score = accuracy_score(answer.round(),y)
    return 1.0 - score

max_depthとround_numは[1,30]の整数値
learning_rateは[0.0,1.0]の少数値
と言う範囲でオートチューニングします。
評価は毎回テストデータで行うので、7行目predict(bst,df_test)は引数にdf_testを入れてます。

main()

main.py
def main():
    df_original = pd.read_csv("/Users/pc1013/Desktop/first/XGBoost_optuna/df/train.csv")
    df_test = preprocess(df_original.tail(100))
    df_train = preprocess(df_original.head(791))
    y = df_test['Survived']
    df_test = df_test.drop('Survived',axis=1)
    
    #optunaの前処理
    obj_f = partial(objective, df_train, df_test, y)
    #セッション作成
    study = optuna.create_study()
    #回数
    study.optimize(obj_f, n_trials=100)
    
    max_depth = study.best_params['max_depth']
    learning_rate = study.best_params['learning_rate']
    round_num = study.best_params['round_num']
    
    bst = train(df_train,max_depth,learning_rate,round_num)
    print('\nparams :',study.best_params)
    answer = predict(bst,df_train.drop('Survived',axis=1)).round().astype(int)
    print('train score :',accuracy_score(answer.round(),df_train['Survived']))
    answer = predict(bst,df_test).round().astype(int)
    xgb.plot_importance(bst)
    print('test score :',accuracy_score(answer.round(),y))

変更は8行目から。
obj_fではobjective()の引数を与えています。
n_trialはチューニングの試行回数で、
10から増やしいき50くらいから、増やしても変化があまりみられなかったので、今回は100回としてみました。

study.best_params[element]でハイパーパラメータ(element)のチューニング結果を得られます。
これを使って、モデルを作成し、評価します。

方針3(optunaで沢山パラメータチューニング)

新たに、今までデフォルトだったパラメータたち

  • alpha
  • gamma
  • min_childe_weigh
  • colsample_bytree

をチューニングします。
それぞれのパラメータについては、調べてもなんとなくしかわかりませんでした。
subsampleとlambはか学習を防ぐためのパラメータですが、モデルが過学習しそうになかったため除外して
subsample = 1
lanb = 1

objectice()とmain()については、パラメータ増やすだけなので、コードは省略。
パラメータの範囲だけ⬇︎

  • gamma - [0.0,1.0]
  • subsample - [0.0,1.0]
  • colsample_bytree - [0.0,1.0]
  • min_childe_weigh - [0.0,1.0]
  • alpha - [0.0,1.0]

実行結果

結果1(xgboostのみ)

精度は、85.0%
学習データ精度85.5%→過学習なし。
寄与率
titanic_1.png

結果2(xgboost+optuna)

精度は、89.0%
学習データ精度94.5%→過学習なし?
寄与率
titanic_2.png

結果3(xgboost+optuna+沢山パラメータ)

精度は、89.0%
学習データ精度88.2%→過学習なし。
寄与率
titanic_3-0.png

trial回数100
xgboostのアルゴリズムをbinary:logistic(ロジスティック2項分類)にして実行したところ、
精度97.5%が出ました。
9割超えた!
寄与率
titanic_3-1.png

まとめ

XGBoostが既にすごい。
optuna使うとちょっと精度上がる。
パラメータ増やすと安定する。

追記

過ちに気づいたので、追記.
本記事では、データを
「 データ → 学習データ + テストデータ 」
と分割し、テストデータを使った損失関数の出力から、パラメータをチューニングしていた.
しかし、これをテストデータでテストすると、本来より精度が高く出る可能性がある.ハイパーパラメータがオーバーフィットする可能性があるからだ.

よって、正しく精度を測るには
「 データ → 学習データ + 検証データ + テストデータ 」
というように分割し、パラメータチューニングは検証データで行い、チューニング後にテストデータで精度を測る必要がある.

21
18
0

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
  3. You can use dark theme
What you can do with signing up
21
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?