HOW TO TD(User Engagement)Treasure Data User Engagement

ParameterGridを使ったパラメータチューニング

ホーム » ParameterGridを使ったパラメータチューニング

データマネジメントチームの金 氣範です。
前回の時系列予測に続き、パフォーマンスの良いモデルのパラメータを探索する方法について紹介します。

大体のモデルは特に指定しない場合のパラメータはデフォルト値が設定され、良い結果になったりする場合がありますが、それよりパフォーマンスを上げたい場合はパラメータを直接変更しなければなりません。パラメータはモデルによって様々な種類が存在します。例え、3つのパラメータを10パターンずつ試し(10 x 10 x 10 : 1000パターン) 評価指標の確認・比較を繰り返すと、精神も体も疲れてしまいます。そのため、ParameterGridを使ってチューニングを行うことで、プロセスがシンプルになりパラメータ探索の効率を上げることができます。

さて、前回のProphetから調整しやすいパラメータとしてchangepoint_prior_scale(トレンドの柔軟性:0~1.0)、changepoint_range(潜在的な変化点を観測するための幅:0~1.0)がありますが、そのパラメータをParameterGridを使ったチューニングついて紹介します。

ParameterGrid()はdict型のparameterの組み合わせを設定しておくと、その一連の組み合わせが順番にマッピングされます。
例えば、5 x 5種類の組み合わせを以下のようにdict型で設定すると、

params_grid = {‘A’ : [1,2,3,4,5], ‘B’ : [0.1,0.2,0.3,0.4,0.5]}
a = ParameterGrid(params_grid)

これでaには、

A = 1, B = 0.1
A = 1, B = 0.2

A = 5, B = 0.5

上記のようなそれぞれの組み合わせがマッピングされます。それから、

for p in a:
  ​​model = Prophet(**p)

forでそれぞれのパターンのパラメータでモデルを作ることができます。

それでは、前回のモデルを使ってチューニングができそうか試してみましょう。
前回のモデルの平均絶対誤差(MAPE)は2.024%でした。以下のように12 x 7のパターンでパラメータを評価してみようと思います。

params_grid = {'growth':['linear'], 'changepoint_prior_scale':[0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6], 
               'changepoint_range': [0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9], 'seasonality_mode': ['multiplicative']}

探索を行った結果、{‘changepoint_prior_scale’: 0.6, ‘changepoint_range’: 0.9, ‘growth’: ‘linear’, ‘seasonality_mode’: ‘multiplicative’}のパラメータがbestだったため、このパラメータで結果はどうなってるか確認してみましょう。

チューニングした前後の精度


チューニングで以前より全体の平均絶対誤差が2.024%から1.976%に改善となりましたが、あくまで現時点でのテストではということは意識しておきましょう!少ない改善の場合は今後様子をみていくか、もっと多くのテストをして比較・評価してみることをおすすめします。

以下はWorkflowでの実装及び、探索結果をexportするコードとなります。

結果をTreasure Data CDPに戻すための、workflow

timezone: Asia/Tokyo

# main process
+train_predict:
    docker:
        image: "digdag/digdag-python:3.9"
    _env:
        TD_API_KEY: TD apikey入力
        ENDPOINT: TD環境に合わせてhttps://api.treasuredata.co.jp or https://api.treasuredata.comを入力
        DB: DB名入力
    py>: scripts.test_py.main

結果をTreasure Data CDPに戻すための、custom script

import pandas as pd
from pytd import pandas_td as td
from fbprophet import Prophet
from sklearn import metrics
from sklearn.model_selection import ParameterGrid

# pytdの必要な情報をWorkflow側の変数からcall
con = td.Client(apikey=os.environ.get('TD_API_KEY'), endpoint=os.environ.get('ENDPOINT'))
presto = td.create_engine('presto:{}'.format(os.environ.get('DB')), con=con)
database = os.environ.get('DB')

def main():
    # MAPE測定
    def mean_absolute_percentage_error(Y_test, Y_pred): 
        Y_test, Y_pred = np.array(Y_test), np.array(Y_pred)
        return np.mean(np.abs((Y_test - Y_pred) / Y_test)) * 100
    # 対象の時系列データをload
    load_td = td.read_td_query('''
        SELECT * FROM sample_dataset
        ''', engine=presto)
    # index設定
    sample_df = load_td.set_index('date')
    # IndexをDatetime format指定 & sort
    sample_df.index = pd.DatetimeIndex(sample_df.index)
    sample_df.sort_index(inplace=True)
    
    # モデルの作成

    # 全体の旅客数を選択しtrain setの作成
    base_forecast = sample_df['2000-01':'2017-12'].iloc[:,0].reset_index()
    base_forecast.columns = ['ds','y']

    # testするためのstart_date(month)
    t_dt = '2018-01'

    # parameter探索対象
    params_grid = {'growth':['linear'], 'changepoint_prior_scale':[0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6], 
                   'changepoint_range': [0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9], 'seasonality_mode': ['multiplicative']}
    # ParameterGridでマッピング
    grid = ParameterGrid(params_grid)

    # マッピングされたパターンでモデルの評価
    #結果格納用dataframe
    result_all = pd.DataFrame()
    for p in grid:
        model = Prophet(**p)
        model.fit(base_forecast)
        future = pd.DataFrame(df['2018-01':'2018-12'].index)
        future.columns = ['ds']
        forecast = model.predict(future)

        # 中間結合用dataframe
        result_t1 = pd.DataFrame()
        result_t2 = pd.DataFrame()
        result_t3 = pd.DataFrame()
        print('test', p)
        for i in range(0,12):
            from_dt =  (pd.to_datetime(t_dt) + relativedelta(months=1*i)).strftime('%Y-%m')
            print('評価年月:', from_dt)
            print('Mean absolute percentage error:', mean_absolute_percentage_error(df.loc[from_dt:from_dt].iloc[:,0], forecast.set_index('ds')[from_dt:from_dt].yhat))
            result_t1 = pd.DataFrame([mean_absolute_percentage_error(df.loc[from_dt:from_dt].iloc[:,0],forecast.set_index('ds')[from_dt:from_dt].yhat)], columns=)
            result_t2 = pd.concat([result_t2, result_t1], axis=1)

        result_t2 = result_t2.T
        result_t2.index = future.ds
        result_t3 = pd.concat([pd.DataFrame(df.loc['2018-01':'2018-12'].iloc[:,0]), forecast.set_index('ds')['2018-01':'2018-12'].yhat.round(), result_t2], axis=1)
        result_t3['param'] = str(p)
        result_t3['mean_12months'] = result_t2.mean().values[0]
        result_all = pd.concat([result_all, result_t3])
        
    result_all.columns = ['旅客数(実績)', '旅客数(予測)', 'MAPE', 'param', 'mean_12months']
    print('Best parameter is : ', result_all.sort_values(by='mean_12months', ascending=True).iloc[0,3])
    
    # 対象DBにて予測結果を格納
    td.to_td(result_all, '{}.test_export'.format(database), con=con, if_exists='append')


最後に、今回はシンプルなパターンの例となりましたが、実際には1万パターンもしくはそれ以上の計算など大量の場合が多いため、こういった機能を使うとすごく助かると思います。それでは、ぜひ試してみてください!

金 氣範

Data Managementチーム

新卒でメディア企業に入社し、データサイエンス組織の立ち上げメンバーとして、金融とITの融合、レコメンドエンジンの開発を経て、データセンター・クラウドサービス企業にてPrivate DMPサービス開発、クラウドサービスのログ効率化を経験。 その後、大手ポータル企業へデータソリューションプロジェクト立ち上げメンバーとして参画。企業間データビジネスのPoC推進(データドリブンなビジネス課題の探索・解決・商品企画・需要予測など)に従事。 2019年にトレジャーデータに参画し、Treasure Data CDP構築の支援、機械学習関連ビジネス課題に対する支援に携わる。

得意領域 : 需要予測、トレンド検知、データと機械学習、ビジネス課題解決

Back to top button