1
2

More than 1 year has passed since last update.

[kaggle / python] 回帰問題(house prices)に取り組む(6)~ハイパーパラメータのチューニング~

Last updated at Posted at 2022-06-26

前回の続きです。
今回は、ハイパーパラメータのチューニングを行いました。

前回の記事はこちら。

もう、「超初歩」っていう感じじゃなくなってきたので、今回のタイトルからは「超初歩」の文言を外しました。

the previous and today's result(前回と今回の結果)

やったこと score
前回 カテゴリ特徴量に対するOne hot Encodingの適用 0.14835
今回 ハイパーパラメータのチューニング(optuna) 0.13723

なんかすごい改善した。

Why?

ハイパーパラメータによって精度はかなり変わります。
なので、ハイパーパラメータのチューニングは、どのモデルだとしてもやって損はないと思います。(説明が雑でごめんなさい(´;ω;`))

さて、実際にやったことを見ていきます。

1. source cord

1-0. same as the previous cord

いつも通り、この「1-0.」は前回と同じソースです(^ワ^*)
今回ここでは、

  1. モジュール読み込み
  2. データ読み込み
  3. 数値特徴量のカテゴリ変数化
  4. 特徴量の追加と削除
  5. One-hot encoding
  6. trainingデータとtestデータの分割

まで行っています。

# 1. import modules
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from xgboost import XGBClassifier, XGBRegressor

# 2. load data
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
train = pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/train.csv')
test = pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/test.csv')

# 3. cast columns
train['MSSubClass'] = train['MSSubClass'].apply(str)
train['YrSold'] = train['YrSold'].astype(str)
train['MoSold'] = train['MoSold'].astype(str)
test['MSSubClass'] = test['MSSubClass'].apply(str)
test['YrSold'] = test['YrSold'].astype(str)
test['MoSold'] = test['MoSold'].astype(str)

# 4-1. add new column
train['totalRoomsSF'] = train['1stFlrSF'] + train['2ndFlrSF'] + train['TotalBsmtSF']
test['totalRoomsSF'] = test['1stFlrSF'] + test['2ndFlrSF'] + test['TotalBsmtSF']

# 4-2. drop ID column
train_ID = train['Id']
test_ID = test['Id']
train = train.drop(['Id'], axis=1)
test = test.drop(['Id'], axis=1)

# 5-1. 全保有データを縦方向に結合
## 一度全データを縦方向に連結するので、
## 事前に、train(学習データ)とtest(提出用データ作成のための特徴量)を識別するための
## 'for_train'カラムを追加しておく
train['for_train'] = True
test['for_train'] = False

## データを縦方向に結合
df_all_data = pd.concat([train, test])

# 5-2. One hot Encdoging
# NOTE: 今回One hot Encodingにかける、object型の列を特定
encode_target_columns = df_all_data.columns[df_all_data.dtypes == 'object']

## One hot Encoding適用
encoded_all_data = pd.get_dummies(df_all_data, columns=encode_target_columns)

# 6-1. split into 'train' and 'test'
encoded_train = encoded_all_data.loc[encoded_all_data['for_train'], :]
encoded_test = encoded_all_data.loc[~encoded_all_data['for_train'], :]

# 6-2. split data into explanatory variable(説明変数) and response variable(目的変数)
tmp_train_y = encoded_train['SalePrice']
encoded_train_x = encoded_train.drop(['for_train', 'SalePrice'], axis=1)
encoded_test = encoded_test.drop(['for_train', 'SalePrice'], axis=1)

# 6-3. split data into training data and test data
x_train, x_test, y_train, y_test = \
    train_test_split(encoded_train_x, tmp_train_y, test_size=0.20, random_state=0)

1-1. Newly added lines

今回新たに実装した or 変更を加えたのは以下の部分です

7.最適なハイパーパラメータの探索
8.学習model作成(& 検証)

さて、前処理、データ分割まで終えた後で、今回新しくハイパーパラメータのチューニングを行いました。

学習データと相性の良いパラメータを探したいはずなので、ハイパーパラメータのチューニングを行うタイミングは、全ての前処理が終わった後、学習の直前としました。

実装はほぼこちらの記事の通りです。

# 7. 最適なハイパーパラメータの探索
import time
from sklearn.model_selection import KFold, cross_val_score
import optuna

seed = 42  # 乱数シード
# NOTE: ハイパーパラメータのチューニング時に用いるXGBoostモデル
model = XGBRegressor(
    n_estimators=20, random_state=seed,
    eval_metric='rmse',
    early_stopping_rounds=5,
)
cv = KFold(n_splits=3, shuffle=True, random_state=seed)
scoring = 'neg_mean_squared_error'
# 学習時fitパラメータ指定
fit_params = {
    'verbose': 0,  # 学習中のコマンドライン出力
    #'early_stopping_rounds': 5,  # 学習時、評価指標がこの回数連続で改善しなくなった時点でストップ
    #'eval_metric': 'rmse',  # early_stopping_roundsの評価指標
    'eval_set': [(x_test, y_test)],  # early_stopping_roundsの評価指標算出用データ
}
start = time.time()

# ベイズ最適化時の評価指標算出メソッド
def bayes_objective(trial):
    params = {
        # 'n_estimators': trial.suggest_int('n_estimators', 10, 100),
        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3, log=True),
        'min_child_weight': trial.suggest_int('min_child_weight', 3, 8),
        'max_depth': trial.suggest_int('max_depth', 2, 4),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.2, 1.0),
        'subsample': trial.suggest_float('subsample', 0.2, 1.0),
        'reg_alpha': trial.suggest_float('reg_alpha', 0.001, 0.1, log=True),
        'reg_lambda': trial.suggest_float('reg_lambda', 0.001, 0.1, log=True),
        'gamma': trial.suggest_float('gamma', 0.0001, 0.01, log=True),
    }
    # モデルにパラメータ適用
    model.set_params(**params)
    # cross_val_scoreでクロスバリデーション
    scores = cross_val_score(
        model, x_train, y_train, cv=cv,
        scoring=scoring, fit_params=fit_params,
        n_jobs=-1
    )
    val = scores.mean()
    return val

# ベイズ最適化を実行
study = optuna.create_study(
    direction='maximize',
    sampler=optuna.samplers.TPESampler(seed=seed)
)
study.optimize(bayes_objective, n_trials=200)

# 最適パラメータの表示と保持
best_params = study.best_trial.params
best_score = study.best_trial.value
print(f'最適パラメータ {best_params}\nスコア {best_score}')
print(f'所要時間{time.time() - start}')

ここで求まったハイパーパラメーターである best_params を使って、 xgboost の学習から結果の提出まで行っていきます!

best_params

# 出力
{'learning_rate': 0.19655973362890475,
 'min_child_weight': 8,
 'max_depth': 4,
 'colsample_bytree': 0.7154187972679216,
 'subsample': 0.9242436226210999,
 'reg_alpha': 0.009030975784551901,
 'reg_lambda': 0.039104226426611016,
 'gamma': 0.0005399897992324197}
# 8. 学習model作成(& 検証)
# n_estimatorsを 20 から 200 に増やしたら精度が上がったので、上げておいた
model = XGBRegressor(n_estimators=200, random_state=0, **best_params)
model.fit(x_train, y_train, eval_set=[(x_train, y_train), (x_test, y_test)])

#trainデータに対してのloss推移をplot
plt.plot(model.evals_result()['validation_0']['rmse'], label='train rmse')
#testデータに対してのloss推移をplot
plt.plot(model.evals_result()['validation_1']['rmse'], label='eval rmse')
plt.grid()
plt.legend()
plt.xlabel('rounds')
plt.ylabel('rmse')
plt.show()

# 学習結果 > [199]	validation_0-rmse:5320.75537	validation_1-rmse:27679.82422

# NOTE:
# n_estimators = 20 にしたときの精度は以下の通り
# 学習結果 > [19]	validation_0-rmse:18346.21094	validation_1-rmse:31334.98242

第5回の検証結果と比べると、

評価用データ 第5回RMSE 今回RMSE
trainingデータ 8181.078... 5320.755...
validationデータ 35119.731... 27679.824...

という結果に。
今回、大幅に精度改善しているのがわかります。

1-2. submission

さて、提出しましょう。

# 9. create csv for submission
predict_submission = model.predict(encoded_test)
submission = pd.DataFrame(
    {'Id': test['Id'], 'SalePrice': predict_submission}
)
submission.to_csv('submission.csv', index=False)

この結果、スコアは0.14835から0.13723へと改善されました。
かなりの改善に繋がりました...(; ・`д・´)

Discussion (考察)

今回の変更は、

  • Optunaで見つけたハイパーパラメータを利用
    Optunaで探索させる範囲は地道に狭めることで、より良いパラメータを探させた
  • たまたま見つかったそこそこ良いn_estimatorsを利用

という2点でした。

それだけでかなり改善したので、ハイパーパラメータってやっぱり重要なんですね、、、と改めて感じました。

そして順位については、前回まで上位60%だったのに、今回遂に上位50%を切れたので、ちょっと小躍りしてます(((o(*゚▽゚*)o)))

Next step

次は、目的変数を対数変換して学習させ、精度改善を試みた結果を記事にします。

References

今回参考にした記事です。

Optunaだけでなく、ハイパーパラメータ探索の様々な手法を取り上げて下さっています。

シリーズ一覧

No New Trial Score Link Note
1 xgboost 0.15663 こちら
2 Label Encoding 0.15160 こちら
3 Add and delete column 0.15140 こちら
4 Make integer into categorical 0.14987 こちら
5 One hot Encoding 0.14835 こちら
6 Hyper parameters tuning 0.13723 本記事
7 Logarithimic transformation 0.13347 記事未作成
1
2
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
1
2