前回の続きです。
今回は、ハイパーパラメータのチューニングを行いました。
前回の記事はこちら。
もう、「超初歩」っていう感じじゃなくなってきたので、今回のタイトルからは「超初歩」の文言を外しました。
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.」は前回と同じソースです(^ワ^*)
今回ここでは、
- モジュール読み込み
- データ読み込み
- 数値特徴量のカテゴリ変数化
- 特徴量の追加と削除
- One-hot encoding
- 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 | 記事未作成 |