Mímisbrunnr知恵の泉

← MLOps 一覧

🎓 レベル:発展 | 重要度:B(推奨)

📎 前提:特徴量エンジニアリングのパイプライン化 | 関連:学習推論スキューの防止

要点(BLUF)

1. なぜ専用基盤が要るのか

特徴量パイプライン(特徴量エンジニアリングのパイプライン化)を fit/transform で正しく作っても、運用が大きくなると次の問題が出ます:

特徴量ストアは、特徴量を一度定義したら学習・推論で同じ実体を共有することでこれを解きます。

2. オフラインストアとオンラインストアの二面性

flowchart TB
  D["特徴量定義(1つのコード)"] --> P["特徴量計算パイプライン"]
  P --> OFF["オフラインストア(履歴・大容量)"]
  P --> ON["オンラインストア(最新値・低遅延KVS)"]
  OFF -->|"ポイントインタイム結合"| TR["学習データ生成"]
  ON -->|"低遅延ルックアップ"| SV["オンライン推論"]
オフラインストアオンラインストア
用途学習データ生成・分析オンライン推論
内容特徴量の全履歴エンティティごとの最新値
求められる性能大容量・スループット低遅延(数ミリ秒)
実体例データウェアハウス・列指向KVS・インメモリ

同じ特徴量定義から両方を生成するので、学習と推論で値がずれません。

3. ポイントインタイム結合(point-in-time join)

学習データを作るとき、各ラベル(予測対象イベント)の発生時刻における特徴量を取らねばなりません。「今の最新値」を全行に貼ると、未来の情報が過去のラベルに漏れます(リーク)。

flowchart LR
  E["ラベル:2026-03-01のイベント"] --> J["この時刻 以前 の最新特徴量だけ取得"]
  F["特徴量履歴(時刻つき)"] --> J
  J --> R["時間整合した学習行"]

4. 動く最小例:オフライン/オンラインストアとポイントインタイム結合

最小の特徴量ストアを作り、(a) オンラインは最新値を返す、(b) 学習データ生成はポイントインタイム結合で過去時点の値を返す、ことを確認します。

import pandas as pd

# 特徴量の履歴(user_id, 観測時刻, 特徴量値)
feature_history = pd.DataFrame({
    "user_id": [1, 1, 1, 2, 2],
    "event_time": pd.to_datetime(
        ["2026-01-01", "2026-02-01", "2026-03-15", "2026-01-10", "2026-03-01"]),
    "spend_30d": [100, 150, 220, 80, 130],
})

# オンラインストア:各userの最新値だけ(推論用)
online = (feature_history.sort_values("event_time")
          .groupby("user_id").tail(1).set_index("user_id")["spend_30d"])
print("=== オンラインストア(最新値・推論用)===")
print(online.to_string())

# ラベル:予測したいイベントとその発生時刻
labels = pd.DataFrame({
    "user_id": [1, 2],
    "label_time": pd.to_datetime(["2026-02-15", "2026-02-15"]),
    "y": [1, 0],
})

# ポイントインタイム結合:label_time 以前 の最新特徴量を貼る
def point_in_time_join(labels, history):
    rows = []
    for _, r in labels.iterrows():
        past = history[(history.user_id == r.user_id) &
                       (history.event_time <= r.label_time)]
        val = past.sort_values("event_time")["spend_30d"].iloc[-1] if len(past) else None
        rows.append(val)
    out = labels.copy(); out["spend_30d"] = rows
    return out

train = point_in_time_join(labels, feature_history)
print("\n=== 学習データ(ポイントインタイム結合・2026-02-15時点)===")
print(train.to_string(index=False))

出力:

=== オンラインストア(最新値・推論用)===
user_id
2    130
1    220

=== 学習データ(ポイントインタイム結合・2026-02-15時点)===
 user_id label_time  y  spend_30d
       1 2026-02-15  1        150
       2 2026-02-15  0         80

出力の意味:オンラインストアは推論用に各ユーザーの最新値(user1=220, user2=130)を返します。一方、学習データ生成では 2026-02-15 時点で実際に手に入っていた値だけを使うので、user1 は 150(3-15 の 220 はまだ未来なので使わない)、user2 は 80(3-01 の 130 は未来)になりました。もし最新値 220/130 を学習に貼っていたら、未来の情報が漏れてオフラインでは高精度・本番で崩れる典型的リークになります。特徴量ストアはこの時間整合を構造的に守ります。

5. 運用の勘所

なぜそうするのか

特徴量ストアの本質的な価値は「学習と推論で同じ特徴量を、時間整合を保って供給する」ことです。手作りでこれを保つのは、規模が大きくなると人間には不可能になります(二重実装・リーク・再実装が必ず混入する)。専用基盤に時間整合と一貫供給を任せることで、スキューとリークという2大事故を構造的に防げます。

⚠️ よくある落とし穴

対応 lab

関連ノート