AutoGluon-TimeSeriesのための堅牢なPydanticクラス設計:データ検証からモデル学習まで

序論

機械学習プロジェクトの成功は、高品質なモデルだけでなく、堅牢で保守性の高いデータパイプラインに大きく依存します。特に、AutoGluonのような自動機械学習(AutoML)フレームワークを利用する場合、その強力な機能を最大限に引き出すためには、入力データの構造と品質を保証することが不可欠です。AutoGluonの時系列予測モジュール autogluon.timeseries は、パネルデータ、静的特徴量、時間変化する共変量(covariates)など、複雑なデータ構造を扱う能力を持っています 1。しかし、これらのデータを正しくフォーマットし、モデルに供給するプロセスは、エラーが発生しやすく、プロジェクトのボトルネックとなり得ます。
本レポートでは、autogluon.timeseries を用いた需要予測タスクを想定し、データ検証ライブラリであるPydanticを活用したクラス設計について、専門的な見地から詳述します。ここでの目標は、単にデータを格納するクラスを定義することではありません。AutoGluonが要求するデータ構造の本質を捉え、データサイエンティストや機械学習エンジニアが直面する一般的な落とし穴を未然に防ぎ、再利用可能でスケーラブルなデータ入力システムを構築することです。
本設計では、以下の要件に対応します。

  1. timestamp(日付時刻型)と target(浮動小数点もしくは整数型)を必須属性とする、パネル時系列データの厳密な定義。

  2. 地点や製品IDなどの静的特徴量(Static Features)の管理。

  3. 将来の値が既知の共変量(Known Covariates)と、過去の値のみが既知の共変量(Past Covariates)の明確な区別と検証。

  4. eval_metric、time_limit、presets といったAutoGluonの学習パラメータの事前検証。

この設計を通じて、データ関連のエラーを学習プロセスの早期段階で検出し、コードの保守性を向上させ、最終的にはより信頼性の高い予測モデルの開発を支援する、体系的なアプローチを提示します。


第1章 建築的基礎:AutoGluonのデータモデルとPydanticの対応付け

効果的なPydanticクラスを設計するための第一歩は、そのクラスが最終的に変換される対象、すなわちAutoGluonのコアデータ構造を深く理解することです。この章では、autogluon.timeseries.TimeSeriesDataFrame の構造を分析し、その設計思想を反映したPydanticアーキテクチャの基礎を築きます。


1.1 autogluon.timeseries.TimeSeriesDataFrame の解剖学

AutoGluonの時系列モジュールは、TimeSeriesDataFrame という特殊なデータ構造を基本として動作します。これは pandas.DataFrame を継承しつつ、時系列データ特有の要件に対応する機能が追加されたものです 3。
TimeSeriesDataFrame の中核をなすのは、「ロングフォーマット」と呼ばれるデータ形式です。これは、各行が単一の時点における単一の時系列(アイテム)の観測値を表す形式です 1。このフォーマットは、複数の時系列を同時に扱うパネルデータ分析において標準的です。具体的には、以下の3つの必須要素で構成されます。

  1. item_id: パネルデータ内の各時系列を一意に識別するためのID。製品コード、店舗ID、センサーIDなどがこれに該当します。データフレーム内では、int型またはstr型であることが期待されます 3。

  2. timestamp: 各観測値が記録された日時。pandas.Timestamp 型または互換性のある形式である必要があります 3。

  3. target: 予測対象となる数値。例えば、製品の売上数量や株価などです。この列名はデフォルトで "target" となりますが、変更も可能です 6。

さらに、TimeSeriesDataFrame の最も特徴的な機能の一つが、静的特徴量(static features)を保持する能力です。これは、時間によって変化しない各アイテムのメタデータを格納するためのものです。例えば、製品のブランドや色、店舗の所在地などが該当します 8。この静的特徴量は、メインの時系列データとは別に、
item_id をインデックスとする pandas.DataFrame として TimeSeriesDataFrame オブジェクトの static_features 属性に格納されます 5。
この時間変化するデータ(targetや共変量)と時間不変のデータ(静的特徴量)の分離は、極めて重要な設計思想です。これにより、データがその時間的性質に応じてクリーンに分割されます。モデル学習の観点からも、静的特徴量はアイテムごとに一度だけ処理すればよく、タイムスタンプごとに処理する必要がないため、効率的です。したがって、我々が設計するPydanticモデルも、この分離構造を忠実に反映させるべきです。すべてのデータを一つの巨大なモデルに押し込めるのではなく、時間変化するデータポイントと静的特徴量をそれぞれ別のPydanticモデルとして定義し、それらを上位のモデルで組み合わせるアーキテクチャを採用します。これは、最終的な目標となるデータ構造を模倣するという、本設計の核となる原則です。


1.2 特徴量と共変量への動的かつ検証可能なアプローチ

現実の予測プロジェクトでは、使用する特徴量のセットが事前に固定されていることは稀です。プロジェクトごとに、静的特徴量、既知の共変量、過去の共変量の名称や数が変動します。もし、class MyData(BaseModel): temperature: float のように特徴量をハードコーディングしてしまうと、そのモデルは特定のスキーマにしか対応できず、柔軟性と保守性を著しく損ないます 11。
この課題に対するPydanticの解答が、create_model 関数です。この関数を用いることで、実行時にBaseModelを継承したクラスを動的に生成できます 12。これにより、特徴量名のリストといった実行時の入力に基づいて、モデルの構造を定義することが可能になります。
この能力を最大限に活用するための最適な設計パターンは、静的なPydanticクラスを定義するのではなく、「ファクトリパターン」を採用することです。具体的には、create_dynamic_timeseries_model のようなファクトリ関数を作成します。この関数は、静的特徴量、既知の共変量、過去の共変量の名称リストを引数として受け取ります。そして、内部で create_model を呼び出し、受け取った名称をフィールド名とし、それぞれに適切な型ヒントが付与されたPydanticクラスを動的に構築して返します。
このアプローチは、データ検証ロジックを特定の予測問題のスキーマから完全に切り離します。同一のファクトリ関数を、全く異なる特徴量セットを持つ複数のプロジェクトで再利用できるため、コードの再利用性が向上し、定型的なコードの記述を削減できます。これは、単なるデータモデリングから一歩進んで、汎用的で設定可能なツールを構築するという、より高次の設計思想を体現するものです。


1.3 データモデルの階層構造

以上の考察に基づき、以下の階層的なPydanticモデル構造を提案します。

  1. TimeSeriesDataPoint: 観測の最小単位を表す基底モデル。普遍的に要求される timestamp: datetime と target: Union[float, int] をフィールドとして持ちます。

  2. StaticFeaturesModel: 動的に生成されるモデル。ユーザーが指定した静的特徴量の名称(例:product_category: str, location_id: int)がフィールドとなります。

  3. PanelDataModel: 単一アイテムの完全な時系列データを表現するモデル。item_id: Union[str, int]、TimeSeriesDataPoint オブジェクトのリスト、そして StaticFeaturesModel のインスタンスをフィールドとして持ちます。この構造は、TimeSeriesDataFrame の概念、すなわち単一アイテムの時系列データとその静的メタデータの組み合わせを直接的に表現しています。

この階層構造により、AutoGluonのデータモデルとの整合性を保ちつつ、Pydanticによる型検証の恩恵を最大限に享受する、堅牢かつ柔軟なデータ入力システムが実現されます。


第2章 共変量と特徴量の精密なモデリング

この章では、本設計において最も繊細さが求められる部分、すなわちAutoGluonがサポートする多様な特徴量カテゴリを正しく構造化し、検証する方法について掘り下げます。共変量の誤った分類は、予測時のエラーの一般的な原因であり、我々のPydantic設計はこれを防ぐことを目的とします。


2.1 静的特徴量の構造化

静的特徴量は、時系列全体にわたって不変の属性であり、モデルがアイテム間の関係性を学習する上で重要な役割を果たします 9。
AutoGluonは、static_features DataFrameの列の dtype に基づいて、特徴量の型を自動的に推論します。int型とfloat型は連続値(continuous)として、object型、str型、category型はカテゴリカル(categorical)として解釈されます 8。
ここで注意すべきは、「category型の罠」です。例えば、店舗ID(store_id)のように整数で表現される識別子を、大小関係のある連続値としてではなく、独立したカテゴリとして扱いたいケースは頻繁に発生します。AutoGluonのドキュメントでは、このような場合に dtype を明示的に category に変換する必要性が強調されています 8。
このドメイン知識を我々のPydantic設計に組み込みます。動的に生成される StaticFeaturesModel に加え、Pydanticモデルからpandas DataFrameへの変換ロジック(第4章で詳述)において、この型変換をインテリジェントに処理する仕組みを設けます。例えば、ユーザーがファクトリ関数に特徴量を指定する際に、('store_id', int, 'category') のようにタプルで型情報を渡せるように設計します。これにより、変換ブリッジは、最終的に生成されるpandas列の dtype を明示的に category に設定できます。これは、AutoGluonの挙動に関する重要な知識を、データパイプラインの早い段階で形式化し、自動化する試みです。


2.2 決定的な違い:既知の共変量 vs. 過去の共変量

AutoGluonにおける共変量の扱いは、その値が将来にわたって既知か否かによって厳密に区別されます。この区別は、モデルの選択と予測の実行可能性に直接影響するため、極めて重要です。

  • 既知の共変量 (Known Covariates): 予測期間中の値が、予測を行う時点で分かっている特徴量です。例えば、祝日、曜日、月、プロモーションの予定などがこれにあたります 8。これらの特徴量は、
    TimeSeriesPredictor のコンストラクタに known_covariates_names 引数でその列名をリストとして渡すことで指定されます 6。
    predict メソッドを呼び出す際には、予測期間に対応するこれらの共変量の未来の値を、known_covariates 引数で提供することが必須となります 19。

  • 過去の共変量 (Past Covariates): 予測期間中の値が未知である、時間変化する特徴量です。例えば、他の製品の売上、気温、降水量、あるいは目的変数自体を変換したもの(対数変換など)が該当します 8。これらは、学習データフレームにおいて、
    target 列と known_covariates_names で指定された列以外のすべての列として、AutoGluonによって自動的に解釈されます 16。

多くのユーザーがこの区別で混乱し、predict 時に未来の known_covariates を提供し忘れることで、実行時エラーに遭遇します 21。また、すべてのモデルが両方のタイプの共変量をサポートしているわけではないことにも注意が必要です 8。
この混乱を避けるため、我々のPydantic設計は、データ定義の段階でユーザーにこの区別を意識させることを強制します。Pydanticモデルに features: Dict のような汎用的なフィールドを設けるだけでは、この問題は解決しません。
そこで、動的に生成される TimeSeriesDataPoint モデル内に、known_covariates: Optional] = None と past_covariates: Optional] = None という、2つの明確に区別されたオプショナルな辞書フィールドを設けます。ファクトリ関数は、ユーザーから提供された共変量名のリストに基づき、これらのネストされたモデルのフィールドを移入します。
この設計により、各共変量がどちらのカテゴリに属するかがデータ構造レベルで明示的になり、曖昧さが排除されます。後の変換プロセス(第4章)では、これらの分離された辞書を用いて最終的なpandas DataFrameを構築し、known_covariates 辞書のキーから known_covariates_names リストを動的に生成して TimeSeriesPredictor に渡すことができます。これにより、入力データの定義段階で、予測時の要件まで考慮した、一貫性のあるデータ構造が保証されます。


第3章 予測パラメータのカプセル化と検証

この章では、AutoGluonの学習パラメータを管理するためのPydanticモデルの設計に焦点を当てます。設定値をコード内に散在させるのではなく、一つの検証可能なオブジェクトにカプセル化することで、設定ミスを早期に発見し、高価な fit プロセスの実行前にエラーを捕捉することが可能になります。


3.1 PredictionParameters モデル

ユーザー要件に基づき、eval_metric、time_limit、presets といった主要なパラメータを格納するための PredictionParameters という BaseModel を作成します。AutoGluonでは、eval_metric や prediction_length は TimeSeriesPredictor のコンストラクタ引数であり 6、一方で
time_limit や presets は fit メソッドの引数です 16。我々のモデルは、これらを一元的に管理することで、設定の全体像を明確にします。


3.2 typing.Literal による制約の強制

presets のようなパラメータを単純な str 型として受け入れると、"medium_quality" とすべきところを "medium-quality" とタイプミスするような、単純ながら発見が困難なエラーを引き起こす可能性があります。このようなエラーは、AutoGluonの内部で実行時エラーとして現れ、デバッグを困難にします。
この問題を解決するため、Pydanticが提供する typing.Literal を活用します。これにより、フィールドが取りうる値を特定の文字列リテラルの集合に限定することができます 25。これは、コンパイル時の型チェック(静的解析ツール使用時)や、IDEによるオートコンプリートを可能にし、開発者の体験を大幅に向上させます。
具体的な実装は以下のようになります。

  • presets: Optional[Literal['fast_training', 'medium_quality', 'high_quality', 'best_quality']]: AutoGluonが提供する主要なプリセットに限定します 2。

  • eval_metric: Optional]: AutoGluonのドキュメントに記載されている評価メトリクスの包括的なリストに基づきます 6。

  • time_limit: Optional[int] = Field(None, gt=0): time_limit が指定される場合、それが正の整数であることを保証します。

  • prediction_length: int = Field(..., gt=0): 予測期間は必須であり、正の整数でなければなりません。

これらの制約をまとめた PredictionParameters モデルのフィールドリファレンスを以下に示します。



表1: PredictionParameters フィールドリファレンス

この表は、ユーザーのための明確なクイックリファレンスとして機能します。AutoGluonのドキュメントに散在する情報を、検証可能で理解しやすい単一の形式に統合します。presets や eval_metric に許容される値を直接示すことで、ユーザーが外部ドキュメントを参照する必要性を減らし、一般的な設定ミスを防ぎます。

フィールド名
Pydantic型
説明
デフォルト値
prediction_length
int = Field(..., gt=0)
予測ホライゾン。未来の何タイムステップ先まで予測するかを指定します。正の整数である必要があります。
(必須)
eval_metric
Optional]
モデルの評価と選択に使用されるメトリクス。各メトリクスの詳細についてはAutoGluonのドキュメントを参照 6。
None (AutoGluonのデフォルト値を使用)
presets
Optional[Literal['fast_training', 'medium_quality', 'high_quality', 'best_quality']]
学習時間と精度のトレードオフを管理するための、事前設定されたモデルとハイパーパラメータのセットを指定する文字列 2。
None (AutoGluonのデフォルト値を使用)
time_limit
Optional[int] = Field(None, gt=0)
fit() 呼び出しのおおよその時間制限(秒)。指定する場合は正の整数である必要があります 16。
None (時間制限なし)



この PredictionParameters モデルを導入することで、設定値の検証がモデル学習の実行前にシフトされ、"fail-fast"(早期失敗)の原則が実現されます。これにより、計算リソースの無駄遣いを防ぎ、開発サイクルを加速させることができます。


第4章 統合実装:PydanticからAutoGluonへのブリッジ

これまでの章で設計した各コンポーネントを統合し、検証済みのPydanticモデルをAutoGluonが消費可能なオブジェクトに変換する、実践的なエンドツーエンドのワークフローを構築します。この章では、そのための具体的なコードと変換ロジックを提示します。


4.1 統合モデル ForecastingJob

ユーザーが直接操作する最上位のPydanticクラスとして ForecastingJob を定義します。このモデルは、これまでに設計したすべての要素を集約します。

Python

from typing import List, Dict, Any, Union, Optional, Type
from pydantic import BaseModel, Field, create_model, validator
from datetime import datetime

# (第3章で定義した PredictionParameters モデル)
class PredictionParameters(BaseModel):
    #...

# (第1, 2章の設計に基づく動的モデル)
# これらはファクトリ関数によって生成される
# TimeSeriesDataPoint: 動的にフィールドが追加される
# StaticFeaturesModel: 動的にフィールドが追加される
# PanelDataModel: 上記の動的モデルを含む

class ForecastingJob(BaseModel):
    """
    A top-level model representing a complete forecasting task.
    """
    job_id: str
    parameters: PredictionParameters
    panel_data: List[Any] # List を動的に扱う

    @validator('panel_data', pre=True, each_item=True)
    def validate_panel_data_structure(cls, v, values):
        # ここで panel_data の各要素が PanelDataModel の構造に従うか
        # 動的な検証を行うことができる
        # 簡単のため、ここでは実装を省略
        return v


panel_data の型を List[Any] としているのは、PanelDataModel 自体が動的に生成されるため、静的に型付けすることが難しいためです。実行時の検証は validator を用いて行うことが可能です。


4.2 変換ブリッジ:DataConverter クラス

このクラスは、ForecastingJob インスタンスを入力として受け取り、AutoGluonが必要とするオブジェクト群に変換する役割を担います。
DataConverter は、以下の主要なメソッドを持ちます。

  1. to_autogluon_train_data():

  • job.panel_data をイテレートします。

  • 各 PanelDataModel から、ネストされた TimeSeriesDataPoint のリストを展開します。

  • 最終的な「ロングフォーマット」のDataFrameの各行を表す辞書のリストを作成します。各辞書には item_id, timestamp, target、そして known_covariates と past_covariates のすべてのキーと値が含まれます。

  • この辞書のリストから、主要な pandas.DataFrame を生成します。

  • timestamp 列を pd.to_datetime を用いて日付時刻型に変換します。

  1. to_autogluon_static_features():

  • job.panel_data をイテレートします。

  • 各アイテムから item_id と StaticFeaturesModel インスタンスを抽出します。

  • 各 item_id の静的特徴量を含む辞書のリストを作成します。

  • このリストから pandas.DataFrame を生成し、item_id をインデックスとして設定します。

  • 第2章で議論したように、ユーザーが指定した静的特徴量の型情報に基づき、astype('category') などを適用します。

  1. get_predictor_init_args():

  • job.parameters から eval_metric と prediction_length を抽出します。

  • データ内の空でない最初の known_covariates 辞書のキーを調べることで、known_covariates_names を動的に決定します。

  • TimeSeriesPredictor のコンストラクタに渡す準備ができた引数の辞書を返します。

  1. get_predictor_fit_args():

  • job.parameters から time_limit と presets を抽出します。

  • fit() メソッドに渡す引数の辞書を返します。



表2: PydanticからAutoGluonへのコンポーネントマッピング

この表は、我々のPydantic設計構成要素とAutoGluonライブラリ内の概念・オブジェクトとの間の明確な対応関係を示す「ロゼッタストーン」として機能します。これにより、検証済み入力データがAutoGluonが必要とする特定の構造にどのように変換されるかについての明確なメンタルモデルをユーザーに提供します。

Pydanticコンポーネント
AutoGluonターゲット
変換ロジックの概要
PanelDataModel.item_id
メインpd.DataFrameのitem_id列およびstatic_features pd.DataFrameのインデックス
時間変化データと静的特徴量を結びつける主キーとして使用されます。
TimeSeriesDataPoint.timestamp
メインpd.DataFrameのtimestamp列
pandas.Timestampオブジェクトに変換されます。
TimeSeriesDataPoint.target
メインpd.DataFrameのtarget列
直接マッピングされます。列名はAutoGluonの慣例に従い、デフォルトで"target"となります 6。
StaticFeaturesModelのフィールド
TimeSeriesDataFrameに付随するstatic_features pd.DataFrameの列 8
動的に生成されたモデルから移入されます。dtype変換(例:categoryへ)がここで適用されます。
TimeSeriesDataPoint.known_covariatesのキー
TimeSeriesPredictorコンストラクタに渡されるknown_covariates_namesリスト 6
キーのリストが抽出され、将来既知の特徴量を予測器に通知するためにコンストラクタに渡されます。
TimeSeriesDataPoint.known_covariatesの値
メインpd.DataFrameの列
辞書はメインDataFrameの列にフラット化されます。
TimeSeriesDataPoint.past_covariatesの値
メインpd.DataFrameの列(past_covariatesとして解釈される)
辞書は列にフラット化されます。AutoGluonは、targetでもknown_covariateでもない列を自動的にpast_covariateとして扱います 16。
PredictionParameters
TimeSeriesPredictorコンストラクタおよびfitメソッドのキーワード引数
検証済みのパラメータがTimeSeriesPredictor(...)およびpredictor.fit(...)用の辞書にアンパックされます。




4.3 完全な実行ウォークスルー

このサブセクションでは、全プロセスを示す、コメント付きの完全なPythonスクリプトを提供します。

Python

import pandas as pd
from typing import List, Dict, Any, Union, Optional, Type, Tuple
from pydantic import BaseModel, Field, create_model, validator
from datetime import datetime, timedelta
from autogluon.timeseries import TimeSeriesDataFrame, TimeSeriesPredictor

# --- 1. Pydanticモデルの定義 (第1, 2, 3章) ---

# 3.1 & 3.2: パラメータモデル
class PredictionParameters(BaseModel):
    prediction_length: int = Field(..., gt=0)
    eval_metric: Optional] = None
    presets: Optional[Literal['fast_training', 'medium_quality', 'high_quality', 'best_quality']] = 'medium_quality'
    time_limit: Optional[int] = Field(None, gt=0)

# 1.2 & 2.2: 動的モデルファクトリ
def create_dynamic_timeseries_models(
    static_feature_defs: Dict],
    known_covariate_defs: Dict],
    past_covariate_defs: Dict]
) -> Tuple, Type]:

    KnownCovariatesModel = create_model(
        'KnownCovariatesModel',
        **{name: field_def for name, field_def in known_covariate_defs.items()}
    )
    PastCovariatesModel = create_model(
        'PastCovariatesModel',
        **{name: field_def for name, field_def in past_covariate_defs.items()}
    )
    StaticFeaturesModel = create_model(
        'StaticFeaturesModel',
        **{name: field_def for name, field_def in static_feature_defs.items()}
    )

    TimeSeriesDataPoint = create_model(
        'TimeSeriesDataPoint',
        timestamp=(datetime,...),
        target=(Union[float, int],...),
        known_covariates=(Optional[KnownCovariatesModel], None),
        past_covariates=(Optional[PastCovariatesModel], None)
    )

    PanelDataModel = create_model(
        'PanelDataModel',
        item_id=(Union[str, int],...),
        static_features=(StaticFeaturesModel,...),
        time_series_data=(List,...)
    )
    return PanelDataModel, StaticFeaturesModel

# 4.1: 統合ジョブモデル
class ForecastingJob(BaseModel):
    job_id: str
    parameters: PredictionParameters
    panel_data: List[Any] # 動的に生成されたPanelDataModelのリスト

# --- 4.2: データコンバータ ---
class DataConverter:
    def __init__(self, job: ForecastingJob, static_feature_types: Dict[str, str]):
        self.job = job
        self.static_feature_types = static_feature_types

    def to_autogluon_timeseries_dataframe(self) -> TimeSeriesDataFrame:
        # メインの時系列データフレームを作成
        records =
        for panel in self.job.panel_data:
            for dp in panel.time_series_data:
                record = {
                    'item_id': panel.item_id,
                    'timestamp': dp.timestamp,
                    'target': dp.target
                }
                if dp.known_covariates:
                    record.update(dp.known_covariates.dict())
                if dp.past_covariates:
                    record.update(dp.past_covariates.dict())
                records.append(record)
        main_df = pd.DataFrame(records)
        main_df['timestamp'] = pd.to_datetime(main_df['timestamp'])

        # 静的特徴量データフレームを作成
        static_records =
        for panel in self.job.panel_data:
            static_record = panel.static_features.dict()
            static_record['item_id'] = panel.item_id
            static_records.append(static_record)
        static_df = pd.DataFrame(static_records).set_index('item_id')

        # 2.1: 静的特徴量の型変換
        for col, dtype in self.static_feature_types.items():
            if col in static_df.columns:
                static_df[col] = static_df[col].astype(dtype)

        return TimeSeriesDataFrame(main_df, static_features=static_df)

    def get_predictor_init_args(self) -> dict:
        known_covariates_names =
        # 最初のデータポイントから既知の共変量の名前を取得
        if self.job.panel_data and self.job.panel_data.time_series_data:
            first_dp = self.job.panel_data.time_series_data
            if first_dp.known_covariates:
                known_covariates_names = list(first_dp.known_covariates.dict().keys())

        return {
            "prediction_length": self.job.parameters.prediction_length,
            "eval_metric": self.job.parameters.eval_metric,
            "known_covariates_names": known_covariates_names
        }

    def get_predictor_fit_args(self) -> dict:
        return {
            "presets": self.job.parameters.presets,
            "time_limit": self.job.parameters.time_limit
        }

# --- 実行例 ---
if __name__ == '__main__':
    # 0. 特徴量とパラメータを定義
    STATIC_DEFS = {
        'brand': (str,...),
        'store_id': (int,...)
    }
    KNOWN_COVARIATE_DEFS = {
        'is_holiday': (bool,...)
    }
    PAST_COVARIATE_DEFS = {
        'temperature': (float,...)
    }
    # 2.1: 静的特徴量の意図する型を指定
    STATIC_DTYPES = {'store_id': 'category'}

    # 1. ファクトリを使って動的モデルを生成
    PanelData, StaticFeatures = create_dynamic_timeseries_models(
        STATIC_DEFS, KNOWN_COVARIATE_DEFS, PAST_COVARIATE_DEFS
    )

    # 2. サンプルデータを作成 (JSONや辞書からロードすることを想定)
    job_data = {
        "job_id": "job-123",
        "parameters": {
            "prediction_length": 24,
            "eval_metric": "MAPE",
            "presets": "medium_quality",
            "time_limit": 600
        },
        "panel_data":
            },
            {
                "item_id": "product_B",
                "static_features": {"brand": "BrandY", "store_id": 102},
                "time_series_data": [
                    {
                        "timestamp": (datetime.now() - timedelta(days=2)).isoformat(),
                        "target": 200,
                        "known_covariates": {"is_holiday": False},
                        "past_covariates": {"temperature": 22.3}
                    },
                    {
                        "timestamp": (datetime.now() - timedelta(days=1)).isoformat(),
                        "target": 220,
                        "known_covariates": {"is_holiday": False},
                        "past_covariates": {"temperature": 23.0}
                    }
                ]
            }
        ]
    }
    # PanelDataモデルを使って各アイテムをパース
    job_data['panel_data'] =]

    # 3. ForecastingJobモデルで全体を検証
    try:
        forecasting_job = ForecastingJob.parse_obj(job_data)
        print("Pydantic validation successful.")
    except Exception as e:
        print(f"Pydantic validation failed: {e}")
        exit()

    # 4. DataConverterでAutoGluon用のデータに変換
    converter = DataConverter(forecasting_job, STATIC_DTYPES)
    train_data = converter.to_autogluon_timeseries_dataframe()
    predictor_init_args = converter.get_predictor_init_args()
    predictor_fit_args = converter.get_predictor_fit_args()

    print("\n--- Converted Data for AutoGluon ---")
    print("TimeSeriesDataFrame head:\n", train_data.head())
    print("\nStatic Features:\n", train_data.static_features)
    print("\nPredictor Init Args:", predictor_init_args)
    print("\nPredictor Fit Args:", predictor_fit_args)

    # 5. AutoGluon Predictorを初期化して学習
    predictor = TimeSeriesPredictor(
        target='target',
        **predictor_init_args
    )
    predictor.fit(
        train_data,
        **predictor_fit_args
    )

    print("\n--- AutoGluon Training Summary ---")
    print(predictor.fit_summary())

    # 6. 予測
    # 予測には未来の既知の共変量が必要
    future_known_covariates_list =
    for item_id in train_data.item_ids:
        last_timestamp = train_data.loc[item_id].index.max()
        future_timestamps = pd.date_range(last_timestamp + timedelta(days=1), periods=forecasting_job.parameters.prediction_length, freq=train_data.freq)
        for ts in future_timestamps:
            future_known_covariates_list.append({
                'item_id': item_id,
                'timestamp': ts,
                'is_holiday': (ts.weekday() >= 5) # 簡単な例
            })
    future_known_covariates = TimeSeriesDataFrame(pd.DataFrame(future_known_covariates_list))

    predictions = predictor.predict(train_data, known_covariates=future_known_covariates)
    print("\n--- Predictions ---")
    print(predictions.head())


第5章 専門家による推奨事項と高度な考察

本レポートで提示した設計は、単なるコードの実装にとどまらず、堅牢な機械学習システムを構築するための戦略的な選択を反映しています。この最終章では、その設計の背後にある思想を明確にし、将来的な拡張性について議論します。


5.1 上流での検証価値:早期かつクリーンな失敗

本設計の中核的な利点は、「上流での検証」という概念にあります。Pydanticレイヤーを導入しない場合、データ型の不一致、欠損値、不正な特徴量名といったデータ関連のエラーは、AutoGluonの fit メソッドの実行中、つまりプロセスのかなり後段で検出されます。その結果として表示されるエラーメッセージやスタックトレースは、しばしば長く、根本原因を特定するのが困難です。
対照的に、我々の設計ではPydanticを用いて厳格なデータコントラクトを定義することで、検証を可能な限り早い段階、すなわちデータのインスタンス化の瞬間にシフトさせます。Pydanticが送出する ValidationError は、"field 'presets' is not one of [...]" のように、問題の箇所と原因を人間が読んで理解しやすい形で明確に示します。この「fail-fast」(早期失敗)という哲学は、堅牢なソフトウェア開発およびMLOpsの基本原則であり、開発者のデバッグ時間を大幅に削減し、パイプライン全体の信頼性を向上させます。


5.2 保守性のための設計:ファクトリパターンの力

本設計で採用した動的なファクトリパターンは、保守性とスケーラビリティを確保するための鍵となります。新しいプロジェクトや特徴量セットごとにPydanticモデルをハードコーディングするというナイーブなアプローチと比較すると、その優位性は明らかです。

  • ハードコーディングアプローチ: このアプローチは、コードの重複を引き起こし、変更に対して脆弱です。新しい特徴量を追加するたびに、開発者は新しい検証コードを記述し、テストする必要があり、プロジェクトがスケールするにつれて管理が困難になります。

  • ファクトリパターン: 対照的に、我々のファクトリパターンは、単一で再利用可能、かつ十分にテストされたインフラストラクチャです。新しいデータスキーマには、コードの変更ではなく、設定(特徴量名のリストを渡す)によって適応します。これは、ソフトウェア設計における「オープン/クローズドの原則」(拡張に対しては開いており、修正に対しては閉じているべき)を体現しています。

このアプローチにより、データ検証のコアロジックを一度だけ実装すれば、それを様々なプロジェクトで再利用できるため、長期的な保守コストが大幅に削減されます。


5.3 予測と推論のための設計拡張

本設計の価値は、学習フェーズにとどまりません。predictor.predict() メソッドは、特に known_covariates に関して、独自のデータ要件を持っています。予測時には、予測期間(prediction_length)に対応する未来の known_covariates の値を TimeSeriesDataFrame として提供する必要があります 19。
我々のPydanticモデルは、この推論時の入力データを検証するためにも再利用できます。例えば、PredictionRequest という新しいPydanticモデルを設計することが考えられます。このモデルは、target 値を要求せず、代わりに未来の prediction_length 期間分の known_covariates を必須とします。これにより、予測を実行するためのAPIエンドポイントもまた、堅牢で検証されたものになります。
このように、Pydanticによるデータコントラクトの定義は、モデルのライフサイクル全体(学習、評価、推論)にわたって一貫したデータの品質と構造を保証するための強力な基盤となります。これは、実験的なスクリプトから本番稼働レベルの機械学習システムへと移行する上で、不可欠なステップです。
引用文献

  1. AutoGluon–TimeSeries: AutoML for Probabilistic Time Series Forecasting - arXiv, 7月 13, 2025にアクセス、 https://arxiv.org/pdf/2308.05566

  2. Easy and accurate forecasting with AutoGluon-TimeSeries | Artificial Intelligence - AWS, 7月 13, 2025にアクセス、 https://aws.amazon.com/blogs/machine-learning/easy-and-accurate-forecasting-with-autogluon-timeseries/

  3. AutoGluon Time Series - Forecasting Quick Start, 7月 13, 2025にアクセス、 https://auto.gluon.ai/stable/tutorials/timeseries/forecasting-quick-start.html

  4. Forecasting Time Series - Quick Start — AutoGluon Documentation 0.6.0 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.6.0/tutorials/timeseries/forecasting-quickstart.html

  5. autogluon.timeseries.TimeSeriesDataFrame, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.8.0/api/autogluon.timeseries.TimeSeriesDataFrame.html

  6. autogluon.timeseries.TimeSeriesPredictor - AutoGluon 1.3.2 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/dev/api/autogluon.timeseries.TimeSeriesPredictor.html

  7. autogluon.timeseries.TimeSeriesPredictor, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.8.1/api/autogluon.timeseries.TimeSeriesPredictor.html

  8. Forecasting Time Series - In Depth - AutoGluon 1.3.2 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/dev/tutorials/timeseries/forecasting-indepth.html

  9. Forecasting Time Series - In Depth — AutoGluon Documentation 0.7.0 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.7.0/tutorials/timeseries/forecasting-indepth.html

  10. autogluon.timeseries.TimeSeriesDataFrame, 7月 13, 2025にアクセス、 https://auto.gluon.ai/1.0.0/api/autogluon.timeseries.TimeSeriesDataFrame.html

  11. AutoGluon-TimeSeries: A robust time-series forecasting library by Amazon Research : r/datascience - Reddit, 7月 13, 2025にアクセス、 https://www.reddit.com/r/datascience/comments/1957lru/autogluontimeseries_a_robust_timeseries/

  12. Pydantic Dynamic Model Creation in FastAPI - Orchestra, 7月 13, 2025にアクセス、 https://www.getorchestra.io/guides/pydantic-dynamic-model-creation-in-fastapi

  13. Pydantic: Dynamically creating a model, with field types, examples and descriptions., 7月 13, 2025にアクセス、 https://antipatterns.blogspot.com/2025/02/pydantic-dynamically-creating-model.html

  14. Dynamically adding fields inplace to an existing model classes? · Issue #1937 - GitHub, 7月 13, 2025にアクセス、 https://github.com/pydantic/pydantic/issues/1937

  15. Forecasting Time Series - In Depth - AutoGluon 0.8.2 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.8.0/tutorials/timeseries/forecasting-indepth.html

  16. TimeSeriesPredictor.fit - AutoGluon 1.3.2 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/dev/api/autogluon.timeseries.TimeSeriesPredictor.fit.html

  17. TimeSeriesPredictor.fit - AutoGluon 1.1.0 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/1.1.0/api/autogluon.timeseries.TimeSeriesPredictor.fit.html

  18. Forecasting Time Series - In Depth - AutoGluon 0.8.1 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.8.1/tutorials/timeseries/forecasting-indepth.html

  19. TimeSeriesPredictor.predict - AutoGluon 0.8.1 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.8.1/api/autogluon.timeseries.TimeSeriesPredictor.predict.html

  20. TimeSeriesPredictor.fit - AutoGluon 1.0.0 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/1.0.0/api/autogluon.timeseries.TimeSeriesPredictor.fit.html

  21. [NOT a BUG] In TemporalFusionTransformer: Past covariate not being recognized in TimeSeriesPredictor.predict · Issue #4951 - GitHub, 7月 13, 2025にアクセス、 https://github.com/autogluon/autogluon/issues/4951

  22. Is future known_covariates needed to do the predict ? #2778 - GitHub, 7月 13, 2025にアクセス、 https://github.com/autogluon/autogluon/discussions/2778

  23. Forecasting Time Series - Model Zoo - AutoGluon 1.3.2 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/stable/tutorials/timeseries/forecasting-model-zoo.html

  24. TimeSeriesPredictor.fit - AutoGluon 0.8.1 documentation, 7月 13, 2025にアクセス、 https://auto.gluon.ai/0.8.1/api/autogluon.timeseries.TimeSeriesPredictor.fit.html

  25. Standard Library Types - Pydantic, 7月 13, 2025にアクセス、 https://docs.pydantic.dev/latest/api/standard_library_types/

  26. Field Types - Pydantic, 7月 13, 2025にアクセス、 https://docs.pydantic.dev/1.10/usage/types/

  27. Literals errors include validation errors for wrong literal type · Issue #2717 - GitHub, 7月 13, 2025にアクセス、 https://github.com/samuelcolvin/pydantic/issues/2717

  28. `Literal` annotation does not render type to json-schema · Issue #8905 - GitHub, 7月 13, 2025にアクセス、 https://github.com/pydantic/pydantic/issues/8905

いいなと思ったら応援しよう!