フィーチャーストア

GitLabのSnowflakeネイティブのML機能管理・サービング・取得のためのフィーチャーストア。

フィーチャーストア

フィーチャーストアは、機械学習特徴量の管理・計算・サービングを行うGitLabの中央集権的なシステムです。Snowflakeのネイティブフィーチャーストアとパラメーター化されたSQL ユーザー定義関数(UDF)を使用して、事前にすべての履歴データを計算することなく、任意の日付とルックバックウィンドウに対してオンデマンドで特徴量を生成します。

ソースコードはsnowflake-feature-storeプロジェクトにあります。

仕組み

特徴量は3つのパラメーター(FEATURE_DATELOOKBACK_WINDOW_VALUELOOKBACK_WINDOW_UNIT)を受け入れるSQL UDFとして定義されます。UDFがデプロイされると、システムは渡されたパラメーターの値に基づいて動的ビューを作成し、そのビューをSnowflakeのフィーチャーストアのFeature Viewとして登録します。データサイエンティストは、必要なfeature views、モデリング対象を定義するspineクエリ、ユースケースの日付/ルックバックパラメーターを指定することで特徴量を取得します。

データフロー: 既存のdbtテーブル → UDF(パラメーター化されたSQL)→ 動的ビュー → Feature Views → フィーチャーストアAPI → Python / Jupyter

主要概念

エンティティ

エンティティはfeature viewのプライマリキー(Joinキー)です。フィーチャーストアは現在2つのエンティティをサポートしています:

  • dim_namespace_id — GitLabネームスペース識別子
  • dim_crm_account_id — アカウント識別子

エンティティはentities/entities.yamlで定義され、デプロイ時にSnowflakeに登録されます。

Feature Views

Feature Viewは単一のUDFに裏付けられた関連する特徴量の名前付きコレクションです。各feature viewはドメイン固有のfeature_views.yamlファイルで設定されます:

feature_views:
  namespace_product_stage:
    version: "1.0"
    udf_name: "NAMESPACE_PRODUCT_METRICS_UDF"
    entity: "dim_namespace_id"
    timestamp_col: "feature_date"
    description: "Monthly product adoption metrics for namespaces"
    updated_by: "kdietz"
    updated_at: "2026-04-15"

UDF(ユーザー定義関数)

UDFは特徴量計算ロジックを含むSQL関数です。すべてのUDFは3つのパラメーターを持つ標準的なシグネチャに従います:

CREATE OR REPLACE FUNCTION MY_FEATURE_UDF(
    FEATURE_DATE DATE DEFAULT CURRENT_DATE() - 1,
    LOOKBACK_WINDOW_VALUE INT DEFAULT 1,
    LOOKBACK_WINDOW_UNIT VARCHAR DEFAULT 'month'
)
RETURNS TABLE(
    dim_namespace_id VARCHAR,
    feature_date DATE,
    ...
)
LANGUAGE SQL
AS $$ ... $$;

3つのパラメーターはすべてのUDFシグネチャに必要です(SQL本体で使われない場合も含む)。エンティティ列はfeature view間で一貫性を保つために常にVARCHARにキャストする必要があります。

特徴量の説明

各feature viewには、すべての出力列をドキュメント化するコンパニオンYAMLファイルがあります:

descriptions:
  feature_A: "Description of Feature A"
  feature_B: "Description of Feature B"

特徴量ドメイン

特徴量は保守性と発見可能性のためにビジネスドメインごとに整理されています:

ドメイン説明Feature Viewの例
product製品の使用状況、採用namespace_product_usage, namespace_duo_saas_usage
sales商談、活動、請求account_sales_activities
customer_success顧客ヘルスとエンゲージメントaccount_health_scores
supportサポートチケットメトリクスaccount_support_tickets
marketingマーケティングアトリビューションaccount_marketing_touchpoints

各ドメインディレクトリは次の構造に従います:

features/{domain}/
├── feature_views.yaml               # Feature view定義
├── descriptions/                    # feature viewごとのドキュメント
│   └── {feature_view_name}.yaml
└── udfs/                            # SQL UDF
    └── {feature_view_name}.sql

リポジトリ構造

snowflake-feature-store/
├── notebooks/
│   ├── update_feature_store.ipynb   # 特徴量のデプロイ(開発ワークフロー)
│   └── serving_features.ipynb       # MLモデル用の特徴量取得
├── src/
│   ├── feature_store_manager.py     # コアオーケストレーター
│   ├── update_features.py           # デプロイ用CLIエントリポイント
│   ├── detect_changes.py            # Gitベースの変更検出
│   ├── ci_deploy.py                 # CI/CDラッパー
│   └── utils/
│       ├── config_loader.py         # YAML設定管理
│       └── udf_type_validator.py    # UDF戻り型検証
├── features/{domain}/               # ドメイン整理された特徴量
├── entities/entities.yaml           # エンティティ定義
├── config/snowflake_config.yaml     # 環境設定
├── .gitlab-ci.yml                   # CI/CDパイプライン
├── Dockerfile                       # CI用コンテナ
└── pyproject.toml                   # Python依存関係

環境

フィーチャーストアは3つの環境を使用します:

環境データベース目的ワークフロー
Dev{ROLE}_PROD (例: KDIETZ_PROD)個人の開発とテストnotebooks/update_feature_store.ipynbを使用
Staging共有CI環境本番前の検証MR CIパイプライン経由でトリガー
ProductionFEATURE_STORE.SF_FEATURE_STORE本番特徴量サービングmainへのマージ時に自動的にデプロイ

はじめに

前提条件

  • Python 3.12+
  • JupyterLab、Jupyter Notebook、またはVSCode
  • 適切な権限を持つSnowflakeアカウント
  • Snowflake接続のためのdbtプロファイル設定

インストール

git clone https://gitlab.com/gitlab-data/data-science-projects/snowflake-feature-store.git
cd snowflake-feature-store
uv sync
./.venv/bin/jupyter lab build --minimize=False
./.venv/bin/jupyter lab --port=8888

Snowflake接続

ユースケースに応じて~/.dbt/profiles.ymlにエントリを追加します。

特徴量のサービング(本番への読み取り専用アクセス):

gitlab-snowflake:
  outputs:
    feature_store_serve:
      type: snowflake
      threads: 8
      account: gitlab
      user: [email protected]
      role: YOUR_ROLE
      database: FEATURE_STORE
      warehouse: DEV_XS
      schema: SF_FEATURE_STORE
      authenticator: externalbrowser

特徴量の開発または修正(個人の開発データベース):

gitlab-snowflake:
  outputs:
    feature_store_dev:
      type: snowflake
      threads: 8
      account: gitlab
      user: [email protected]
      role: YOUR_ROLE
      database: {ROLE}_PROD
      warehouse: DEV_XS
      schema: SF_FEATURE_STORE
      authenticator: externalbrowser

開発ワークフロー

特徴量の追加または修正は、ローカル開発、ステージング検証、本番デプロイの3段階のプロセスに従います。

ステップ1: ローカル開発

すべての特徴量開発は、個人のSnowflakeデータベースとupdate_feature_store.ipynbノートブックを使用してローカルで開始します。

特徴量ファイルを作成または修正する:

  1. UDF — 適切なドメインのudfs/ディレクトリにSQLファイルを追加または編集します:

    -- features/product/udfs/my_new_feature.sql
    CREATE OR REPLACE FUNCTION MY_NEW_FEATURE_UDF(
        FEATURE_DATE DATE DEFAULT CURRENT_DATE() - 1,
        LOOKBACK_WINDOW_VALUE INT DEFAULT 1,
        LOOKBACK_WINDOW_UNIT VARCHAR DEFAULT 'month'
    )
    RETURNS TABLE(
        dim_namespace_id VARCHAR,
        feature_date DATE,
        my_feature_column NUMBER
    )
    LANGUAGE SQL
    AS
    $$
    SELECT
        CAST(dim_namespace_id AS VARCHAR) AS dim_namespace_id,
        FEATURE_DATE AS feature_date,
        COUNT(*) AS my_feature_column
    FROM some_dbt_table
    WHERE event_date BETWEEN
        DATEADD(LOOKBACK_WINDOW_UNIT, -LOOKBACK_WINDOW_VALUE, FEATURE_DATE::DATE)
        AND FEATURE_DATE::DATE
    GROUP BY 1
    $$;
    
  2. Feature view設定features/{domain}/feature_views.yamlにエントリを追加します:

    feature_views:
      my_new_feature:
        version: "1.0"
        udf_name: "MY_NEW_FEATURE_UDF"
        entity: "dim_namespace_id"
        timestamp_col: "feature_date"
        description: "Description of what this feature view captures"
        updated_by: "your_name"
        updated_at: "2026-04-15"
    
  3. 特徴量の説明features/{domain}/descriptions/my_new_feature.yamlを作成します:

    descriptions:
      my_feature_column: "Description of this specific feature column"
    
  4. エンティティ(必要な場合)— 新しいエンティティを使用する場合はentities/entities.yamlに追加します。

開発データベースにデプロイする:

notebooks/update_feature_store.ipynbを開き、PROFILE_TARGET = "feature_store_dev"を設定します。次にDEPLOY_MODEを設定します:

  • "incremental"origin/mainに対するgit diffを使用して変更を自動検出し、影響を受けたビューのみをデプロイ
  • "full_refresh" — すべてのfeature viewをゼロからデプロイ
  • "manual"MANUAL_FEATURE_VIEWSにリストされたビューのみをデプロイ

ステップ2: ステージング検証

変更がローカルで動作したら、ブランチをプッシュしてマージリクエストを開きます。

  • staging-feature-store-changes-incremental — MRで変更されたfeature viewを検出してステージングスキーマにデプロイ
  • staging-feature-store-changes-full-refresh — すべてのfeature viewをステージングにゼロからデプロイ

ステップ3: 本番デプロイ

MRがmainにマージされると、本番パイプラインが自動的に実行されます:

  • deploy-feature_store-incremental — すべてのマージで自動実行
  • deploy-feature-store-full-refresh — 完全な再デプロイが必要な場合の手動ジョブ

特徴量のサービング

MLモデルのトレーニングまたは推論のために特徴量を取得するには、notebooks/serving_features.ipynbを使用します。

1. Feature Viewsを定義する

feature_views_dict = {
    "namespace_product_stage": "1.0",
    "namespace_information": "1.0"
}

2. Spineクエリを定義する

spine_query = """
SELECT CAST(dim_namespace_id AS VARCHAR) AS dim_namespace_id,
       '2025-05-27'::TIMESTAMP AS snapshot_date
FROM PROD.common_prep.prep_namespace_order_trial
WHERE order_start_date BETWEEN '2024-03-17' AND '2025-05-27'
  AND trial_type IN (1, 4, 5, 7)
"""

3. ルックバックウィンドウを設定する

グローバル(すべてのfeature viewに同じ):

lookback_window_value = 6
lookback_window_unit = "month"

4. serve_featuresを呼び出す

from gitlabds import serve_features

combined_features = serve_features(
    session=session,
    feature_store=fs,
    feature_views_dict=feature_views_dict,
    spine_df=spine_query,
    feature_date=snapshot_date,
    lookback_window_value=lookback_window_value,
    lookback_window_unit=lookback_window_unit,
    spine_timestamp_col="snapshot_date",
    include_feature_view_timestamp_col=False
)

CI/CDパイプライン

.gitlab-ci.ymlbuildstagingdeploysecurityの4段階のパイプラインを定義します。

変更検出

CIパイプラインはsrc/detect_changes.pyを使用してgit SHAを比較し、どのfeature viewが変更されたかを正確に判断します。

トラブルシューティング

UDF型の不一致

UDFの宣言された戻り型が実際の出力型と一致しない場合、デプロイ中の検証でこれを検出します。

権限

フィーチャーストアは本番デプロイにFEATURE_STORE_PRODUCERロールを使用します。

よくある問題

  • エンティティID型エラー: UDFでエンティティ列を常にVARCHARにキャストしてください
  • パラメーターの欠如: 3つのUDFパラメーターすべてが存在する必要があります
  • エンティティをまたいだJoin: 同じエンティティを共有するfeature viewのみがserve_features呼び出し時に結合できます