Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

時系列基盤モデルChronos-Boltでお手軽に時系列予測を試してみた

皆さんこんにちは
Acroquestのデータサイエンスチーム「YAMALEX」のチームリーダ、@tereka114です。
YAMALEXチームでは、コンペティションへの参加や自社製品開発、技術研究などに日々取り組んでいます。
(詳細はこちらをご覧ください。)

LLMやVLMをはじめとした基盤モデルが近年、多く公開されています。
画像や自然言語処理といったものとは異なりますが、時系列モデリングでも基盤モデルが利用されようとしています。

本記事では、時系列基盤モデルの一つである「Chronos-Bolt」を紹介します。

Chronos-Boltについて

Chronosは端的に言えば、予め様々な時系列を学習させることで、適用するデータの学習をしなくとも、時系列予測を可能とする時系列基盤モデルです。
そのChronosの後継モデルがChronos-Boltになり、Chronosと比較して、精度、性能共に大きく向上しました。

参考1)Chronosの論文
arxiv.org

本記事ではAutoGluonを利用して、Chronos-Boltでの時系列予測処理を実装してみました。
AutoGluonは、AWSが中心となって開発されているOSSのAutoMLフレームワークです。AutoGluonには、いくつかの時系列予測モデルが実装されており、Chronos-Boltも含まれています。

参考2)追加学習なしの zero-shot で高精度な時系列予測 : Chronos-Bolt を AutoGluon で利用する(AWS公式ブログ)
aws.amazon.com

1. 学習不要で高精度な推論ができる。
Chronos/Chronos-Boltでは、学習なしで時系列予測を可能とするために、既存の様々な時系列データに加え、TS-MixupやKernelSynthといった合成データを作成する手法も利用し、多くの時系列データを学習しています。
それにより、ドメインに対する学習がない場合でも精度をある程度担保した予測ができます。
一般的に、予測精度を高めるためには学習が必要ですが、Chronos-Boltは学習データが存在しないケースにおいても一定の精度を見込むことができます。

2. Chronosモデルと比較して高速(最大260倍)
Chronosモデルでは一度に1つのステップの未来を予測しています。
しかし、Chronos-Boltは複数の予測(t, t+1, t+2)を同時に推論することで高速化を実現しています。公式ブログのベンチマークによれば、Chronosと同じモデルの大きさで、最大260倍の性能を発揮しています。

Chronos-Boltの推論速度(AWS公式ブログより)

3. Chronosモデルと比較して高精度
公式のベンチマークによれば、Chronosモデルと比較して高精度の結果が得られています。
この点においてもChronos-Boltを利用するメリットがあります。

Chronos-Boltを用いた精度比較(AWS公式ブログより)

Chronos-Boltを使った時系列予測

本記事ではChronos-Boltを利用した次の3つのパタンを紹介します。

パタン 説明
ゼロショット推論 学習なしで推論する
Finetune実施 学習ありで推論する
回帰モデルを利用した特徴量の外挿利用 推論する時系列以外にも補助的に利用する特徴量を使う。

準備

必要なライブラリのインストール

Chronos-Boltを利用するためには、事前にautogluonをインストールする必要があります。

pip install autogluon
データ準備

データセットはKaggleで公開されている「Store Sales - Time Series Forecasting」を利用します。
エクアドルに存在するリテール「Corporación Favorita」の商品売上数のデータを利用します。
www.kaggle.com

そのままのデータ形式では、適用が難しいので、次の実装で加工します。

import polars as pl

transaction = pl.read_csv("./data/store-sales-time-series-forecasting/transactions.csv")
holiday = pl.read_csv("./data/store-sales-time-series-forecasting/holidays_events.csv")

transaction = transaction.join(holiday, how="left", on="date")
transaction = transaction.fill_null("null")
transaction = transaction.with_columns(
    pl.col("date").str.to_date()
)
transaction = transaction.with_columns(
    pl.col("date").dt.month().alias("month"),
    pl.col("date").dt.day().alias("day"),
    pl.col("date").dt.weekday().alias("weekday")
)

df = transaction
train_df = df.filter(
    pl.col("date").dt.year() < 2017
)
train_df.write_csv("./data/train.csv")
valid_df = df.filter(
    (pl.col("date").dt.year() >= 2017) & (pl.col("date").dt.month() == 1)
)
valid_df = pl.concat([train_df, valid_df], how="diagonal_relaxed")
valid_df.write_csv("./data/valid.csv")

本解析で利用する主要なパラメータを記載します。

項目 説明
date 日付
store_nbr 店舗
transactions 取引量★今回の予測値
type 祝日ORイベント
locale 適用エリア
locale_name 地域などの名前
month dateから抽出した月
day dateから抽出した日
weekday dateから抽出した曜日

ゼロショット推論

ゼロショットで推論するのは非常に簡単です。
次の実装で対応できます。

id_columnはグルーピングするカテゴリ、timestamp_columnは時間を示す列、また、targetで予測列を指定します。
TimeSeriesPredictor.fitメソッドにて、presets="bolt_base"を指定することで、Chronos-Boltを利用できます。この際、bolt_baseをbolt_small/bolt_tinyにすることで、Chronos-Boltの別サイズのモデルの利用ができます。

import polars as pl
from autogluon.timeseries import TimeSeriesDataFrame, TimeSeriesPredictor

train_data = TimeSeriesDataFrame.from_path( 
     "data/train.csv", 
     id_column="store_nbr", 
     timestamp_column="date", 
)
predictor = TimeSeriesPredictor(prediction_length=30, freq="D", target="transactions").fit(train_data, presets="bolt_base")
predictions = predictor.predict(train_data)

推論結果を可視化します。
autogluonに可視化用のutility関数がありますので、その関数を利用します。
一時的にデータが欠損している部分がありますが、学習なしでも概ね追従できていることがわかります。

test_data = TimeSeriesDataFrame.from_path( 
     "data/valid.csv", 
     id_column="store_nbr", 
     timestamp_column="date", 
)
predictor.plot(test_data,predictions,max_history_length=365,item_ids=[25])

本プロットの青線が実績、オレンジの線がChronos-Boltによる予測結果を示しています。
また、オレンジは線の前後で網掛けになっていますが、こちらは80%信頼区間が表現されています。

推論結果(ゼロショット)

Finetune

次にFinetuneして学習した結果を利用して、時系列予測を試みます。
Finetuneは次の実装で可能です。
この実装では、ゼロショットとFinetuneしたモデルの2パタンのモデルを作成されます。
hyperparametersに含まれるChronosキーの中にリストがあり、ゼロショットとFinetuneしたモデルの2パタンを定義しています。主なポイントとして、fine_tuneをTrueにすることです。

predictor = TimeSeriesPredictor(prediction_length=31, eval_metric="MASE", freq="D", target="transactions").fit( 
     train_data, 
     hyperparameters={ 
         "Chronos": [ 
             {"model_path": "bolt_base", "ag_args": {"name_suffix": "ZeroShot"}}, 
             {"model_path": "bolt_base", "fine_tune": True, "ag_args": {"name_suffix": "FineTuned"}}, 
         ] 
     }, 
     enable_ensemble=False, 
     time_limit=600, 
)

終了後に、次の実装でゼロショットとFinetuneしたモデルの評価結果を取得できます。

predictor.leaderboard(test_data)
学習結果(外挿あり)

また、本predictorの実装を利用した結果を次の通り予測、プロットすることが可能です。

predictions = predictor.predict(train_data, model="ChronosZeroShot[bolt_base]")
predictor.plot(test_data,predictions,max_history_length=365,item_ids=symbols, max_num_item_ids=len(symbols))

ゼロショットと比較して、より実績にフィットしているように見えます。

推論結果(Finetune

特徴量の外挿利用

予測対象となる時系列データ以外にも補助的に利用できる時系列が含まれる場合があります。
例えば、店舗の売上に対して、来店に影響する降水量といったものが関係する場合があり、そのような場合に利用ができます。
ただし、外挿は当日のデータを利用することになりますので、検証するうえで、当日のデータを獲得できない場合、当日の予測をすることは難しいです。
(例:予測前日に予測当日の正確な降水量を使う場合)

外挿を利用する場合の予測値は次の通りの式になります。
回帰予測値 = Chronos-Boltの予測値 + CatBoost(外挿回帰モデル)の予測値

主な変更点として、TimeSeriesPredictorのknown_covariates_namesに外挿する列を指定します。
今回のモデルでは日付特徴(month, day, weekday)、祝日情報(type, locale, locale_name)を入力します。実装は次の通りです。

predictor = TimeSeriesPredictor(
    prediction_length=31,
    eval_metric="MASE",
    target="transactions",
    known_covariates_names=["type", "locale", "locale_name", "month", "day", "weekday"],
    freq="D"
).fit(
    train_data,
    hyperparameters={
        "Chronos": [
            {"model_path": "bolt_base", "ag_args": {"name_suffix": "ZeroShot"}},
            {
                "model_path": "bolt_base",
                "covariate_regressor": "CAT",
                "target_scaler": "standard",
                "ag_args": {"name_suffix": "WithRegressor"},
            },
        ],
    },
    time_limit=600,
    enable_ensemble=False,
)

Finetuneと同じように比較が可能です。

predictor.leaderboard(test_data)
学習結果(外挿)

今回の予測では、外挿となる特徴量を与えることが必要です。
こちらの推論は31日全てのデータが揃っているものでなければ、難しいので、全て揃っているstore_nbrが25のデータにフィルタリングして推論結果を可視化します。
実装は次の通りです。

filtered_id_df = train_data.to_data_frame().loc[25]
filtered_id_df['item_id'] = 25
filtered_id_df['timestamp'] = filtered_id_df.index
train_data = TimeSeriesDataFrame.from_data_frame(filtered_id_df)

filtered_id_df = test_data.to_data_frame().loc[25]
filtered_id_df['item_id'] = 25
filtered_id_df['timestamp'] = filtered_id_df.index
test_data = TimeSeriesDataFrame.from_data_frame(filtered_id_df)

predictions = predictor.predict(train_data, model="ChronosWithRegressor[bolt_base]", 
                                known_covariates =test_data
                               )
predictor.plot(test_data,predictions,max_history_length=365,
               item_ids=[25], max_num_item_ids=1)

更にFinetuneと比較して実績線に近くなったように見えます。

推論結果(外挿)

最後に

Auto GluonからChronos-Boltを簡単に利用し、時系列予測を試せました。
LLMのような基盤モデルの考えが、時系列モデルにも応用ができるのは非常に面白く、今後の発展も期待できそうです。

Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com