Kaggleで考えるMLシステムの設計とアーキテクチャ

こんにちは、えいりんぐーです

MLシステムを作るプロジェクトに携わっていますが、やっぱりMLシステムを作るのは難しいなーと感じでいます
個人的には、設計やアーキテクチャがいまいちだなーと感じることが多いです

というわけで、Kaggleのコンペ毎に、どんなアーキテクチャが適切かを考えてみたいと思います
とはいうものの、まずは、設計やアーキテクチャを考えるうえで、ポイントとなるような話をします

# 一応TLDR

- 良い設計、良いアーキテクチャを学ぶ
- ビジネス上重要な要素を把握する
- 少なくとも最悪でないアーキテクチャを選ぶ
- デプロイ容易性とテスト容易性は、業績に影響する

# 良いコードを知る

https://note.com/cyberz_cto/n/n26f535d6c575

さて、上記の記事の通り、何が良いのか、なぜ良いのか、を知っていないと、良いものを作れないです

MLはただでさえ技術的負債が生じやすいです
なので、良いコード、良いアーキテクチャを知ることで、構想や設計段階から、負債が生じにくいシステムを作りましょう

記事で述べられている観点は以下の通りです

- 凝集度を高くして、意味のある機能の集合にする
- 結合度を低くして、不要な依存関係を減らす

またクリーンアーキテクチャの方針として、「レイヤーに分離し、関心の分離を行う」、「それらの依存性は内側だけに向かう」ことを挙げていますが、私がクリーンアーキテクチャを詳細には把握していないため、ここでは細かく取り上げません

# MLシステムの要素

有名なMLの技術的負債の論文の通り、実際にはMLコード自体はシステム全体の一部でしかありません
前述の凝集度や結合度も踏まえて、分離できる要素は分離しましょう

![ML components](https://cdn-ak.f.st-hatena.com/images/fotolife/s/sh111h/20200622/20200622074908.png)

[TFX](https://www.tensorflow.org/tfx/guide) のように、各要素を分離しつつ、統合的に実装できるフレームワークもあります

![TFX](https://www.tensorflow.org/static/tfx/guide/images/prog_fin.png)

# ソフトウェアアーキテクチャの基礎

システム全体のアーキテクチャを考える上では、[ソフトウェアアーキテクチャの基礎](https://www.oreilly.co.jp/books/9784873119823/)が参考になります
書籍では、重要なこととして以下を挙げています

- ソフトウェアアーキテクチャはトレードオフがすべて
- 少なくとも最悪でないアーキテクチャを選ぶ
- ビジネスドライバーを理解してアーキテクチャ特性に落とし込む

書籍中で考慮されているアーキテクチャ特性は下記のものです

- 分割タイプ: 何をもとにコンポーネントが分割されているか
- 量子数: 独立してデプロイ可能なアーティファクト
- デプロイ容易性
- 弾力性: ユーザーやトラフィックの一時的な増加への対応
- 進化性
- 耐障害性
- モジュール性
- 全体的なコスト
- パフォーマンス
- 信頼性
- スケーラビリティ
- シンプルさ
- テスト容易性

つまり、ユースケースや開発チームの状況を考慮して、優先的な特性を満たすアーキテクチャを選択する必要があります

# 企業のパフォーマンスとデプロイ容易性

さて、アーキテクチャ特性として量子数やデプロイ容易性がありました
このデプロイ容易性 (とテスト容易性) は、下記の記事の通り企業の業績に影響します

https://www.publickey1.jp/blog/22/22022_1.html

このことを踏まえると、可能であれば、サービス全体において、ML部分は下図のように、1つの独立したサービスとしてデプロイできるアーキテクチャが望ましいと考えられます

![ml_service_arch_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267335/bb6622f1-4355-9587-4379-7afc8fb84092.png)

実際、**MLサービスの中**では、前述の論文のとおり、特徴量抽出や推論精度のモニタリングなど様々な要素がありますが、**外側のサービスからすると**、基本的にMLサービスの予測結果だけを必要としているはずです
つまり、ある人は商品Aを買いそうか、ある写真の中に猫が写っているか、などが知りたいだけで、その推論結果が妥当かつ利用できるかぎり、他のサービスと密結合にする必要がありません

もちろん、推論や学習で使うデータは、他のサービスから生まれているため、そのデータのやり取りやパイプラインについては注意して検討する必要があります

MLサービスのデプロイについては、[メルカリがデザインパターンを公開している](https://mercari.github.io/ml-system-design-pattern/README_ja.html)ので、参考になります

# Kaggleコンペを考えてみる

ここまでの話を踏まえて、いくつかのKaggleのコンペを題材に、アーキテクチャを考えてみましょう

## Recruit Restaurant Visitor Forecasting

https://www.kaggle.com/c/recruit-restaurant-visitor-forecasting

1つ目はリクルートの飲食店の来店人数予測コンペです
Airレジやホットペッパーグルメの予約データなどを使って、来店人数を予測するコンペです
2018年当時リクルートが来店数を予測するサービスをアナウンスしています

https://xtrend.nikkei.com/atcl/case/bdt/18/022000381/

大まかに必要な要素とアーキテクチャを考えてみましょう

- 飲食店経営者は、来店数が知りたい
- 予測サービスの運営者は、予測精度や学習の管理をしたい
- ユーザー数や機能が多くなれば、マイクロサービスの方がスケーラブルだが、ここでは例として古典的な多層アーキテクチャで良さそう
- データは基本デイリーなので、推論や学習もデイリーのバッチで良さそう
- AirレジやHPGのデータは、MLで使えるようにレプリケーションなどが必要そう

上記を踏まえて、アーキテクチャを構成すると以下のようになります

![recruit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267335/bc7a4c1f-e283-8a91-608b-dde49f944022.png)

ここでは、予測サービスが1つのまとまったサービスであるとして、UIを実装する形にしていますが、
予測サービスが、より大きなサービスの中の一機能であるとするなら、UIは外に出して、図のバックエンドを通して、必要なデータをやり取りするマイクロサービスとして構成することもあるでしょう

## IEEE-CIS Fraud Detection

https://www.kaggle.com/c/ieee-fraud-detection

次は IEEE-CIS のカードの決済が不正かどうかを予測するコンペです
決済システムとして、次の要素が求められるでしょう

- 多数の同時ユーザー、トランザクションに対応できるキャパシティとスケーラビリティ
- 決済処理の低いレイテンシー
- 高可用性
- 耐障害性
- (一部データの)強い整合性
- レコメンドや広告出稿ほど、予測対象の分布は変わりやすいわけではなさそう

上記を踏まえアーキテクチャは下記のように検討できます

- 処理の複雑性や運用負荷は高いものの、スケーラビリティのあるマイクロサービスアーキテクチャを採用
- 決済リクエストは、同じくスケーラビリティと可用性を提供しうるトピックやキューでバッファリング
- 決済リクエストは一度決済エントリというサービスで受け取って、下流のサービスに渡していきます
    - 図中では、推論サービスへ直列につながっているが、実際にはその他未記載のサービスへと伝播
- 推論による正常な決済か、不正決済かの判断をもとに、決済の可否をデバイスに返送
- 各マイクロサービスのデータをデータレイクに集め、必要なデータを使って学習をバッチ処理
- 決済サービスなので、一部処理は同期や強整合性とするが、低レイテンシーもまた重要なため、可能な部分で非同期、結果整合性により、処理速度を向上

![ieeecis.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267335/ee922fb2-b309-28aa-801a-b6efaa4547c6.png)

決済システムはSLAが非常に高く、かつやり取りする要素の多いサービスです
私自身は関わったことがないため、正直どんなアーキテクチャにすべきなのか、想像に頼った部分が大きいのです
しかし、ググるとPayPayやメルペイ、楽天の決済システムのアーキテクチャが公開されており、参考にさせていただきました

- [PayPayはなぜマイクロサービスアーキテクチャを採用したのか?決済プラットフォームの構成と狙いを解説](https://logmi.jp/tech/articles/321649)
- [マイクロサービスにおける決済トランザクション管理](https://engineering.mercari.com/blog/entry/2019-06-07-155849/)
- [【レポート】楽天の大規模決済システムを支えるAWSアーキテクチャ #AWSSummit](https://dev.classmethod.jp/articles/aws-summit-online-2020-cus-65/)
- [高まるキャッシュレス需要に決済システムの低コスト化で応える](https://special.nikkeibp.co.jp/atclh/NXT/21/amd0326/page2/)
    - ノード追加とLBを使って3層アーキで構成する例
- [イベント駆動アーキテクチャについて](https://speakerdeck.com/massa142/event-driven-architecture)

## Shopee

https://www.kaggle.com/competitions/shopee-product-matching/overview

最後に、Shopeeの画像とタイトルをもとに、同一商品を同定するコンペです
これまでと違い、画像と自然言語を扱います

Shopeeはシンガポール発のECモールサイトで、さまざまな販売者が出品できます
出品商品の中には、画像や説明が違っても、同一商品である場合があります
それらを適切に同定できれば、モール上で適切な提示ができますし、スパムの除去にも役立ちます

ちなみに、外側のサービスからすれば推論結果が妥当であればよく、MLサービスの内部がどうなっているかは重要ではない、と書きました
しかし、こういったマルチモーダルなデータで、類似物検索となると、MLサービス内部はかなり複雑になりますね

- アジア有数のECモールであり、スケーラビリティが必要
- 商品情報、在庫管理、決済、配送、など様々な機能が必要
- 商品登録時に、商品マスターにある商品ラベルと紐づけたい
- 高可用性
- 耐障害性

上記を踏まえ、次のようなアーキテクチャを検討します

- スケーラビリティと多様な機能の進化性に対応するためマイクロサービスを採用
- 販売者の商品登録時に、MLの推論で、推奨される商品ラベルを提示
- 購入者は、Shopeeサイトから商品を見たり、購入したりする
- 学習は、予測対象の分布がそこまで変わりやすくないため、バッチ処理とする

![shopee.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267335/c662f655-6c26-97b4-ccc3-79720eebb0e0.png)
(細かいDBの配置は省略)

ECは、こういった出品型のモールや、10Xが提供しているECのプラットフォームなどがあるため、実際自分で0から構築することはあまりないかもしれません
一方で、Amazonがリアル店舗を出したり、オムニチャネルという取り組みがあったりと、今後も新しいサービスの可能性があるため、基本的な仕組みとどう進化できるかを考えるのは重要ではないでしょうか

# 終わり

以上です
サービスとして求められる要素を踏まえて、アーキテクチャを考えてみました

個別のプログラムを実装していくのは楽しいです
しかし、設計やアーキテクチャを適切に検討できないと、実装の段階で右往左往したり、出来上がったものが思ったようなパフォーマンスを発揮できなかったり、その後の改善や拡張も難しくなったりします

ここで検討したものは思考実験的ですが、「少なくとも最悪でないアーキテクチャを選」んだつもりです
とはいえ細かい構成や、もっとマシなアーキテクチャもあると思いますので、今後も学んでいきたいと思います

本記事が参考になれば幸いです