代表的な面接トピック

データエンジニアリング面接:Point-in-Time Feature Join をどのように設計するか?

データ難しい
Offer.cc 編集チーム公開日 更新日

質問

オフライン学習用の特徴量ストア(Feature Store)を設計し、すべての学習サンプルがその時点で利用可能だった特徴量のみを使用する仕組みを説明してください。

プロンプトとコンテキスト

あなたは広告クリック予測のための特徴量プラットフォームを担当しています。各学習行には entity_idlabel_ts、およびラベルが含まれています。イベントの到着に伴って特徴量の値は変化しますが、オンラインリクエストでは最新値の低レイテンシな読み取りが必要です。オフラインの point-in-time join と、バックフィル、遅延イベント、およびオンラインサービングをどのように処理するかを説明してください。エンティティには複数の特徴量バージョンがあり、各レコードには計算時間があり、ラベル時間はビジネスイベント発生時刻であると仮定します。

面接官が評価するポイント

評価のポイントは、ストレージ技術を挙げる前に「その時点で利用可能」の定義を行っているかどうかです。低評価の回答は、単にデータウェアハウスと Redis を挙げます。高評価の回答は、不変条件 feature_ts <= label_ts を提示し、エンティティごとに最新の対象バージョンを選択し、履歴が存在しない場合は null を返し、履歴用の学習ビューとオンラインの最新値を明確に分離します。また、どの遅延イベントが再計算を引き起こすか、およびトレーニングとサービングの歪み(training-serving skew)をどのように検出するかについても説明します。

明確化のための質問

  • ラベル時間はビジネスイベントの発生時刻ですか、それともラベルが書き込まれた時刻ですか? この境界によって結合ロジックが変わります。
  • イベント発生後、どのくらいの時間まで特徴量が到着しても利用可能と見なせますか? 厳密なリアルタイムセマンティクスには可用性タイムスタンプが必要です。
  • 学習はすべての履歴バージョンを再現する必要がありますか、それとも直近のウィンドウのみで十分ですか? 前者は完全な履歴が必要であり、後者はルックバック制限を使用できます。
  • オンラインサービングで必要なのは現時点での最新値ですか、それとも特定のイベント時点の値ですか? 後者の場合は単一のキャッシュ値ではなく、タイムスタンプ付きの API が必要です。

30秒での回答

「私はすべての特徴量レコードに対して、エンティティキー、特徴量バージョン、および利用可能になった時間を保持します。学習データを作成する際は、エンティティごとに as-of join を実行し、feature_ts <= label_ts を満たす最新のレコードを保持します。該当するものがない場合、値は null のままにします。オフラインストアは履歴を保持し、オンラインストアは最新値を提供しますが、どちらも同一の特徴量定義およびマテリアライゼーションフローから生成されます。遅延イベントは再計算キューに入り、影響を受けたウィンドウはバージョン管理されたスナップショットに再構築されます。そして、join の null 率、鮮度、オンライン/オフラインの分布、モデルへの影響を監視します。」

ステップバイステップの解決策

1. 時間の不変条件を定義する

サンプル s=(e, t_label) と特徴量履歴 H_e に対して、C={h∈H_e | h.feature_ts ≤ t_label} を選択し、arg max feature_ts(C) を返します。これにより、未来の値が学習に混入するのを防ぎます。ビジネス上の意味が「データが利用可能であった」ことである場合、追加の制約として available_ts を使用します。イベント時間だけでは楽観的すぎる場合があります。

2. 履歴と結合をモデル化する

エンティティキー、特徴量名またはバージョン、値、feature_tsavailable_ts、ソースバッチ、および品質状態を保存します。学習行には label_ts を持たせます。as-of join のためにエンティティでパーティションを切り、時間で並べ替えます。タイムスタンプが同一である場合の決定論的なタイブレーカーとして、ソースシーケンスまたは書き込みバッチを使用します。完全一致の時間結合(exact-time join)を行うとほとんどの行が欠落し、最新行を単純に選択すると未来の情報がリークします。

3. オフラインパスとオンラインパスを分離する

オフラインストアはバッチ学習用に完全な履歴を保持し、オンラインの Key-Value ストアは最新値を高速に返します。共通の定義から両方のパスが生成され、そのバージョン、入力スナップショット、およびマテリアライゼーションウォーターマークが記録されます。有界な古さ(bounded staleness)が許容される場合、オンラインレスポンスには最新値とその feature_ts を含めることができ、呼び出し側がフォールバックするかどうかを判断できるようにします。これを学習時の値であると見せかけてはなりません。

4. 遅延イベント、バックフィル、バージョンを処理する

遅延イベントはイミュータブル(不変)な Raw レイヤーに書き込み、影響を受けるエンティティと時間ウィンドウの再計算をキューに入れます。リリース済みモデルで使用されているスナップショットを上書きするのではなく、新しい学習用スナップショットを再構築します。ジョブキーとして入力バッチと定義バージョンを使用して、バックフィルの冪等性を担保します。特徴量ロジックが変更された場合は、新しいバージョンを公開しつつ古いバージョンも保持して、過去の実験の再現性を維持します。

5. 検証と監視

オフライン行をサンプリングして feature_ts <= label_ts をアサートし、対象となる履歴が存在しないエンティティの null 率を追跡します。オンラインレイテンシ、特徴量の経過時間(age)、マテリアライゼーション遅延、およびエラーを監視します。学習時とサービング時の分布を比較し、null 処理やウィンドウ境界の違いを検出します。学習を再現できるように、サンプルスナップショット、定義バージョン、および入力ウォーターマークをモデルのメタデータに保存します。

6. フル機能の特徴量ストアを構築すべきでない場合を理解する

小規模なバッチ専用プロジェクトの場合、ウィンドウ関数での並べ替えと as-of join を組み合わせたデータウェアハウスのクエリの方がシンプルです。ミリ秒単位のサービング、特徴量の再利用、および継続的なバックフィルが必要な場合にのみ、オフライン履歴レイヤー、オンライン Key-Value レイヤー、レジストリ、およびマテリアライザーを追加します。すべての特徴量をリアルタイムで計算すると、状態、コスト、および一貫性のリスクが増大します。一方で最新値のみを保持すると、過去の学習データの再構築が不可能になります。

質の高い模範解答

私は「その時点で可視であったこと」を厳格な制約として扱います。各エンティティの特徴量履歴には feature_ts を持たせ、到着がイベントより遅れる可能性がある場合は available_ts も保存します。(entity_id, label_ts) に対して、as-of join によりタイムスタンプが label_ts より後にならない最新のバージョンを選択します。プロダクトのセマンティクスが実際の可用性を要求する場合は、available_ts <= label_ts も必須とします。最新の行を直接結合するのは、未来の更新が過去のサンプルにリークするため安全ではありません。

オフラインレイヤーは学習用に完全な履歴を保持し、オンラインレイヤーは低レイテンシ推論用に最新値を保持しますが、どちらも同じバージョン管理された定義によって駆動されます。遅延イベントは Raw レイヤーに格納され、影響を受けるウィンドウの冪等な再計算をトリガーします。リリース済みモデルのスナップショットは不変のままであり、結果は新しいバージョンとなります。時間の不変条件、null 率、鮮度、オンライン/オフラインの分布、およびモデルへの影響を検証します。低レイテンシサービングの要件がない場合は、オンラインレイヤーを省略してバッチ設計にとどめます。

よくある間違い

  • 間違い: 最新の特徴量行を結合する → 未来の更新が過去のサンプルに入り込み、オフラインメトリクスが過大評価される → エンティティとラベル時間に基づく as-of join を使用する。
  • 間違い: イベント時間は保存するが利用可能時間は保存しない → イベント発生自体は早くてもラベル発生時点ではまだ見えていなかった可能性がある → レイテンシやバッチ処理が重要な場合は available_ts を記録する。
  • 間違い: バックフィル中に特徴量の履歴を上書きする → リリース済みモデルの再現が不可能になる → 入力バッチと定義バージョンをキーとする不変なスナップショットを作成する。
  • 間違い: オンラインとオフラインの変換処理を別々に管理する → null 処理やウィンドウ境界がズレて training-serving skew が発生する → 定義を共有するか、ゴールデンサンプルを用いて両方のパスをテストする。

フォローアップの質問と回答

特徴量イベントがラベルの後に到着しましたが、そのイベント時間はラベルより前です。学習に使用できますか?

イベント時間だけを根拠に使用することはできません。ラベル時点でオンラインサービングから参照できなかった場合は、available_ts <= label_ts を満たす必要があります。そうでなければ、学習はサービングパスが持っていなかった情報をシミュレートすることになります。両方のタイムスタンプを保持し、プロダクトの仕様要件に基づいて厳格なルールか緩和されたルールかを選択してください。

オンラインパスで過去のイベント時点の特徴量が必要です。最新値のキャッシュで十分ですか?

不十分です。最新値のキャッシュは「現時点での最新」を返すものであり、過去のタイムスタンプ時点の値を返すものではありません。タイムスタンプ付きの履歴を公開するか、リクエストに先立って必要なバージョンを事前マテリアライズしてください。レイテンシバジェットがオンラインでの履歴読み取りをサポートできない場合は、オフラインレイヤーを使用します。

遅延イベントが継続的に到着します。再計算の範囲をどのように限定しますか?

エンティティ、時間ウィンドウ、および特徴量バージョンごとにリクエストをまとめ(coalesce)、最大ルックバック期間と優先度を設定します。そのウィンドウを超えたイベントはバッチ処理または手動プロセスにルーティングし、再構築されなかったモデルバージョンを記録します。すべての履歴を無制限に追従するのではなく、影響を受ける行数、再計算時間、およびキューの経過時間を監視します。

公開情報ソース

関連する質問