Python 「PyCaret」複数の回帰モデルをCUIで自動生成する

 PyCaretは、対話型のIDEであるJupyter Notebook、Jupyter Lab、 Google Colaboratory等での使用を推奨しています。そのため、コマンドラインCUI)ではエラーで動作ストップする場合もいくらか遭遇します。そこで、本記事ではCUIで実行する場合でも、それに近いことをできるような雛形コードを載せました。具体的には、各モデルのハイパーパラメータの自動調整とその結果をひとつの画像ファイルでグラフ出力することで比較できるようにしました。更に、各々の回帰モデルをオブジェクトファイル(.pkl)で保存します。また、それを読み込む方法の雛形コードを記載しています。 

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

▼ 下図は、モデル別の適合度R2の図です。ハイパーパラメータは自動調整しています。左上から右へ順に、線形重回帰(このパラメータは一意的に決まる)、ラッソ回帰、リッジ回帰、ランダムフォレスト回帰、Gradient Boosting回帰、xgboost回帰、LightGBMです。前者3つは線形回帰モデルで、それ以降は非線形回帰モデルです。ランダムフォレスト以降の非線形回帰モデルの適合度は85%以上と高めです。

f:id:HK29:20200923215938p:plain

▼下図は、モデル別に誤差をプロットした図です。前半3つの線形回帰モデルは±10以上の誤差幅です。一方、後半の非線形回帰モデルの誤差は±5程度と比較的小さいことがわかります。

f:id:HK29:20200923220023p:plain

▼下図は、学習過程をプロットした図です。一つ目が空欄なのは、線形重回帰モデルでパラメータは一意的に決まるためにグラフがありません。ランダムフォレスト、XGBR、LGBMは、学習している様子がわかります。

f:id:HK29:20200923220226p:plain

■本プログラム
ハイパーパラメータを自動調整してオブジェクトファイルで保存する。また、それを読み込む雛形コードを記載しています。

#!/usr/bin/env python
# coding: utf-8
import os
import pandas as pd
import pandas_profiling as pdp
from pycaret.regression import *
import matplotlib
matplotlib.use('Agg') # バックグラウンドで画像生成などする宣言
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
now = datetime.datetime.now()
now = now.strftime("%y%m%d")

# csvファイルを読み込み、データフレームと特徴量のリストを返す関数
def read_csv_func(mytarget, myfeature_list_for_graph_plot):
    # csvファイルをpandasのDataFrame型で読み込む
    df = pd.read_csv(file_path,
                     header=0,
                     encoding='utf-8')

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

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

    # データのサマリーを生成
    myprofile = pdp.ProfileReport(df)
    myprofile
    myprofile.to_file(file_name + "_ProfileReport.html")

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

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

    return df, feature_name_list


# 回帰モデル結果をグラフで可視化する関数
def plot_model_func(tuned_model_list):
    # ひとつの画像ファイルで保存するための列数を決める
    cnt = len(tuned_model_list) 
    cols = 3
    rows = cnt // cols
    rows += cnt % cols

    fig = plt.figure(1, figsize=(12,10),dpi=200) 
    # サブプットを動的に追加して、ひとつの画像ファイルで保存する
    for i, auto_tuned_reg in enumerate(tuned_model_list, start=1):
        try:
            ax = fig.add_subplot(rows, cols, i)
            plot_model(auto_tuned_reg, plot = 'vc')
        except:
            pass
    plt.tight_layout()
    plt.savefig('_vc.png')
    plt.close()

    # 相関性のチェック
    fig = plt.figure(1, figsize=(12,10),dpi=200) 
    for i, auto_tuned_reg in enumerate(tuned_model_list, start=1):
        try:
            ax = fig.add_subplot(rows, cols, i) 
            plot_model(auto_tuned_reg, plot = 'error')
        except:
            pass
    plt.tight_layout()
    plt.savefig('_error.png')
    plt.close()

    # 残渣プロット
    fig = plt.figure(1, figsize=(15,10),dpi=200) 
    for i, auto_tuned_reg in enumerate(tuned_model_list, start=1):
        try:
            ax = fig.add_subplot(rows, cols, i) 
            plot_model(auto_tuned_reg, plot = 'residuals')
        except:
            pass
    plt.tight_layout()
    plt.savefig('_residuals.png')
    plt.close()


# 保存した回帰モデルをロードする
def load_model_func(load_model_file):
    load_reg_model = load_model(load_model_file)

    # ロードした回帰モデルの中身を確認する
    print(load_model_file)
    for i, data in enumerate(load_reg_model):
        print(i, data)
        with open(load_model_file + '.csv', 'a') as f:
            buf = str(i) + ',' + str(data)
            f.write(buf + '\n')

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

    return load_reg_model


def main():
    # csvファイルを読み込み、データフレームと特徴量のリストを返す
    df, feature_name_list = read_csv_func(mytarget, myfeature_list_for_graph_plot)

    # PyCaret用のデータセット生成(前処理)
    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) # 型チェックの手動確認のため一時ストップするか

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

    # モデルオブジェクトを作成する
    lr = create_model('lr', verbose = False) # 線形重回帰
    lasso = create_model('lasso', verbose = False) # ラッソ回帰
    ridge = create_model('ridge', verbose = False) # リッジ回帰
    rf = create_model('rf', verbose = False) # ランダムフォレスト回帰
    gbr = create_model('gbr', verbose = False) # Gradient Boosting回帰
    xgboost = create_model('xgboost', verbose = False) # xgboost回帰
    lgbm = create_model('lightgbm', verbose = False) # LightGBM
    model_list = [lr, lasso, ridge, rf, gbr, xgboost, lgbm]

    # ハイパーパラメータを自動調整する
    tuned_model_list=[]
    for mymodel in model_list:
        tuned_model_list.append(tune_model(mymodel,
                                fold = 10, #default
                                round = 4, #default
                                n_iter = 10, #default
                                optimize = 'MAE', #MAE, MSE, RMSE, RMSLE, R2 and MAPE
                                verbose = False))
    
    # 回帰モデル結果をグラフで可視化する関数
    plot_model_func(tuned_model_list)

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

    # 回帰モデルを保存する
    model_file_name_list = []
    for i, auto_tuned_reg in enumerate(tuned_model_list, start=1):
        mystr = str(auto_tuned_reg)
        model_name = mystr[:mystr.find(r'(')]
        save_name = now + '_' + model_name
        save_model(auto_tuned_reg, model_name = save_name)
        model_file_name_list.append(save_name)

    # 保存した回帰モデルをロードしてパラメータを確認する。
    load_model_list = []
    for i, mymodel in enumerate(model_file_name_list):
        load_model = load_model_func(mymodel)
        load_model_list.append(load_model)
        

if __name__ == "__main__":
    # 分析するcsvファイルのパスを指定
    file_path = './boston_dataset.csv'
    # 拡張子なしのファイル名を取得
    file_name = os.path.splitext(os.path.basename(file_path))[0]
    
    # 目的変数
    mytarget = 'PRICE'
    # 行列散布図用のカラム名(列名)をリスト化する
    # 全部のカラム名をプロットするとグラフが非常に小さくなるので項目を絞るため
    myfeature_list_for_graph_plot = ['RM', 'CRIM', 'LSTAT', 'PTRATIO', 'PRICE']

    main()
    print('finished')

以上

<広告>