Python 「PyCaret」ハイパーパラメータの自動チューニング【カスタムパラメータ設定の方法】

 PyCaretにはハイパーパラメータのチューニングを自動で行う機能が備わっています。一方、その項目と範囲を手動で設定することも出来ます。本記事では、そのカスタム設定に加えて自動設定した場合とで得られたハイパーパラメータの比較、適合度の比較とその雛形コードを載せました。

import numpy as np
import random
columns_n = train_x_df.shape[1] # 特徴量の数(入力因子xの列数)
val_list = list(range(2, int(columns_n * 0.85), 1))
max_depth_list = random.sample(val_list, int(columns_n*0.5)) 
# カスタムパラメータ(ランダムフォレストの場合)
params = {#"n_estimators": [10, 100, 200], # default 100
          #"criterion": ["mse", "mae"], # default mse
          "max_depth": max_depth_list,
          "min_samples_leaf": [1, 2, 4], # default 1
          #"max_features": ["auto", "sqrt"], # “auto”, “sqrt”, “log2”
}
tuned_reg = tune_model(reg2,
                       custom_grid = params,
                       n_iter = 20, #default 10
                       optimize = 'MAE', #MAE, MSE, RMSE, RMSLE, R2 and MAPE
                       verbose = False)

 

■以降、本コードの実施例です。

 本コードで分析したデータは、機械学習の回帰分析用でお馴染みのボストンデータセットcsvファイルを使用した。csvファイルの作成例は次のリンクです。Python scikit-learn付属のボストン市の住宅価格データ(Boston house prices dataset)をcsvファイル化する - PythonとVBAで世の中を便利にする

▼ランダムフォレスト回帰のハイパーパラメータの自動調整(PyCaretのコードによる)
 下図左は、ハイパーパラメータを自動で調整した場合で、下図右は調整するハイパーパラメータの項目とその探索範囲を指定して自動調整しました。その結果、両者の調整されたパラメータは異なっていますが、回帰モデルの適合度R2乗は85%を超えています。

f:id:HK29:20200920221014p:plain

▼当てはまりの良さのチェック
 生データと予測値を散布図でプロットして、視覚的にも当てはまりの良さのチェックをします。横軸は説明変数のひとつRM(部屋数)で縦軸はボストンの住宅価格です。黒〇は生データで、赤〇はハイパーパラメータ自動調整によって得た回帰モデルによる予測値で、青〇はハイパーパラメータの探索範囲を手動で設定して自動調整によって得た回帰モデルによる予測値です。両者ともに、見た目にもあてはまりは良く、非線形回帰分析による威力です。

f:id:HK29:20200920221409p:plain

▼特徴量の感度順(PyCaretのコードによる)
 説明変数(入力因子)を影響度が大きい順に並べてくれます。

f:id:HK29:20200920221130p:plain

▼SHAP Value:シャープレイ値(PyCaretのコードによる)
 特徴量について、上記の感度の他にその方向も視覚的に教えてくれる手法です。横軸は目的変数の住宅価格です。例えば、説明変数のひとつRM(部屋数)を見ると、赤色(High)は図中の右寄りに集まっています。この意味は、部屋数が増えると住宅価格は上がる傾向であるということです。一方、CRIMは犯罪率です。赤色が中心より左寄りです。この意味は、犯罪率が高い地域だと住宅価格は下がる傾向にあるということです。

f:id:HK29:20200920221306p:plain

▼行列散布図
 上図、ランダムフォレストによって導かれた特徴量の感度の高い順に、行列散布図でプロットした図です。目的変数と説明変数の関係がわかります(全部の説明変数でこれをやると、画像が非常に小さく何が何だかわからない)。また、説明変数間にも依存がみられるものがあり、これを交互作用(相互作用)と呼びます。これは自然の摂理で、何かしら互いに関係があるのは珍しくなく、線形の回帰分析で行うには無理がある理由でもあります。

f:id:HK29:20200920221341p:plain

■本プログラム
 本コードは、Jupyter Labで作成したコードを.pyファイルで出力し、コマンドラインで動作するように一部編集したファイルです(PyCaretの主要メソッドにおいて引数verbose=False等)。csvファイルと目的変数を指定すれば汎用的には使えるかと思います。

#!/usr/bin/env python
# coding: utf-8
import matplotlib
matplotlib.use('Agg') # バックグラウンドで画像生成などする宣言

# In[1]:


# csvファイルをpandasのDataFrame型で読み込む
import os
import pandas as pd
file_path = './boston_dataset.csv'
file_name = os.path.splitext(os.path.basename(file_path))[0]
df = pd.read_csv(file_path,
                 header=0,
                 encoding='utf-8')

# In[2]:


# 基本統計量を取得する
describe_data = df.describe()
# csvファイルに保存する
describe_data.to_csv(file_name + '_describe.csv',
                     sep=',',
                     index=True,
                     encoding='utf-8')


# In[3]:


# ヒストグラムを作成する
import matplotlib.pyplot as plt
plt.subplots_adjust(left=0.1, right=0.95, bottom=0.1, top=0.95)
df.hist() # ヒストグラムを表示する
plt.tight_layout() # グラフ表示を整える
plt.savefig('03_hist.png')
plt.close()


# In[4]:


# 特徴量の名前をリスト化
mytarget = 'PRICE'
feature_name_list = df.columns.values.tolist()
feature_name_list.remove(mytarget)


# In[5]:


# PyCaret用のデータセット生成(前処理)
from pycaret.regression import *
mydata = setup(data = df,
               normalize = False, # 標準化(平均0の分散1)にするかどうか
               #ignore_features = ['ZN', 'CHAS', B'],
               train_size = 0.7, # 訓練データの割合
               session_id = 1, # ランダムシード
               target = mytarget,               
               #categorical_features=[''], # カテゴリ型を列名で指定               
               numeric_features = feature_name_list, # 数値型を列名で指定
               silent=True) # 型チェックの手動確認のため一時ストップするか


# In[6]:


# 訓練データの確認
train_x_df = mydata[2]
print(train_x_df)


# In[7]:


# テストデータの確認
test_x_df = mydata[3]
print(test_x_df)


# In[8]:


# モデルオブジェクト作成する
reg1 = create_model('rf',
                    verbose = False)


# In[9]:


# ハイパーパラメータをデフォルト範囲内で自動調整する
auto_tuned_reg = tune_model(reg1,
                            fold = 10, #default
                            round = 4, #default
                            n_iter = 10, #default
                            optimize = 'R2', #default
                            verbose = False)


# In[10]:


# 学習過程を確認する。Validation Curve
plot_model(auto_tuned_reg, plot = 'vc')
plt.savefig('10_auto_tuned_reg_vc.png')
plt.close()


# In[11]:


# Prediction Error Plot
plot_model(auto_tuned_reg, plot = 'error')
plt.savefig('11_auto_tuned_reg_error.png')
plt.close()


# In[12]:


# 自動調整されたハイパーパラメータを確認する
#evaluate_model(auto_tuned_reg)
plot_model(auto_tuned_reg, plot = 'parameter')


# In[13]:


# 調整するハイパーパラメータの項目とその範囲を手動で指定する場合
reg2 = create_model('rf',
                    ensemble = False, #default
                    method = None, #default
                    fold = 10, #default
                    round = 4, #default
                    cross_validation = True, #default
                    verbose = False)
import numpy as np
import random
columns_n = train_x_df.shape[1] # 特徴量の数(入力因子xの列数)
val_list = list(range(2, int(columns_n * 0.85), 1))
max_depth_list = random.sample(val_list, int(columns_n*0.5)) 
# カスタムパラメータ(ランダムフォレストの場合)
params = {#"n_estimators": [10, 100, 200], # default 100
          #"criterion": ["mse", "mae"], # default mse
          "max_depth": max_depth_list,
          "min_samples_leaf": [1, 2, 4], # default 1
          #"max_features": ["auto", "sqrt"], # “auto”, “sqrt”, “log2”
}
tuned_reg = tune_model(reg2,
                       custom_grid = params,
                       n_iter = 20, #default 10
                       optimize = 'MAE', #MAE, MSE, RMSE, RMSLE, R2 and MAPE
                       verbose = False)


# In[14]:


# 学習過程を確認する。Validation Curve
plot_model(tuned_reg, plot = 'vc')
plt.savefig('14_tuned_reg_vc.png')
plt.close()


# In[15]:


# Prediction Error Plot
plot_model(tuned_reg, plot = 'error')
plt.savefig('15_tuned_reg_error.png')
plt.close()


# In[16]:


# Residuals Plot
plot_model(tuned_reg, plot = 'residuals')
plt.savefig('16_tuned_reg_residuals.png')
plt.close()


# In[17]:


# 調整されたハイパーパラメータを確認する
#evaluate_model(tuned_reg)
plot_model(tuned_reg, plot = 'parameter')


# In[18]:


# Feature Importance
plot_model(tuned_reg, plot = 'feature')
plt.savefig('18_tuned_reg_feature.png')
plt.close()


# In[19]:


# 特徴量のサマリー
interpret_model(tuned_reg)
plt.savefig('19_tuned_reg_feature_summary.png')
plt.close()


# In[20]:


# correlation 
interpret_model(tuned_reg, plot = 'reason', observation = 10)


# In[21]:


import seaborn as sns
# 列名を指定してデータ抽出
# →全部のカラム名をプロットするとグラフが非常に小さくなるので項目を絞るため
df2 = df.loc[:, ['RM', 'CRIM', 'LSTAT', 'PTRATIO', 'PRICE']]
# 行列散布図
# 目的変数と説明変数の関係、説明変数間の関係が視覚的にわかる。
sns.set_context('talk')
mypairplot = sns.pairplot(df2,
                          kind="reg",
                          markers=".",
                          diag_kind="kde",
                          diag_kws=dict(shade=True),)
plt.savefig('21_pairplot.png')
plt.close()

# In[22]:


# 未知データを用いて予測する(ここではテストデータを用いて)
# ハイパーパラメータを自動調整したモデル
final_reg1 = finalize_model(auto_tuned_reg) # モデルをファイナライズする
predict_df1 = predict_model(final_reg1, data=test_x_df) # 回帰モデルを用いて予測
# ハイパーパラメータの項目と範囲を指定して自動調整したモデル
final_reg2 = finalize_model(tuned_reg) # モデルをファイナライズする
predict_df2 = predict_model(final_reg2, data=test_x_df) # 回帰モデルを用いて予測

# テストデータのインデックスを取得
myindex = test_x_df.index
# 予測データのインデックスをテストデータのインデックスで置換
predict_df1.set_index(myindex, inplace=True)
predict_df2.set_index(myindex, inplace=True)
# 全データからテストデータのxyデータを取得
test_xy_df = df.iloc[test_x_df.index]

# 列名を変更する
predict_df1 = predict_df1.rename(columns={'Label': 'PREDICT_by_auto_tuned_model'})
predict_df2 = predict_df2.rename(columns={'Label': 'PREDICT_by_custom_tuned_model'})

# データフレームを列方向(横方向)へ結合する
DF = pd.concat([test_xy_df, 
                 predict_df1.loc[:, 'PREDICT_by_auto_tuned_model'],
                 predict_df2.loc[:, 'PREDICT_by_custom_tuned_model']],
                 axis=1) # テストデータと予測データを列方向へ結合


# In[23]:


# テストデータによる生データと予測1, 予測2との相関性の確認
x_name = 'RM'
y_name = 'PRICE'
fig = plt.figure(dpi=120)
ax = fig.add_subplot(1,1,1, facecolor='lightyellow') #'azure' 'lightyellow'
ax.set_title('test_data', fontsize=14)
ax.set_xlabel(x_name, fontsize=16)
ax.set_ylabel(y_name + '(target)', fontsize=16)
ax.scatter(DF.loc[:,x_name], DF.loc[:,y_name],
           facecolors='none', edgecolors='k', label='Raw data')
ax.scatter(DF.loc[:,x_name], DF.loc[:,'PREDICT_by_auto_tuned_model'],
           facecolors='none', edgecolors='r', label='auto tuned RandomForest model')
ax.scatter(DF.loc[:,x_name], DF.loc[:,'PREDICT_by_custom_tuned_model'],
           facecolors='none', edgecolors='b', label='custom tuned RandomForest model')
ax.legend(loc='best', fontsize=14)
ax.tick_params(labelsize=16)
plt.tight_layout()
plt.subplots_adjust(left=0.1, right=0.95, bottom=0.1, top=0.95)
plt.savefig('23_scatter_raw_vs_pred1_vs_pred2.png')
plt.close()

# In[24]:


# 構築した回帰モデルをデプロイする
#deploy_model(model = tuned_reg, model_name = 'tuned_reg_aws', platform = 'aws', 
#             authentication =  {'bucket'  : 'pycaret-test'})


# In[25]:


# 回帰モデルを保存する
import datetime
now = datetime.datetime.now()
now = now.strftime("%y%m%d")
save_model(tuned_reg, model_name = 'boston_randomforest_model_' + now)


# In[26]:


# 保存した回帰モデルをロードする
load_reg_model = load_model('boston_randomforest_model_' + now)


# In[27]:


# ロードした回帰モデルの中身を確認する
for i, data in enumerate(load_reg_model):
    print(i, data)
    if 'RandomForestRegressor' in str(data):
        element_No = i


# In[28]:


# ロードした回帰モデルをセットする
load_reg = load_reg_model[element_No]
# ロードした回帰モデルのハイパーパラメータを確認する
#evaluate_model(load_reg)
plot_model(load_reg, plot = 'parameter')


# In[29]:


print('finished')

以上

<広告>