スクラッチから始めるフィーチャーストア:最小限の実装
Python、DuckDB、Parquet、Redis、FastAPIを使って最小限のフィーチャーストアを構築し、レジストリ、オフラインストア、オンラインストア、マテリアライゼーション、取得APIの5つのコンポーネントを解説。AI時代の設計変更についても考察。
フィーチャーストアは、機械学習の本番環境におけるデータ不整合問題を解決する基盤インフラです。多くのチームは、実際にデプロイして初めてその必要性に気づきます。ノートブックでは完璧に動作していた不正検知モデルが本番で静かに壊れる、サポートエージェントがユーザーを知らずに一般的な回答をする、レコメンダーパイプラインが同じ「30日間の支出」計算を3つのジョブで重複し、そのうち2つが食い違う——こうした問題の根源はトレーニング-サービス間のスキュー(訓練と推論での特徴値のずれ)にあります。フィーチャーストアは、特徴を一度定義し、トレーニング用とサービス用の2つの形で保存し、両者を同期させることでこの問題を解決します。
この記事では、Python、DuckDB、Parquet、Redis、FastAPIを使って最小限のフィーチャーストアをゼロから構築する方法を紹介します。その後、AIアプリケーションがフィーチャーストアの実際の使い方をどう変えるのかを見ていきます。完全なコードは約200行と短く、各コンポーネントを順に解説します。
フィーチャーストアが解決する現代的な課題
従来のフィーチャーストアは主にトレーニング-サービス間のスキューに対処してきました。訓練セットを構築するSQLと推論時に実行されるコードパスが異なるため、値が徐々にずれていく問題です。オフライン/オンライン分割が標準的な対策です。しかし、近年の課題はより広範です。大規模言語モデル(LLM)エージェントやRAGパイプラインは、推論時に毎回10ミリ秒未満で構造化ユーザーコンテキストを必要とします。LLMにはユーザーの記憶がなく、パーソナライズされた出力を得るには、ユーザーのプランティア、最近のアクティビティ、アカウント状態をプロンプトに注入し、それらの値を高速かつ一貫して返すシステムが必要です。これこそがフィーチャーストアのオンラインストアと取得APIが提供する機能です。したがって、私たちが構築するフィーチャーストアは、従来の予測機械学習ユースケースとLLMコンテキストユースケースの両方を処理し、同じ5つのコンポーネントで両方に対応します。
5つのコアコンポーネント
- 特徴レジストリ:特徴をコードとして定義します。Pythonのデータクラス(dataclass)を使用して、各特徴の名前、エンティティ、データ型、データソース(Parquetファイルパス)を宣言します。レジストリは他のすべてのコンポーネントが読み取る単一の情報源であり、特徴の名前変更や型変更、ソース変更はここで一元管理されます。本番システムでは、レジストリはGitリポジトリにチェックインされたYAMLファイルやPythonモジュールとなり、変更にはコードレビューが必須です。
- オフラインストア:Parquetファイルをストレージ層とし、DuckDBをクエリエンジンとして使用します。オフラインストアは全特徴値の完全な履歴を保持します。DuckDBはParquetファイルを直接読み取るため、別途データベースを実行する必要はありません。ASOF LEFT JOINを使用してポイントインタイムジョインを実現し、各トレーニング行に対して、そのイベント時点以前に存在した最新の特徴値のみを選択します。これによりデータ漏洩を防止します。トレーニングやファインチューニングを行うモデルにとっては必須の機能です。純粋な推論時LLMユースケースではこの関数を呼び出さないかもしれませんが、それでもオフラインストアはバックフィル、評価データセット、監査の源泉として重要です。
- オンラインストア:Redis上に構築され、エンティティごとに最新の値のみを保持します。Redisのハッシュルックアップはサブミリ秒で完了します。キーの形式はentity:entity_id、値は全特徴を含むハッシュです。1回のHMGETで複数の特徴を一度に取得できます。例えば、ローカルのRedisインスタンスで3つの特徴を取得する場合、1ミリ秒未満で完了します。
- マテリアライゼーションパイプライン:定期的にオフラインストアから最新の値をオンラインストアにプッシュします。QUALIFY句を使ってエンティティごとに最新の行を選択し、同一ユーザーの全特徴を1回のRedis書き込みにまとめてラウンドトリップを削減します。更新頻度は特徴ごとに異なります:watch_count_30dは毎時、last_genreはほぼリアルタイム、user_segmentは毎日。本番システムでは、このパイプラインはAirflow、cron、またはストリーミングジョブでスケジュール実行されます。
- 取得API:FastAPIベースで、型付きのHTTPエンドポイント(例:POST /get-online-features)を公開し、モデルやLLMアプリケーションから呼び出されます。パーソナライズされたLLMレコメンダーの例として、ストリーミングサービスを考えます。ユーザーがアプリを開くと、エンドポイントはユーザーIDからユーザーセグメント、30日間の視聴数、最後に視聴したジャンルを返し、これらがLLMプロンプトに注入されて「次に見るべき作品」を生成します。フィーチャーストアは「ユーザー8a2f」というIDをLLMが利用可能な構造化コンテキストに変換します。
フィーチャーストアとベクトルデータベースの境界
ベクトルデータベース(Pinecone、Weaviate、pgvectorなど)は、推論時にモデルの前段に配置される点でフィーチャーストアと似ていますが、解決する検索問題は異なります。ベクトルデータベースは類似した過去の視聴セッションを返すのに対し、フィーチャーストアはユーザーのセグメントや最近のカウントを返します。本格的なLLMスタックは両方を使用します。ベクトルデータベースが類似性検索を、フィーチャーストアが構造化キーバリュールックアップを担当し、プロンプトで両者を組み合わせます。補完関係であり、置き換えられるものではありません。
よくあるアンチパターン
- モデルサービス内で特徴を計算する:トレーニングノートブックとAPIで同じロジックが重複し、定義が数ヶ月で乖離する。
- オンラインストアを信頼できる情報源とみなす:Redisは障害時にデータを失う可能性があり、オフラインストアが正規の情報源であり、オンラインストアはキャッシュにすぎない。
- レジストリをスキップする:複数のチームが独立にactive_userを定義し、ダッシュボードがモデルと一致しなくなる。
- ベクトルデータベースをフィーチャーストアと呼ぶ:エンティティキーによる構造化ルックアップができない。
- ポイントインタイムジョインなしでバックフィルする:トレーニングセットは素晴らしく見えるが、本番モデルは壊れており、そのギャップはデータ漏洩による。
Feast、Tecton、Databricksとの比較
私たちの約200行のコードは、これらの本番システムをミニチュアで実現しています。同じパターンでさらに進むなら、セルフホスト型のFeastが最も近い比較対象です。TectonとDatabricksはマネージドパスを提供し、LLM向けの明示的な機能(TectonのLLM用特徴取得API、Databricksの複合生成AI向け特徴提供)を持ちます。選択は、どれだけ自分で運用したいか、既存のスタックがDatabricksにあるかどうかに大きく依存します。
結論
実用的なフィーチャーストアは5つのコンポーネント(レジストリ、オフラインストア、オンラインストア、マテリアライゼーション、取得API)で構成されます。一度構築することで、本番システムの設計理由を理解できます。また、AIが設計をどう変えるかも明らかになります:オンライン検索パスがLLMの接点となり、トレーニングや評価時にはポイントインタイムジョインが重要であり、ベクトルデータベースはフィーチャーストアの内部ではなく隣に位置します。これらの部品を押さえれば、最小版をFeast、Tecton、Databricksに置き換えるのは、基本的にレジストリの移行に過ぎません。システムの形状は同じままです。
——著者:Nate Rosidi(データサイエンティスト、製品戦略専門家、StrataScratch創業者)