Lyftでのリアルタイム機械学習基盤の構築

 
0
このエントリーをはてなブックマークに追加
Daichi Takayama
Daichi Takayama (高山 大地)

以下の文章は、執筆者のお二方(Konstantin Gizdarski Martin Liu )から許可を得て翻訳したものです。

https://eng.lyft.com/building-real-time-machine-learning-foundations-at-lyft-6dd99b385a4e

2022年初、LyftはすでにLyftLearnという包括的な機械学習プラットフォームを持っていました。これには、モデルサービングトレーニング、CI/CD、機能サービング、そしてモデル監視システムが含まれていました。

リアルタイムという面では、リアルタイムでの推論(inference)や入力機能の検証はサポートしていました。しかし、プラットフォームの多くの部分(トレーニングや詳細な監視など)において、ストリーミングデータが主要な機能として十分にサポートされることはありませんでした。

ストリーミングデータを機械学習のワークフローで使用するチームもいくつかありましたが、それは数週間から数ヶ月に渡るエンジニアリングを要することもあるほどの大変なプロセスでした。ですが、開発者たちから、リアルタイムML(Machine Learning)システム構築の強い願望があったのです。

Lyftはリアルタイム・マーケットプレイスであり、多くのチームがリアルタイム信号による機械学習モデルの強化による利益を得ています。

そこで、お客様のニーズを満たすために、ストリーミングを伴うリアルタイム機械学習の取り組みを開始したのです。目標は、Lyftの抱える数百人のML開発者たちが効率的に新しいモデルを開発したり、ストリーミングデータを活用しての既存モデルの強化を可能とする基盤を構築することでした。

この記事では、その目標に向けての私たちの取り組みや、途中で得た学びについてお伝えします。


リアルタイム機械学習にできること

私たちがまず最初に行ったことは、MLエコシステム内でストリーミングデータを活用できる一般的なユースケースを特定することでした。

ストリーミングを活用できるリアルタイムMLアプリケーションには、主に3つの能力があることが分かりました:

  1. リアルタイム機能 → リアルタイムストリーミングデータを活用しての機能のコンピューティング
  2. リアルタイム学習 → リアルタイムストリーミングデータを活用してのモデルトレーニング
  3. イベント駆動の決定 → リアルタイムストリーミングデータを活用しての意思決定(モデルの再トレーニング、アラートのトリガー、推論の呼び出しなど)

私たち自身の調査とFlinkのユースケースのドキュメントを参考にしたことにより、これら3つの能力は絶対的だということが判明しました。

実際、「イベント駆動の決定」は特に、多岐にわたるユースケースに適用可能なほど汎用的でした。

構築した直後にチーム内の別のグループがリアルタイム異常検出プロダクトを構築するためにも利用しました。こちらの記事の執筆時点においては、マッピングチームはイベント駆動の決定プロダクトを活用し、geohashごとにデータを集約し、モデルを適用してLyftの交通インフラを再構築するための作業を進めています。

そのように、新しく作った仕組みが、すぐに更に進んだ形で活用される様子を目の当たりにできたのはとても嬉しかったです。私たちの作ったものは役に立つものだという証拠だからです。


MLライフサイクル全体におけるリアルタイム機能

LyftLearnチームにおける焦点は、厳格な機械学習ライフサイクルを推進することであり、以下の図にその内容が示されています。

厳格な機械学習ライフサイクルを支えるために、リアルタイムMLの各機能に対し、モデルのライフサイクルの全段階 — プロトタイピングから開発、プロダクション、そしてメンテナンスに至るまで — でのサポートを提供することを目指しました。
ML Model Development Life cycle

-MLモデル開発フローの視覚化-

以前にLyftLearn Servingを構築した際の主なデザイン原則の一つは、モデルトレーニングとサービングの間に統一された環境を作ることでした。この環境は共有Dockerイメージを通して提供され、ランタイムの違いによる微妙なバグを排除することを保証しました。

ストリーミングにおいても、環境全体にわたる一貫性を保ち、再現性と開発の速度を確保するという同様の目標を立てました。

開発者は、ノートブック内でリアルタイムパイプラインを開発し、実際のストリーミングデータでその場でテストを行い、コードが機能的かつ論理的に正確であることを確認できる必要がありました。その後、確認されたコードをGitHubリポジトリにコミットし、スムーズに本番環境にデプロイすることができるようになりました。

リアルタイムMLアプリケーションのコードは「一度書けばどこでも実行できる」ことが重要です。


共通インターフェースの設計: RealtimeMLPipeline

目標は技術的な複雑さを抽象化し、開発者がストリーミングデータをMLモデルに簡単に統合できるようにすることでした。

その実現のために、全てのリアルタイムMLアプリケーションを定義するためのRealtimeMLPipeline

という共通インターフェースを開発しました。このインターフェースは、指定された機能を実行するために必要なパイプラインオブジェクトを構築する際、必要なメタデータの量を最小限に抑えるように設計されています。


RealtimeMLPipeline

driver_accept_proportion_10m

RealtimeMLPipelineインターフェースをより深く理解できるよう、1つのリアルタイム機能のユースケースを例にとり、詳しく見てみましょう。

リアルタイム機能をエンコードするRealtimeMLPipelineを定義するため、機能の名前やバージョン、それを計算するためのクエリなどのメタデータを開発者が提供し、Pythonオブジェクトをインスタンス化します。そのPythonオブジェクトは、私たちがサポートするすべての環境 - ローカルテスト環境、ノートブック、ステージング、本番環境 - で移植可能です。

下記のコードは、RealtimeMLPipelineインターフェースを利用してリアルタイムの機能計算を定義する一例を示しています:

feature_sql = """
SELECT driver_id AS entity_id, window_start AS rowtime, count_accepted / count_total as feature_value
FROM (
SELECT driver_id,
window_start,
CAST(sum(case when status = 'accepted' then 1.0 else 0.0 end) AS DOUBLE) as count_accepted,
CAST(count(*) AS DOUBLE) as count_total
FROM TABLE(
TUMBLE(TABLE driver_notification_result, DESCRIPTOR(rowtime), INTERVAL '10' MINUTES)
)
GROUP BY driver_id, window_start, window_end
)
"""

feature_sink = DsFeaturesSink()
feature_definition = FeatureDefinition('driver_accept_proportion_10m', 'some_feature_group', Entity.DRIVER, 'float')
pipe = RealtimeMLPipeline()
pipe\
.query(feature_sql)\
.register_feature(feature_definition)\
.add_sink(feature_sink)

この例では、「driver_accept_proporition_10m」という特徴量を計算するためのRealtimeMLPipelineが示されています。これは、ドライバーが通知を受け入れた割合を10分間隔のtumbling window.で計算する機能です。

このRealtimeMLPipelineオブジェクトは、Jupyterノートブック、ローカルテスト環境、そして本番環境でのFlinkクラスターなど、異なる実行環境で利用可能です。ノートブックやローカル環境では、このパイプラインは臨時のFlinkクラスターで実行され、結果は検証用のローカルファイルシステムに書き込まれます。

一方、ステージングや本番環境では、パイプラインは多テナントで本格的なスケールのFlinkクラスターで実行され、計算された特徴量はKafkaを通じて出力され、最終的には特徴量ストレージシステムに送信されます。

コードブロック内で、RealtimeMLPipelinepipeとして定義しています。さまざまな環境で実行するには、次のコードを実行するだけです:

pipe.run()

このpipeオブジェクトが異なる環境間でシリアル化され、ロードされる様子を想像することができるのではないでしょうか。


開発環境と本番環境をまたぐRealtimeMLPipeline

Illustration of the Analytics Event Abstraction which enabled development in a notebook of streaming applications as well as deployment to staging and production.

上の図は、RealtimeMLPipelineが開発環境と本番環境において一貫して動作する様子を示しています。

重要な構成要素の一つは、Analytics Event Abstractionレイヤーです。このレイヤーは、非本番環境でのRealtimeMLPipeline実行時にはS3からデータを読み込み、本番環境ではリアルタイムデータストリーム(執筆時点ではKinesis)からデータを取得するように設計されています。

もう一つ重要な要素は、私たちのプロトタイピング環境のJupyterノートブックとともに起動するアドホックFlinkクラスタです。このクラスタは、私たちの本番クラスタと同じバージョンのFlinkをPyFlinkを通じて実行します。

この設計により、複雑な分散システムを2つの異なる運用環境で均一に動作させることを実現しました。一つはKubernetesをベースにしたホステッドJupyter環境で、ここではS3 FileSystemコネクタを介してイベントデータを取得します。もう一つは、KafkaとKinesisからデータを取得するKubernetesベースの本番環境です。

このようにMLライフサイクル全体で均一な動作を実現することで、リアルタイムアプリケーションの開発速度が大幅に向上し、RealtimeMLPipelineの構築にかかる時間が数週間から数日に短縮されました。開発者はノートブックで迅速にアプリケーションを試行し、同じコードを本番環境にデプロイすることが可能となりました。


ストリーミングを用いたリアルタイムMLのLyft事業への適用

プロジェクトを始動する前に、私たちは新しいリアルタイムMLの利用シナリオを幾つか特定し、それらに対するシミュレーションとオフライン分析を実施しました。結果として、リアルタイムデータをMLモデルに取り込むことでパフォーマンス指標が改善されることが確認されました。

最初に試みた二つのアルファケースは、(1)ETAモデルのバイアスを矯正するためのセカンダリモデルの迅速な再トレーニングと、(2)ドライバー毎の特定の安全特性を計算することでした。

リアルタイム機能が完成し、アルファケースが導入された後、内部プレゼンテーション、チームのニュースレター、他チームとのミーティングを通じてこれらの機能を積極的に宣伝しました。リアルタイムMLの利用により開発時間が短縮され、長期的な運用コストも低減される利点は多くの人にとって明らかでした。リアルタイムMLプロジェクトは、Rider、Driver、Marketplace、Mapping、Safetyを含むほぼ全てのエンジニアリング部門から顧客を獲得しました。

以降、私たちはエンゲージメントモデルを確立しました。初期段階ではチームメンバーがパートナーチームのエンジニアと共にリアルタイムMLアプリケーションの範囲設定、設計、構築を行い、観測性の追加、実験設定、結果分析などを一緒に行いました。これをプロジェクトのオープンベータフェーズと呼びます。

オンボーディングプロセスを洗練させ、ドキュメントを充実させることで、私たちの関与は徐々に減り、パートナーチームがより独立して作業を進られるサービスモデルに移行しました。序盤のガイドや指導の後は、判断が難しい時やトリッキーなケースなどの必要な場合のみ、私たちが関与する形です。

初期のケースではアプリケーションを立ち上げるのに数週間かかっていましたが、今ではLyftLearnのエンジニアが新しいリアルタイムMLアプリケーション立ち上げるのに必要なのは数日のみです。


技術的な課題

ストリーミングアプリケーションの一貫した動作を確保することは、特にLyftLearn Servingにおいて、大きな課題でした。ストリーミングプロセスの状態を持つ性質と分散された機能は、ソフトウェア開発の複雑さを大幅に増加させました。

さらに、ストリーミングの抽象化は、データサイエンティストやソフトウェアエンジニア、LyftLearnエンジニアにとって直感的なものではなかったため、学習曲線は急でした。

以下、リアルタイムMLの機能を構築する過程で直面した技術的な課題の一部です:

  • ストリーミングを使用する際の開発者の学習曲線が急であること。インターフェースをできるだけシンプルに、かつ柔軟にパッケージ化する必要がありました。
  • ストリーミングデータの移り変わりやすい性質が、バックテストを難しくしたこと。
  • セキュリティ制限のためにノートブック環境でのストリーミングデータのサポートが不足していること。ウェアハウスデータでストリーミングデータをシミュレートする必要がありました。
  • 伝統的なコードと比べて、状態を持つ分散ストリーミングジョブのデバッグが難しいこと。
  • アプリケーションを構成する複数のプロセスによって生じる不透明性。たとえば、ノートブックでFlinkジョブを実行する場合、JVM、Pythonカーネル、ノートブックのポッド、ジョブマネージャのポッド、タスクマネージャのポッドなど、様々な要素に対処しなければなりませんでした。

時間と忍耐力が必要となりましたが、徐々にチーム全体でFlinkの運用と拡張の両方での専門知識を身につけました。

Lyftでは、オープンソースのFlinkプロジェクトのフォークを実行しています。重要な初期投資として、このフォークのイテレーションとリリースを迅速化するためのリリースプロセスの見直しが挙げられます。これにより、プロジェクトのチューニングに必要な時間を節約することができました。

具体的なFlinkへの変更としては、S3からウェアハウスデータを読み込むためのFileConnectorサポートを追加、FileSystemConnectorからファイルを読み取る順序を変更、S3とHiveの接続をサポートするための必要なJava拡張を注入するためにapache-flink-librariesをフォーク、プロトタイピング環境でFlink UIを公開、問題の診断のために追加の統計を計測、PyFlinkの中でnumpyの依存関係を固定解除するなど、その他にも多くの変更を行いました。

全体的に見れば、これらは小さな変更でしたが、上流に変更を加えるのではなく、自分たちのフォークでイテレーションを行い、そのための効率的なプロセスを持つことが、進行上不可欠でした。

注: この投稿ではLyftでのFlinkスタックについて詳しく説明していませんが、歴史的な背景については、Micah Wyldeによるこちらの解説をお勧めします。


主な教訓

振り返ってみると、このプロジェクトから得られた主な教訓の多くは、組織内で複雑な技術に基づく新しい能力を開発し、スケールする方法に関するものばかりでした。

このセクションでは、それらの教訓のいくつかを強調しています。

  1. 必要性の特定と確認 → 顧客との対話を通じて、役立つ可能性のある能力を特定します。プラットフォームの新しい能力が彼らが現在持っている実際の問題に対処している稼働かを確認します。即座の需要がない場合は、その能力のためにプラットフォームを拡張する設計を考えるのではなく、すぐにそれを開発するかどうかを検討してください。
  2. アルファユーザーの活用 → 顧客中心の構築を維持するために、能力ごとに数人のアルファユーザーと連携します。通常、1人だけでは足りません。少なくとも2人を目指してください。3人の方がベターでしょう。これらの顧客と緊密に連携し、システムを進化させます。
  3. 拡張可能でシンプルなインターフェース → 最初から、柔軟かつシンプルなインターフェースを追い求めることで、新しい要件が出てきたときに何らかの制限にぶつかることを避けることができます。
  4. 簡単なオンボーディングプロセスの設計 → セットアップが迅速でドキュメントが整っているプロトタイピング環境によってオンボーディングを簡素化することで、人々があなたのシステムを容易に使用し、学ぶことができるようになります。また、あなたとあなたのステークホルダーにとっても、構築しているものが実際に機能していることを迅速に確認できるようになります。
  5. ドキュメントと宣伝 → とある地点から、プラットフォームの成功の鍵を握るのは、顧客をスケーラブルな方法でオンボードできるかどうかになります。最良の方法は、ドキュメントに投資することです。成功したプラットフォームは、個人としての帯域幅を持っている以上のオンボーディングサポートの負担を持ちます。十分なドキュメントが整ったら、能力を宣伝し、興味を持った人々をドキュメントとプロトタイピング環境の組み合わせに誘導しましょう。

謝辞

LyftでのリアルタイムMLとストリーミングを先導してくれたMartin Liuに特別な感謝を表します。その卓越した技術的深さと忍耐力は、このプロジェクトの成功に不可欠でした。

プロジェクトの実現に貢献してくれたSeth SapersteinRavi MaghamHakan BabaMihir MathurXiao Zheng、そしてShiraz Zamanを含むエンジニアリングチームのリーダーやパートナーの皆様にも心からの感謝を伝えます。皆様のサポート、ガイダンス、貢献は大変価値がありました。

ETAチーム、トラフィックチーム、マーケットシグナル、トラスト&セーフティチーム、そして他多くのチームの皆様にも感謝します。早期の技術採用者として、Jacob van GoghAlex ContrymanXiaoyi DuanQuinn Liu、そしてJieyi Wangに特別な感謝を述べます。

最後に、LyftLearnチームの残りのメンバーであるJonas TimmermannAlex JaffeAdriana DeneaultAndy RosalesAnindya SahaEric Yaklin、そしてRajeev Prabhakarに感謝を表します。皆さんが素晴らしいチームメイトであることに感謝しています。


Lyftについて

Lyftは、ライドシェア業界のリーディングカンパニーであり、リアルタイムでの機械学習の問題に積極的に取り組んでいます。LyftはリアルタイムMLという分野での課題に直面している会社であり、専門知識を活かす場があります。興味がある方は、Lyft Careersを訪れて、現在の求人をご確認ください。Lyftでのキャリアは、革新的なプロジェクトに取り組み、業界をリードする技術の進化に貢献する機会があります。

info-outline

お知らせ

K.DEVは株式会社KDOTにより運営されています。記事の内容や会社でのITに関わる一般的なご相談に専門の社員がお答えしております。ぜひお気軽にご連絡ください。