re:Invent 2024: Klarnaのリアルタイム信用評価システムへの進化
はじめに
海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!
📖 AWS re:Invent 2024 - Klarna: Accelerating credit decisioning with real-time data processing (FSI319)
この動画では、KlarnaのSenior EngineerであるTony Liu氏が、Buy Now Pay Later企業であるKlarnaのCredit Underwritingシステムの進化について解説しています。従来のバッチ処理による特徴量計算から、Amazon DynamoDB、AWS Glue、Amazon S3などを活用したリアルタイム処理システムへの移行により、データの信頼性向上、コスト効率化、処理の一貫性確保を実現した事例を紹介しています。特に、Business Logic Libraryの導入によりバッチ処理とリアルタイム処理の実装の違いを最小化し、Retrospective Calculationにより新機能の迅速な実験を可能にした点は、大規模な金融システムにおける実践的な解決策として注目に値します。
※ 画像をクリックすると、動画中の該当シーンに遷移します。
re:Invent 2024関連の書き起こし記事については、こちらのSpreadsheet に情報をまとめています。合わせてご確認ください!
本編
リアルタイム与信審査の台頭:Klarnaの事例紹介
皆様、尊敬する同僚の皆様、金融界のイノベーターの皆様、本セッションへようこそ。私はAWSのSenior Database GTM Specialistを務めるDragan Coricと申します。スウェーデンのストックホルムを拠点としており、本日皆様の前でお話しできることを大変光栄に思います。この10年間、金融業界は大きな変革を遂げてきました。単なる成長ではなく、爆発的な発展を遂げ、その成長を支える基盤技術は私たちの身近に存在し、その発展を目の当たりにしています。
また、MillennialsからGen Zのデジタルネイティブへと世代交代が進んでいることも目にしてきました。彼らは金融サービスの主要なユーザーとして、スピード、信頼性、機能性を重視すると同時に、デジタルチャネルを当然のものとして期待しています。単なる期待以上に、それを前提としているのです。おそらく最も興味深い変革の一つが、本日議論するリアルタイム与信審査の台頭でしょう。貸付は長年にわたり金融サービスの一部でしたが、Machine LearningとArtificial Intelligenceに支えられた技術の進歩が、このセグメントに革命をもたらそうとしています。
ローン申請の処理が、数週間でも数日間でもなく、数秒から数分で完了することを想像してみてください。本日は、Klarnaの事例を通じて、業務効率の向上、コスト削減、リスク軽減など、この技術によってもたらされる利点をご紹介します。時間を割いて参加する価値があり、これから紹介される事例も非常に興味深いものになることをお約束します。それでは皆様、KlarnaのTony Liu氏をお迎えしたいと思います。
Klarnaの概要と信用審査プロセス
Draganさん、ご紹介ありがとうございます。聞こえていますでしょうか? はい、完璧です。本日ここに参加でき、データ処理に興味を持つ多くの方々にお会いできて嬉しく思います。今日は、データ処理システムのリスクを最小限に抑える方法、データ処理システムをよりコスト効率が良く、スケーラブルにする方法、リアルタイム処理とバッチ処理の層の違いを減らす方法、そして処理済みデータ上で迅速な特徴量エンジニアリングの反復を可能にする方法についてお話しします。 Klarnaで私たちが行った、まさにこれら4つの課題に取り組んだ journey についてお話ししたいと思います。
私はTony Liuと申します。スウェーデンのストックホルムを拠点とするKlarnaでSenior EngineerとCompetence Leadを務めています。Klarnaでデータ処理の分野に携わって4年になり、現在は与信審査のための顧客特徴量作成を担当するチームのTechnical Leadを務めています。 本日は、与信審査のための顧客特徴量作成に関するデータ処理を改善するために、私たちのチームで推進してきたプロジェクトからの知見と学びを共有させていただきます。今日お話しする考え方やソリューションは、すべてのシナリオに完璧に適合するわけではありませんが、私たちの問題領域とアプリケーションにおいてうまく機能していることが分かったものです。本日のプレゼンテーションから何らかのインスピレーションやアイデアを得ていただき、皆様自身のシステムに活かしていただければ幸いです。
まず最初に、Credit Underwritingについて、特にCustomer Featuresと信用審査のためのCustomer Data Pointsについてお話しします。その後、私たちのセットアップとシステムに関する背景と経緯について詳しく説明します。これは、以前抱えていた課題や問題点、そして私たちが解決しようとしている問題を浮き彫りにするためです。最後に、現在のソリューションのシステムアーキテクチャについて、システム図と具体的な例を見ながら説明していきます。中間状態とデータモデルの更新がどのように行われているかをご紹介します。
また、リアルタイムでのFeature計算の仕組みと、オフライン処理の設定についても説明します。まず、Klarnaとは何かということですが、Klarnaをご存知の方は手を挙げていただけますか?そして、実際にKlarnaを使用したことがある方は?ありがとうございます。これから説明する内容は、手を挙げなかった方々のためのものです。
Klarnaはスウェーデン発のFintechカンパニーで、決済ソリューションと、カスタマーのショッピング体験を向上させる製品を提供しています。私たちの目標は、消費者により多くのコントロールと柔軟性を提供することです。KlarnaはBuy Now, Pay Later企業として知られていますが、Eコマースとショッピングの分野でそれ以上のサービスを提供しています。例えば、Klarna App、Klarna Card、Cash Backプログラムなどがあります。私たちはグローバル企業として、27の市場で60万以上の小売パートナーと協力し、8,700万人のアクティブな消費者にサービスを提供しています。年間取引額は約1,000億ドルで、オンラインだけでなく、実店舗でも強いプレゼンスを持っています。
Klarnaは小売やファッションだけでなく、日常的なサブスクリプション、旅行、電機製品など、さまざまなカテゴリーの加盟店ネットワークを持っています。ここに、皆さんがご存知かもしれないKlarnaを導入しているグローバルブランドの例をいくつか示します。Klarnaは、性別、学歴、収入レベル、居住地域など、非常に多様な顧客基盤を持っています。約99%の消費者残高が支払われており、非常に責任感のある支払い者が多いのが特徴です。また、消費者一人当たりの平均未払い額が低いことも特筆すべき点です。Klarnaの場合、消費者一人当たり平均160ドルで、クレジットカード利用者の平均6,500ドルと比べると、消費者が抱えるリスクは60分の1です。Buy Now Pay Laterソリューションと支払いオプションを提供していますが、実際には29%の顧客が全額を即時支払いを選択しています。
Credit Underwritingの課題と顧客特徴量の重要性
これでKlarnaと私たちの事業規模についてご理解いただけたと思いますので、Credit Underwritingの課題、特にCredit Underwritingに使用するCustomer FeaturesとCustomer Data Pointsについて詳しく見ていきましょう。決済サービスプロバイダーであるKlarnaにとって、購入のリスクを正確に評価できることは非常に重要です。私たちは、すべての購入に対して高頻度でリスク評価を行っています。これは、購入を承認または拒否する際に、その時点での最新の顧客プロファイルを把握したいからです。
リスクアセスメントを行う上で重要な要素の1つが、Credit Underwritingプロセスに供給される顧客データとデータポイントです。Credit Underwritingの本質を突き詰めると、顧客の購入を承認するか拒否するかの判断に帰着します。この判断を行うのがUnderwriting PolicyとCredit Modelであり、どちらもデータによって支えられ、駆動されています。Klarnaでは、社内データ、信用情報機関からの外部データ、そして取引自体に関する情報を使用しています。これらのデータはPolicyとCredit Modelに取り込まれ、最終的に購入の承認または拒否を決定します。Underwriting Policyは基本的に、閾値を持つルールの集合で、最終的に承認か拒否かを決定します。これはフローチャートのようなもので、最終的な状態が承認か拒否になると考えてください。これらのPolicyには、コンプライアンスや規制上の理由に関連する「ハードリジェクト」と呼ばれるルールがよく含まれています。Policyは手動で定義され、その中の閾値はビジネス目標に合わせて調整することができます。
Credit Modelは、購入のデフォルト確率を予測するために使用される機械学習モデルです。これらの機械学習モデルは過去のデータで学習され、機械学習モデルである以上、新規顧客や新しいケースに対して適切な一般化を行うためには、かなりの量のデータが必要になります。また、モデルに供給されるデータポイントが洗練されており、入力データ自体のノイズが少ないことも重要です。最終的に、モデルは顧客のデフォルト確率に対応するモデルスコアを出力し、これがCredit Policyでの判断に使用されます。PolicyもModelも、どちらもデータによって駆動されているのです。
今日は、Klarnaの社内データ、特に内部顧客特徴量に焦点を当てていきます。私のチームメンバーの1人が、説明的で正確な定義を作ってくれました:内部顧客特徴量とは、集約された内部データを消化しやすい情報の断片に還元したものです。基本的に、顧客が最終的に支払い可能かどうかを示す、有用で予測力のあるデータポイントということです。具体的な例を挙げると、顧客が現在Klarnaに対してどれだけの債務があるか、これまでKlarnaにいくら支払ったか、初めてKlarnaで注文した時期はいつか、Klarnaへの支払いが遅れたことがあるか、といったものです。
外部の信用情報機関からもデータを取得しており、これらは私たちのPolicyやModelで正確で予測力のあるリスクアセスメントを行う上で非常に重要です。ただし、これらのデータポイントは頻繁には更新されません。これは、リピーターやロイヤルカスタマーにとって特に重要になります。なぜなら、社内データはリアルタイムで更新でき、顧客のアクションをすぐに考慮に入れることができるからです。例えば、顧客が注文を行った場合、次の購入ではその新しい注文を考慮に入れ、より厳格な判断を行いたいと考えます。同様に、顧客が既存の債務を返済した場合、次の購入ではより多くを許可したいと考えます。まとめると、外部の信用情報機関のデータをベースに、その上に社内のリアルタイムデータを追加することで、購入時点での顧客プロファイルをより完全な形で把握できるということです。
Klarnaの旧システム構成と直面した問題点
問題領域とCredit Underwritingについて理解したところで、システムの歴史と技術的なコンテキストに移りましょう。ここでは、以前のシステム構成について説明します。これは、私たちが以前直面していた課題と、新しいアーキテクチャで解決しようとしている問題を示すためです。こちらが以前の構成の概要です。左側のソースシステムから始まり、内部データベースがあり、右側のUnderwriting Policyで終わります。ソースシステムから見ていくと、この例ではシステムABCがあり、内部データベースを定期的にAmazon S3にエクスポートしています。基本的に、内部データベースのスナップショットやダンプをS3にエクスポートしているわけです。
その後、Amazon EMR上でBatch Feature Systemを稼働させ、これらのデータベースのスナップショットを処理して特徴量を作成し、S3上のFeature Datasetに出力していました。このデータセットはDynamoDBテーブルにアップロードされ、右側のUnderwriting Policyに実際の値を提供するFeature Serviceからアクセスされます。非常に重要でタイムセンシティブな一部の特徴量については、リアルタイム要件があります。そのため、上部にあるReal-time Feature Systemがあり、これはBatch Feature Systemの出力である最新の特徴量の計算結果を使用し、さらにリアルタイムの寄与分やリアルタイムのデルタを追加して、特徴量の最新の状態を把握します。
これはKafkaのメッセージキューから読み取られます。ここで注目すべき点は、このメッセージキューは一部のシステムにのみ存在し、この場合はSystem Cのみがこのリアルタイムイベントを生成しているということです。
Underwriting PolicyはReal-time Feature SystemまたはBatch Feature Systemを呼び出して、ポリシーと判断に必要な特徴量を取得します。その後、対応する特徴量の値とともにすべての判断をAmazon S3のログに記録します。アナリストやData Scientistはこのログにアクセスして特徴量の値を抽出し、トレーニングデータセットや分析用データセットを再作成し、Amazon SageMakerでモデルのトレーニングを行ったり、Amazon Redshiftでさらなる分析を行ったりすることができます。
このセットアップにはいくつかの問題があり、今日はその中から4つを挙げ、後ほど新しいソリューションでどのように解決したかをお示しします。まず、Batch Featureのソースとして内部データベースのダンプに依存しているという点です。これは、データを生成するSystem Aと私たちのBatch Feature Systemの間に契約がないことを意味します。以前に発生した問題として、System Aが内部データベースに変更を加えようとした際、Batch Feature Systemがその内部フィールドに依存していることを知らなかったために、インシデントやバグが発生しました。これにより特徴量の分布が大きく変化し、Underwriting Policyに影響を与えました。
2つ目の問題は、Batch Feature Systemが内部データベースダンプのスナップショットを処理しているという点です。実行のたびに、通常は毎日または1日おきに、すべての履歴データを再処理する必要があります。Klarnaの成長に伴い、これはすぐにコストがかかり、スケーラビリティの面で課題となりました。
この設定における3つ目の問題は、リアルタイム層とバッチ処理システムの間に大きな違いがあることです。これらは異なる言語で実装され、異なるフレームワークを使用し、異なるインフラストラクチャ上で実行されていました。さらに、異なるチームによって管理されていました。つまり、機能を変更したり新しい機能を追加したりする場合、両方のシステムについて知っておく必要があり、その動作がリアルタイム層とバッチ層の両方で同じように反映されるようにする必要がありました。この余分な手間により、Feature engineeringのイノベーションが停滞してしまいました。最初に一連の機能を用意しましたが、余りにも面倒だったため、誰も変更や追加を行わなくなってしまったのです。
最後の問題は、ここの右側の部分に関するものです。データが成熟するのを待つ必要があります。Policyは実際の本番環境での判断からFeature値を抽出し、モデルのトレーニングと分析は本番環境からのPolicyの実際の呼び出しに基づいて行われます。より具体的に言えば、新しいFeatureを試してその有用性を評価したい場合、まずリアルタイムシステムとバッチシステムの両方に新しいFeatureを実装し、本番環境にデプロイして、UnderwritingのPolicyがそのFeatureを要求してAmazon S3にログを記録するようにする必要があります。そして、ユースケースに応じて相当な時間待つ必要があります。なぜなら、十分なデータを収集する必要があるからです。購入のライフサイクルの終わりを知りたい場合もあり、Featureの影響や有用性を評価するまでに数週間や数ヶ月かかることもあります。もし期待通りの結果が得られず、最終的に有用でないと判断された場合は、このプロセス全体をやり直す必要があります。
新しいソリューションのシステムアーキテクチャと実装
これら4つの問題をリストアップすると、それぞれに対応する理想的なシナリオも挙げることができます。
内部システムのデータベースダンプをデータソースとして使用する代わりに、プロデューサーとコンシューマーの間に厳密な契約を持つソリューションが欲しいと考えています。全履歴に対する日次の変換処理に依存する代わりに、成長に対してよりコスト効率が良く、スケーラブルなものが必要です。リアルタイムとバッチの実装が分かれている代わりに、理想的にはバッチ処理層とリアルタイム処理層の違いを最小限に抑えたいと考えています。データの成熟を待つ必要がある代わりに、既存の履歴データを使って新しいFeatureを実験できるツールが欲しいのです。
それでは、現在のソリューションとそのシステムアーキテクチャについて詳しく見ていきましょう。この新しいソリューションは、先ほど挙げた4つの課題にまさに対応しようとしているものです。まず、新しいソリューション自体の概要から始めましょう。 左上隅で気づくかもしれませんが、データソースがありますが、今回は内部データベースダンプの代わりにEventストリームやメッセージキューを使用しています。ここでは、プロデューサー自身が明確に定義したスキーマと明確に定義されたイベントがあり、これはプロデューサーとコンシューマーの間に厳密な契約があることを意味します。Klarnaでは、これらのデータソースやメッセージキューはKafkaトピックとして実装されています。これにより、コンシューマーはデータの正しい解釈とデータに関する前提を行うことができ、プロデューサーはどのシステムがそのデータに依存しているかを知り、変更について告知を行い、データ自体の変更がダウンストリームのシステムにどのような影響を与えるかを理解することができます。これは、内部データベースのエクスポートに依存する代わりに、チーム間でデータをやり取りするこの方法に移行するための、どちらかというと組織的な取り組みでした。
図に戻って見てみますと、基本的に2つのパートに分かれています。上部にはリアルタイム処理部分があり、下部にはオフライン処理部分があります。これらは中央にある Business Logic Library と呼ばれるもので接続されています。Business Logic Library は、アプリケーションから抽出されたビジネスロジックの実装です。これはランタイムにもインフラにも依存しないため、実行環境を気にすることなく、リアルタイム層とバッチ層の両方で同じ機能定義とビジネスロジックの実装を使用できます。
まずはリアルタイム処理層に焦点を当ててみましょう。 基本的に2つの側面があります:Amazon DynamoDB にデータを書き込む更新状態側と、DynamoDB からデータを読み取って特徴量計算を行う読み取り側です。大まかに言うと、データソースからイベントが来ると、Amazon ECS 上で実行される更新状態アプリケーションでそのデータを処理します。Business Logic Library を使用して状態を更新し、DynamoDB に書き込みます。そして右側では、与信ポリシーが特徴量計算サービスを呼び出し、DynamoDB から状態を取得し、Business Logic Library で定義された特徴量計算を適用して、その結果をポリシーに返します。
中央の DynamoDB では、特定のドメイン固有のデータモデルを持つ中間状態を保存しています。Klarnaでは、これは注文レベルで行われており、DynamoDB の1アイテムにつき1つの注文が対応しています。ここでシステムの開発者や設計者として選択を迫られます。書き込み側にどれだけの処理を置き、読み取り側にどれだけの処理を置くかを検討する必要があります。書き込み側でより多くの変換や計算を行い、更新状態ロジックでデータを事前集計すれば、読み取り側での処理が少なくなり、読み取りパフォーマンスが向上します。逆に、読み取り側でより多くの処理を行い、より汎用的なデータモデルを維持すれば、柔軟性が高まります。読み取り側では、より汎用的なデータモデルを維持することで柔軟性を確保していますが、その代わりにパフォーマンスは犠牲になります。
これは本質的にトレードオフの関係にあります。状態テーブルのテーブル定義の例を見てみましょう。これは私たちの問題領域に適用されたものです。ここではパーティションキーとして Order ID を使用しています。1つの注文につき1つの DynamoDB アイテムがあり、その他の属性として、customer_id があります。これは顧客IDに基づいて注文状態を照会できるようにするためのグローバルセカンダリインデックスとして使用され、そして実際の状態自体も保存されています。
ここでモックデータを使用した例を示します。左側には ID フィールドに UUID の集まりがあり、 state 属性には実際の注文状態のエンコーディングがあります。では、 中間状態とデータモデル自体をどのように更新するのかについて、もう少し詳しく見ていきましょう。先ほどの図の左側に 注目します。データソースからイベントを受け取ると、それは更新状態アプリケーションによって消費されます。そのアプリケーションは DynamoDB にアクセスして注文の現在の状態を取得し、Business Logic Library に基づいて現在の状態にイベントを適用します。そして更新された状態を取得し、それを書き戻します。
より具体的な例を見て、実際にどのように動作するのか説明しましょう。例えば、Customer 1がOrder 1に対して123の金額を支払ったというイベントがあったとします。 このとき、アプリケーションはDynamoDBのState Tableに対して、Order 1の現在の状態を問い合わせます。現在の状態が次のようになっているとしましょう: Customer 1のOrder 1で、未払い金額が123で、顧客がまだ支払っていないため支払い済み金額がない状態です。 ビジネスロジックの定義では、これらのイベントをどのように処理するかを定めています。この例はとてもシンプルで、ステータスが「支払い済み」の場合、Stateの支払い済み金額をイベントから受け取った金額に設定するというものです。
これら3つを組み合わせると、 現在の状態に対してイベントを適用し、そのイベントに対して定義されたビジネスロジックを使用することで、 支払い済み金額が123に更新された新しい状態が得られます。この更新された状態をState Tableに書き戻すことで、状態が更新されるわけです。 次に、DynamoDB State Tableのもう一つの側面である、リクエストに応じたFeature計算について見ていきましょう。 Underwriting Policyは、Feature計算サービスに対して特定の顧客に関するFeatureのセットを要求します。Feature計算アプリケーションは、その特定の顧客に関するGlobal Secondary Indexを使ってDynamoDBに問い合わせ、持っているすべての状態を取得します。これらの状態のセットが得られたら、Feature定義を適用してFeature値を算出し、Policyに返却します。
具体例を見てみましょう。 paid_amountという超シンプルなFeatureがあるとします。これは次のように定義されています - 状態のリストを受け取り、各状態の支払い済み金額を合計します。Policy側から Customer 1に関するFeatureセット(この場合、paid_amountと、今は気にしない別のFeature)を要求するリクエストがあった場合、Feature計算サービスはDynamoDBテーブルにアクセスして、その顧客の現在の状態を取得します。この例では注文が1つだけですが、複数の注文がある場合もあります。先ほどの例のように、Customer 1のOrder 1で支払い済み金額が123の状態があります。返却された状態をFeature定義に通すと、 paid_amountは123という簡単な結果が得られます。
これでリアルタイム処理の部分の説明は以上です。 次に、システムの重要な部分であるオフライン処理について詳しく見ていきましょう。先ほど見たシステムアーキテクチャの概要に戻って、この部分に注目してみましょう。
オフライン処理部分は、Sparkを実行するAWS Glueジョブで構成されており、Amazon S3にデータを出力し、すべてがAWS Step Functionsによってオーケストレーションされています。ご覧の通り、リアルタイム処理システムの各部分に対応するGlueジョブがあります。State更新アプリケーションに対しては、同じ変換をバッチ処理やオフラインで実行する対応するGlueジョブがあります。Feature計算についても、アプリケーションがリアルタイムで行うのと全く同じことをオフラインで実行するGlueジョブがあります。
オフラインプロセシングには、主に2つのユースケースがあります。1つ目は「Retrospective Calculation(遡及計算)」と呼ばれるもので、2つ目は「State Backfill(状態の再構築)」です。Retrospective Calculationは、既存の履歴データに対して特徴量の定義を適用し、特定の時点における特徴量の値を算出します。一方、State Backfillは、Amazon DynamoDBの状態テーブルを、現在の状態で効率的に再構築し、ブートストラップするために使用されます。
まず、Retrospective Calculationについて説明しましょう。これは基本的に、ある特定の時点におけるカスタマープロファイルのスナップショットを計算することです。以前の例を使うと、12年前の時点での顧客の支払い総額や支払い額の特徴量がどうだったかを判断できます。これは実際に稼働している特徴量である必要はありません。開発ブランチで特徴量Xを試験的に開発している場合でも、12年前の顧客データにそれを適用することができます。この遡及計算の入力は、決定時点と組み合わせた顧客IDのセットで構成されます。そして、その特定の顧客の、その決定時点におけるすべての特徴量の値でこのデータセットを充実させます。これは特徴量の実験、モデルのトレーニング、変更影響分析などの目的で使用されます。
Retrospective Calculationの設定を見てみましょう。左側にはKafkaトピックにあるデータソースがあります。これらは継続的にAmazon S3にエクスポートされており、すべてのイベントの完全な履歴がS3上に保存されています。遡及データセットを要求する人は、関心のある顧客IDと決定時点の組み合わせを含むデータセットを作成します。次に、Update StateのGlueジョブが、この入力データセットを受け取り、その顧客セットに関連するすべてのイベントを見つけ、以前に定義したBusiness Logic Libraryに基づいて状態を作成します。これは、Update State Applicationが使用するのと同じロジックを使用します。
重要な考慮事項の1つは、入力データセットに決定時点が含まれているということです。StateのGlueジョブでは、決定時点自体でフィルタリングを行い、その時点より前のイベントのみを含めます。その時点では知り得なかったことは含めたくないからです。これらの状態はAmazon S3上のデータセットとして出力され、Feature CalculationのGlueジョブによって取得され、これらの状態に対して特徴量の定義を適用して、すべての特徴量の値を取得します。データセット要求者は、そのデータにアクセスして、望む実験を行うことができます。
具体的な例を見てみましょう。3人の顧客がいて、最初の顧客には2021年と2023年の2つの決定時点があります。まず、このデータセットを持っているすべてのイベントと結合し、関連するイベントを見つけ、それらから状態を作成します。出力は次のようになります。1行目と2行目の状態が異なっているのは、状態3が2020年の後、2023年の前に作成されたためです。2020年の最初の決定時点では、状態3については知らなかったため、それは含まれていません。
新システムの利点と実装における課題
これらを Feature 定義の計算に通します。 ここですべての Feature 値が得られます。例として、顧客の支払額と決定時間のみを挙げています。最初の例では、異なる金額が表示されていますが、これは異なる State を考慮しているためです。最初のケースは State 1と2のみを含み、2番目のケースは State 1、2、3を考慮しています。
この Retrospective 計算の強みは、新しい Feature 定義を過去のデータに適用できることです。つまり、新しいデータを収集するのを待つ必要がありません。新しい Feature の実験や実装を既存のデータに適用できるため、イテレーションサイクルがはるかに速くなります。もう一つの重要な側面は、チーム内の内部ツールとして活用できることです。例えば、修正したいバグを見つけた場合、Feature の実装を変更しますが、その変更はモデルの結果に影響を与える可能性があるため、慎重に行う必要があります。
そのバグを含むデータでモデルを学習している場合、修正によってモデルのパフォーマンスが低下する可能性があります。Retrospective 計算を使用することで、変更を加えていないデータセットと変更を加えたデータセットを作成して比較することができます。これにより、Feature の分布の違いや、最終的なモデルのパフォーマンスへの影響を確認することができます。
オフライン処理のもう一つの重要なユースケースは、State Backfill です。 要約すると、リアルタイムアプリケーションで使用している State テーブルを再構築し、ブートストラップすることが目的です。これは、更新ロジックを変更する場合や、データモデルを変更する場合に必要となります。データモデルにフィールドを追加または削除する場合、新しい定義を使用して、リアルタイムのデータストリームからの新しい受信イベントだけでなく、新しい定義とState ロジックを使用して過去のイベントも適用したいと考えます。
これをオフラインで行う理由は、バッチで完全な履歴を処理する方が費用対効果が高いためです。これらのデータセットは通常非常に大きく、Apache Kafka トピックのデータ保持期間などを考慮する必要がありません。リアルタイムシステムでは、Kafka 上の完全な履歴にアクセスすることは実際には難しいのです。 State Backfill のセットアップは Retrospective 計算と似ていますが、さらにシンプルです。データソースは完全なイベント履歴とともに Amazon S3 にエクスポートされ、その後、最初の AWS Glue ジョブが State を作成します。
このケースでは、入力データセットを考慮する必要はありません。これまでに蓄積された全てのイベント履歴を取得し、データを処理して、各注文の状態を作成します。ここでは判断のタイミングを考慮する必要はありません。なぜなら、基本的に全ての注文の現在の状態が必要だからです。現時点までの全てのデータを処理し、Amazon DynamoDB JSONの形式で状態を整形して作成します。これによりDynamoDB S3インポートを使用できるようになります。これはAmazon S3上のデータセットを取得して新しいDynamoDBテーブルを作成します。その後、リアルタイムの状態更新アプリケーションを使用して新しいテーブルへの書き込みを開始し、準備が整い次第、Feature計算アプリケーションも新しいDynamoDBテーブルから読み取るように切り替えることができます。このプロセス中は、ダウンタイムをゼロにするために2つのDynamoDB状態テーブルを同時に稼働させています。
ここで、以前に挙げた要件のリストと、解決したかった課題を振り返ってまとめてみましょう。まず第一に、プロデューサーとコンシューマー間の厳密な契約については、定義されたスキーマと定義されたイベントを持つ厳密に定義されたイベントストリームを採用することで解決しました。これにより、コンシューマーは適切なデータの解釈と前提を行うことができます。コスト効率とスケーラビリティについては、リアルタイムでデータを処理することで解決しました。以前のセットアップでは毎回全履歴を再処理する必要がありましたが、今回は注文に対する変更のみを処理します。これをオフライン処理やAmazon DynamoDBとAmazon S3を使用したコスト効率の良い状態バックフィルと組み合わせることで、より成長に対応できるコスト効率の良いスケーラブルな方法を実現しています。
3番目の、バッチ処理とリアルタイム処理の違いを減らすという点については、ビジネスロジックをアーキテクチャやランタイムから分離するBusiness Logic Libraryによって解決しました。今では機能を追加または変更する際には、ライブラリに対してのみ変更を行えば、それが自動的に実行環境全体に反映されます。4番目の、新機能と履歴データを使った実験については、オフライン処理と遡及計算機能によって実現されました。これにより、データが成熟するのを待つ必要がなくなり、より迅速なFeatureエンジニアリングの反復が可能になりました。
この実装を進める中で、多くの障壁や課題に直面しました。その中で特に議論する価値があると思われる3つを紹介します。1つ目はデータモデリングの問題で、これは非常にドメイン固有で複雑です。データ処理の方法、Featureで答えたい質問、そしてデータの鮮度やレイテンシーに関するシステム要件に依存します。これは非常に重要です。なぜなら、データモデルで決定や定義したことは、ビジネスロジックやアプリケーションの実装全体に大きな影響を与えるからです。具体的な課題の1つは、読み取り時のパフォーマンスと柔軟性のバランスを決めることです。私たちのチームは、最初は機能を実験したかったため、非常に柔軟な方法で始めました。しかし、製品が成長し、ステークホルダーからのサービスへの要求が増えるにつれて、柔軟性を犠牲にして読み取りパフォーマンスを向上させる必要がありました。
2つ目の課題は、現在も抱えているオフライン処理の部分です。SparkでPython UDFを実行していますが、これはデータのシリアライズが必要で、JVMとPythonインタープリタを介して通信する必要があるため、最も効率的なソリューションとは言えません。この点について経験やよいアイデアをお持ちの方は、後ほど私とお話しください。最後の学びは、Featureの品質は、ソースデータの品質以上にはならないということです。システムや実装がどれだけ技術的に完璧であっても、データの内容に関する課題は常に存在します。私たちは、ほとんどの問題が実際にはデータの内容に関連していることを発見し、データソースとコーナーケースの理解に多くの時間を費やしました。データ検証ツールとモニタリング、そして開発者がコーナーケースのデバッグや生イベントからFeatureまでのデータフローを可視化しやすくするためのツールへの投資は価値があると学びました。
最後までご視聴いただき、ありがとうございます。皆様のシステムに活かせるようなヒントやアイデアを得ていただけたのではないかと思います。この後も会場におりますので、ご質問やさらなるディスカッションをご希望の方はお声がけください。本セッションにご参加いただき、ありがとうございました。素晴らしい re:Invent の1週間をお過ごしください。ありがとうございました。
※ こちらの記事は Amazon Bedrock を利用することで全て自動で作成しています。
※ 生成AI記事によるインターネット汚染の懸念を踏まえ、本記事ではセッション動画を情報量をほぼ変化させずに文字と画像に変換することで、できるだけオリジナルコンテンツそのものの価値を維持しつつ、多言語でのAccessibilityやGooglabilityを高められればと考えています。
Discussion