0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AutoGluon実装 備忘録

Posted at

概要

前回に引き続き、実装のポイントと備忘録に残す。

  • 実施期間: 2025年12月
  • 環境:Ubuntu22.04LTS
  • Python: condaのPython3.12

使用パケージ

import os
# os.environ["CUDA_LAUNCH_BLOCKING"] = "1"
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
from time import time
import numpy as np
import pandas as pd
from autogluon.timeseries import TimeSeriesDataFrame, TimeSeriesPredictor
import matplotlib.pyplot as plt
import plotly.graph_objects as go

AutoGluon用のDataset準備

    df = pd.read_parquet('1348.T_merge.parquet')
    df.index = pd.to_datetime(df.index).tz_localize(None).normalize()
    df = df.reset_index().rename({'Date': 'timestamp'}, axis=1)

    # item_id の追加
    # このデータフレーム全体が1つの時系列(1348.T)であることを明示
    df["item_id"] = "1348.T"

    # agはknown_covariateとして既知の将来の共変数を追加可能
    df["iso_week"] = df["timestamp"].dt.isocalendar().week.astype(int)
    df["financial_quarter"] = df["timestamp"].dt.quarter

    train_df, test_df = df[df['timestamp']<'2025-02-01'], df[df['timestamp']>='2025-02-01']
    print(len(train_df), len(test_df))

    # Long Formへ変換
    # Wide Form (train_df / test_df) から直接読み込む
    train_data = TimeSeriesDataFrame.from_data_frame(
        train_df,
        id_column="item_id",
        timestamp_column="timestamp"
    )
    test_data = TimeSeriesDataFrame.from_data_frame(
        test_df,
        id_column="item_id",
        timestamp_column="timestamp"
    )

ちなみにtrain_dataはこんな感じ。

                           Open         High          Low        Close   Volume  ...       Low_SP     close_sp  close_vix  iso_week  financial_quarter
item_id timestamp                                                                ...                                                                  
1348.T  2009-05-13   689.709351   689.709351   689.709351   689.709351      0.0  ...   882.799988   883.919983  33.650002        20                  2
        2009-05-14   669.553467   669.553467   669.553467   669.553467      0.0  ...   882.520020   893.070007  31.370001        20                  2
        2009-05-15   682.726851   684.278503   681.175200   684.278503   8840.0  ...   878.940002   882.880005  33.119999        20                  2
        2009-05-18   667.210449   667.210449   665.658797   667.210449   3830.0  ...   886.070007   909.710022  30.240000        21                  2
        2009-05-19   681.175248   681.175248   679.623596   679.623596   1140.0  ...   905.219971   908.130005  28.799999        21                  2
...                         ...          ...          ...          ...      ...  ...          ...          ...        ...       ...                ...
        2025-01-27  2831.907535  2841.283078  2812.662999  2816.610596  72930.0  ...  5962.919922  6012.279785  17.900000         5                  1
        2025-01-28  2804.274276  2832.894355  2789.964237  2814.636719  87280.0  ...  5994.629883  6067.700195  16.410000         5                  1
        2025-01-29  2828.453277  2839.309169  2826.479479  2835.361572  19450.0  ...  6012.959961  6039.310059  16.559999         5                  1
        2025-01-30  2825.986021  2845.230556  2824.999121  2841.282959  20000.0  ...  6027.459961  6071.169922  15.840000         5                  1
        2025-01-31  2843.256823  2855.099614  2834.868179  2852.138916  41720.0  ...  6030.930176  6040.529785  16.430000         5                  1     

Training

prediction_length: 任意のhorizon長を指定する。predictするときはこの長さがforecastとして出力される。
freq: Pandasの仕様による。
known_covariates_names: 必要であればlist型でDataFrame中のknown_covariatesを指定する。
eval_metric: オフィシャルサイトに丁寧にまとめられているので用途に合わせて選択する。Lossは望大特性という珍しい仕様のため内部で-1がかけられる。
presets: 予め用意されているモデルのセットを指定する。
time_limit: 秒で上限を指定する。指定がなければ全モデルのtrainingが完了するまで返らない。

    if refit:
        # train_data に含まれる他の列 (Open, High, Low, Volume) は自動的に説明変数として扱われる
        predictor = TimeSeriesPredictor(
            prediction_length=5,
            freq="B",  # Business day frequency
            path="autogluon-1348T-Close",
            target="Close",  # 予測対象の列名を指定
            known_covariates_names=["iso_week", "financial_quarter"],
            eval_metric="MASE",    # Mean absolute scaled error
        )

        predictor.fit(
            train_data,
            presets="medium_quality",
            time_limit=600,
        )
    else:
        # fit済みのモデルを読み込み。
        predictor = TimeSeriesPredictor.load("autogluon-1348T-Close")
        print(predictor.model_names())

AutoMLとして使用されるモデルはココの通り。presetを使いたくなければ下記のように辞書型で任意にモデル選択や、ハイパラの指定が可能。ただし公式がいうようにハイパラの指定は推奨されていない。
また、GPUがない環境ではDNN系のモデルは選択されない。

        predictor.fit(
            train_data,
            hyperparameters={
                "Chronos": [
                    {"model_path": "bolt_small"}, # 軽量版
                    {"model_path": "bolt_base"},  # 標準版
                    {"model_path": "large"}       # 高性能版 (注意: "bolt_large"ではない)
                ]
            },
            # enable_ensemble=True # 3つのChronosのいいとこ取りをするならTrue
        )

trainingが始まると下記のように表示される。GPUはRTX-3090Tiを使用。

Beginning AutoGluon training...
AutoGluon will save models to '/home/ihmon/Documents/ML/ag/autogluon-1348T-Close_1130'
=================== System Info ===================
AutoGluon Version:  1.4.1b20251025
Python Version:     3.12.12
Operating System:   Linux
Platform Machine:   x86_64
Platform Version:   #88~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Oct 14 14:03:14 UTC 2
CPU Count:          24
Pytorch Version:    2.7.1+cu126
CUDA Version:       12.6
GPU Memory:         GPU 0: 23.54/23.54 GB
Total GPU Memory:   Free: 23.54 GB, Allocated: 0.00 GB, Total: 23.54 GB
GPU Count:          1
Memory Avail:       55.90 GB / 62.67 GB (89.2%)
Disk Space Avail:   372.33 GB / 811.13 GB (45.9%)
===================================================

Fitting with arguments:
{'enable_ensemble': True,
 'eval_metric': MASE,
 'freq': 'B',
 'hyperparameters': {'Chronos': [{'model_path': 'large'}]},
 'known_covariates_names': ['iso_week', 'financial_quarter'],
 'num_val_windows': 1,
 'prediction_length': 5,
 'quantile_levels': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9],
 'random_seed': 123,
 'refit_every_n_windows': 1,
 'refit_full': False,
 'skip_model_selection': False,
 'target': 'Close',
 'verbosity': 2}

Evaluation

test datasetを使ってpredictor.leaderboard(test_data)でモデルたちの評価を行うことができる。
"medium_quality"を指定したため合計8個のモデルがTrainingされており、各モデルの-1をかけたlossがsocreとして上位から表示されている。一番上の"WeightedEnsemble"が全モデルをアンサンブルしたモデルだが、この場合そのスコアから"Chronos[bolt_small]"だけが選択されたことがわかる。
GPUがない環境だと"Chronos[bolt_small]"と"TemporalFusionTransformer"はTrainingされない。

data with frequency 'IRREG' has been resampled to frequency 'B'.
Additional data provided, testing on additional data. Resulting leaderboard will be sorted according to test score (`score_test`).
                       model  score_test  score_val  pred_time_test  pred_time_val  fit_time_marginal  fit_order
0           WeightedEnsemble   -0.362003  -0.344317        1.248567       2.390552           0.358120          9
1        Chronos[bolt_small]   -0.362003  -0.344317        1.248344       2.390552           3.979786          7
2              SeasonalNaive   -0.380603  -1.900188        8.074816       1.274556           0.019266          2
3  TemporalFusionTransformer   -0.497300  -0.618541        0.072481       0.020987          64.471157          8
4                      Theta   -0.561908  -1.067661        1.790953       2.100371           0.024960          6
5                        ETS   -0.587241  -0.915873        1.797062       2.578173           0.016829          5
6                      Naive   -0.677659  -0.965803        1.258380       6.939918           0.021187          1
7           RecursiveTabular   -1.071249  -1.160690        0.090148       0.045300           0.929499          3
8              DirectTabular   -1.459341  -6.769379        0.044259       0.055773           0.551997          4

Predict

known_covariates: Trainingでknown_covariates_namesを使用していれば、都度再計算して渡す。

    # fitに使ったデータ(train_data)の末尾から、未来のprediction_length分(5日分)を予測
    # known_covariateを再計算(毎回predictに渡すたびに追加する仕様みたい)
    known_covariates = predictor.make_future_data_frame(train_df)   # <- known_covariatesにはprediction_length分の"item_id"と"timestamp"しか入らない
    known_covariates["iso_week"] = known_covariates["timestamp"].dt.isocalendar().week.astype(int)
    known_covariates["financial_quarter"] = known_covariates["timestamp"].dt.quarter
    predictions = predictor.predict(train_data, known_covariates)

predictは次のようにprediction_length分のDataFrameを返却する。

                           mean          0.1          0.2          0.3          0.4          0.5          0.6          0.7          0.8          0.9
item_id timestamp                                                                                                                                   
1348.T  2025-02-03  2854.435547  2775.269043  2807.301270  2827.823242  2842.539551  2854.435547  2865.813477  2878.108887  2893.184570  2921.000244
        2025-02-04  2852.716309  2766.651855  2803.596924  2825.017334  2840.312256  2852.716309  2865.169434  2877.731445  2892.286133  2920.730469
        2025-02-05  2866.354004  2767.522461  2809.018311  2834.395996  2851.531250  2866.354004  2879.750977  2892.969238  2908.532715  2938.487793
        2025-02-06  2864.736328  2762.536133  2805.918457  2831.386963  2849.533936  2864.736328  2877.556396  2892.081787  2909.668945  2940.818604
        2025-02-07  2856.492676  2755.364990  2798.504150  2823.687012  2842.391602  2856.492676  2869.751465  2883.756836  2901.334473  2934.469727

AutoGluonの良いところはQuantile Regressionであるところと、それがLightGBMのようにmedianを挟んで上下対象の正規分布ではなく、非対称分布である点である。

newplot.png

上チャートのようにmedianに対して下ブレリスクがあることを得ることができる。

運用時の使い方

観測のたびにforecast horizonを予測するなら、都度Training datasetに直近の観測値を追加してpredictする。

# onlineを想定した毎日の予測実行シミュレーション
def simuration(train_data, test_data, target_column, prediction_length, predictor):
    # シミュレーション用のデータを初期化
    current_history = train_data.copy()   # これまでの履歴 (最初は学習データのみ)
    remaining_test = test_data.copy()     # テストデータ (最初はテストデータ全体)

    # 20日分(20回)ループして、毎日データを追加しながら予測を行う
    for t in range(20):
        # テストデータの先頭1行(翌日の確定値)を取得
        # .iloc[[0]] とすることで、SeriesではなくDataFrameとして取得し、concat時の型崩れを防ぐ
        new_observation = remaining_test.iloc[[0]]
        
        # 履歴データに結合 (Update Context)
        current_history = pd.concat([current_history, new_observation])
        
        # テストデータからその行を削除 (次の日のためにプールを減らす)
        remaining_test = remaining_test.iloc[1:]
        
        # 最新の履歴に基づいて未来を予測
        known_covariates = predictor.make_future_data_frame(current_history)
        known_covariates["iso_week"] = known_covariates["timestamp"].dt.isocalendar().week.astype(int)
        known_covariates["financial_quarter"] = known_covariates["timestamp"].dt.quarter
        predictions = predictor.predict(current_history, known_covariates)
        
        # ログ出力 (進捗確認用)
        current_date = new_observation.index[0][1].date()
        print(f"Step {t+1}/20: {current_date} のデータを追加 -> 翌日以降を予測しました。")

        true_val = remaining_test.iloc[:prediction_length][target_column].to_numpy()
        pref_val = predictions.loc[:,"0.5"].to_numpy()      # medianで計算
        print(f'RMSE: {get_rmse(true_val, pref_val):.3f}')

以上

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?