2021.04.08

機械学習システムのフィーチャ管理基盤、Feastの紹介

こんにちは、次世代システム研究室のA.Zです。

この数年間で、機械学習は研究だけではなく、たくさんの企業が実際のサービスに応用しています。応用の現場で、機械学習のフィーチャの管理の課題が発生し、
もっとフィーチャの管理を効率するためのFeature Storeというものが提案されました。今回はFeature Storeの一つ Feastを紹介したいと思います。

 

Feature Storeとは

フィーチャーストアとは一言でいうと、機械学習に使われているフィーチャを管理や提供するための基盤です。フィーチャの登録、保存、共有がもちろんですが、モデルの学習やモデルのサーブにもフィーチャを提供します。

モデルの学習のときに、過去の時系列のフィーチャが大量に必要ですが、逆にモデルサーブのときに、低遅延、最新のフィーチャが必要です。一般的に、Feature Store は2種類のデータベースで構成され、一つは大量なデータを扱えるデータベースともうひとつは低遅延、リアールタイムのデータベースになっています。
具体的に以下の図のイメージです。

 


Feature Storeを導入することで、何かうれしいか。
  • 複数チームや複数モデルモデルのフィーチャ共有・再利用ことができます。
    • フィーチャの作成するにはいろんな前処理を行い、作成するコストがかかります。1回フィーチャを作成し、共有・再利用できれば、コスト削減とモデルの改善スピードには効果があります。
  • モデル学習とモデルサーブのときにフィーチャの一致
  • モデルの学習やモデルサーブするときに、フィーチャの時系列的な正確性は保証される
参照:
https://medium.com/data-for-ai/what-is-a-feature-store-for-ml-29b62580af5d

この数年は、様々なFeature Storeが出てきましたが、次は具体的に一つのFeature Storeについて紹介したいと思います。今回紹介するのはFeastというFeature Storeになります。

 

なぜFeast

最近の人気なFeature Storeの特徴や比較は以下の図にあります。


上記の図からだと、Open-SourceになっているのHopsworkとFeastになっています。更に以下の理由で、今回はFeastを選択しました。
  • Githubのstarsから見ると、Feastのほうが人気。
  • Google Cloudと共同開発しているため、GCP上の機能を考慮し、設計されます。
    • もともと今参加しているプロジェクトはGCPを使うため、GCPのサポートが重要
  • FeastはFeature Storeのみに特化し、既存システムに導入しやすい。
FeastのGithub:
https://github.com/feast-dev/feast

参照:
https://medium.com/data-for-ai/2021-a-year-of-ml-feature-stores-adoption-e0f528506cad

 

Feast Architecture

 

Feastのアーキテクチャは以下の図になります。


 

基本なコンポネントは以下です。
  • Feast Core:
    • フィーチャやエンティティなどの管理や定義の役割
  • Feast Job Service:
    • Feastにデータの格納のプロセスを管理する役割
  • Feast Online Serving:
    • 低遅延、リアルタイムの最新フィーチャを提供する役割
  • Feast Python SDK:
    • 様々な処理や他のシステムと連携するためのインタフェース
  • Online Store
  • Offline Store
 

Feast Concepts

Entity

Entityとは特定ドメインのコンセプトやオブジェクトです。Feastの中にはすべてのフィーチャは何かしらのEntityに紐付けなければなりません。

Feastのentityの定義の例
customer = Entity( name="customer_id", description="Customer id for ride customer", value_type=ValueType.INT64, labels={} )

Sources

Sourcesは基本的に、Feastに入れる前のフィーチャの元のデータソースになります。 Feastにフィーチャを入れる方法とサポートしているSourcesのタイプは以下です。
  • Batch sources
    • Parquet Fileのソース
    • BigQueryテーブルのソース
  • Stream sources
    • Kafkaソース
    • Kinesisソース

Feature Table

Featureテーブルは複数フィーチャーをグループするためのものです。
基本的に、同じ更新頻度フィーチャや同じソースのデータが一つのFeature Tableにまとめて定義します。

 

Feastの環境の構築

今回はGCP上で、全ての環境はTerraformで自動的に作成しました。

Feastのrepositoryに、必要な環境を構築するためのTerraformのコードが用意されています。

環境構築したときに、色々はまったところがありますが、そのはまったところや解決策を簡単に紹介いたします。

Helmの展開がうまく行かない。

Githubのリリースタグをついているソースコードをそのまま使うと、必要なコンポネントの展開は失敗する可能性があります。その場合は
resource "helm_release" "feast" { depends_on = [kubernetes_secret.feast-postgres-secret, kubernetes_secret.feast_sa_secret] name = var.name_prefix chart = "feast-charts/feast" values = [ yamlencode(local.feast_helm_values) ] }

Dataprocのクラスターはうまく立ち上がらない。

dataproc.tfのファイルの中に、以下のブロックのinternal_ip_onlyfalseを設定すれば、
gce_cluster_config {
      subnetwork      = var.subnetwork
      service_account = google_service_account.feast_sa.email

      internal_ip_only = false
    }

GCPのDataprocのsparkのバージョンとの問題:

現在、feastはspark 3.0.1をサポートしていますが、GCPのdataproc imageだと、該当のsparkのバージョンがありません。用意されているdataproc imageはspark 3系だと、spark version 3.1.0しかありません。同じ3系でも、依存しているライブラリは互換性がないため、そのまま利用できない状態になっています。
回避方法は色々あると思いますが、今回は軽く実験のため、dataprocを使用せずに、standaloneのsparkを利用することにしました。

 

Feast基本の紹介

今回は簡単にFeastの利用方法について簡単に紹介したいと思います。
基本的に、サンプルコードは以下ベースに利用します。
https://github.com/feast-dev/feast/tree/master/examples/minimal

EntityとFeatureテーブルの登録登録定義

まず各フィーチャの格納や利用する前に、EntityやFeatureの定義の登録が先に行う必要があります。
import os

from feast import Client, Feature, Entity, ValueType, FeatureTable

client = Client()
driver_id = Entity(name="driver_id", description="Driver identifier", value_type=ValueType.INT64)

# Daily updated features 
acc_rate = Feature("acc_rate", ValueType.FLOAT)
conv_rate = Feature("conv_rate", ValueType.FLOAT)
avg_daily_trips = Feature("avg_daily_trips", ValueType.INT32)

# Real-time updated features
trips_today = Feature("trips_today", ValueType.INT32)

# FeatureTableとは複数featureのグループするため
# 基本的に、同じ更新頻度や関連性が高いフィーチャをまとめてグループする
driver_statistics = FeatureTable(
    name = "driver_statistics",
    entities = ["driver_id"], # Featureテーブルは必ず一つのentityに紐付ける
    features = [
        acc_rate,
        conv_rate,
        avg_daily_trips
    ],
    # テーブルを格納するための定義
    # 今回はBatchの格納定義しますが、同時にStream格納でも定義できます
    batch_source=FileSource(
        event_timestamp_column="datetime",
        created_timestamp_column="created",
        file_format=ParquetFormat(),
        file_url=driver_statistics_source_uri,
        date_partition_column="date"
    )
)


driver_trips = FeatureTable(
    name = "driver_trips",
    entities = ["driver_id"],
    features = [
        trips_today
    ],
    batch_source=FileSource(
        event_timestamp_column="datetime",
        created_timestamp_column="created",
        file_format=ParquetFormat(),
        file_url=driver_trips_source_uri,
        date_partition_column="date"
    )
)

# EntityやFeatureテーブルの登録
client.apply(driver_id)
client.apply(driver_statistics)
client.apply(driver_trips)
 

データ格納

定義は終わったら、該当するテーブルに以下のようにFeastに格納することができます。
#ダミデータ生成
entities = generate_entities()
stats_df = generate_stats(entities)
trips_df = generate_trips(entities)


#こちらで、offline featuresに格納行います。
client.ingest(driver_statistics, stats_df)
client.ingest(driver_trips, trips_df)

#こちらで、online featuresに格納を行います。
job = client2.start_offline_to_online_ingestion(
    driver_statistics,
    datetime(2020, 10, 10),
    datetime(2020, 10, 20)
)
 

学習用のデータの取得

学習データは以下の方法で簡単に取得することができます。
job = client.get_historical_features(
    feature_refs=[
        "driver_statistics:avg_daily_trips",
        "driver_statistics:conv_rate",
        "driver_statistics:acc_rate",
        "driver_trips:trips_today"
    ], 
    entity_source=entities_with_timestamp
)
上記では、結構いい感じでentityのtimestampとfeatureのtimestampを考慮し、時系列のデータの生成ができます。
具体的に仕様は以下のリンクにご参照お願いします。
https://docs.feast.dev/user-guide/getting-training-features#point-in-time-joins

Online storeから最新のフィーチャの取得

モデルサーブするときに、online storeからのフィーチャ取得は以下で取得します。
features = client.get_online_features(
    feature_refs=["driver_statistics:avg_daily_trips"],
    entity_rows=entities_sample).to_dict()
 

感想

  • Feastの利用方法はかなり簡単とシンプルで、使いやすいだと思います。
  • 依存する様々なサービスやコンポネントが多く、構築やメンテナンスが大変そうだと思います。
  • APIや機能が多いが、ドキュメンテーションが結構少ない。本当に簡単なuse-caseしかなく、実際のプロジェクトに取り入れるときに、特集なユースケースに対応できるかどうか
  • Google Cloudがサポートしていると発表されましたが、なぜかDataprocとの互換性がありません。
  • Feastの技術stackは現在かなり限定され、既存のプロジェクトに導入するにはアーキテクチャの変更が発生する可能性が高い
    • 例えば、今Feastのonline storeがRedisしかサポートしていないため、既存のシステムのonline feature servingのバックエンドがRedisに変更するが必要です。Criticalなシステムだと移行リスクが高いと思います。
  • Featureのデータタイプはsparseフィーチャに適切なタイプがなく、自然言語系のフィーチャを使うときに、ストレージのメモリが最適化されません。

まとめ

今回はFeastというFeature Storeを紹介いしました。機械学習モデルのフィーチャの一括で管理できるのはとても便利だと思います。Feast自体の足りないところもありますが、機械学習プロジェクトに導入するには大きなメリット得られると思います。

 
次世システム研究室では、ビッグデータ解析プラットホームの設計・開発を行うアーキテクトとデータサイエンティストを募集しています。興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧からご応募をお願いします。
一緒に勉強しながら楽しく働きたい方のご応募をお待ちしております。

Pocket

関連記事