現場から学ぶMLOps: MonotaROでの実践的アプローチ~オンライン推論編~

はじめに

こんにちは。MonotaROで機械学習エンジニア兼、Tシャツのモデルを務めている新卒3年目の長澤です!

最近は健康のためにスポーツをしているのですが、そのスポーツの疲れで日々が辛くなってきました。観戦と自分で身体を動かす方の割合(重み)をバンディットを使ってうまく最適化していきたいこの頃です。

今回は、自分がここ1,2年(2023~2024)で取り組んできたMonotaROにおけるMLOpsの取り組みについて、実例を交えながら紹介します。MLOpsの実例はあまり世の中に出回っていないので、一つの事例として読んでもらえれば嬉しいです。

この記事で紹介すること

  • MonotaROにおける機械学習エンジニア
  • MLOpsに取り組むにあたっての背景と辛い点
  • MLOpsの事例紹介と今後やりたいこと

この記事で紹介しないこと

  • MonotaROで開発・運用しているアルゴリズム・機械学習モデルについて

MonotaROにおける機械学習エンジニア

機械学習エンジニアは会社によって業務内容にばらつきがある職種だと思っています。自分が知っている中でもデータエンジニアによっているところや、アルゴリズム開発からバックエンド開発まで幅広く行っているところもあります。MonotaROにおける機械学習エンジニアの主な業務は、データサイエンティストが考案したアルゴリズムをユーザーにいち早く届けることです。このため、バックエンドの開発や、アルゴリズムを運用するためのインフラの開発をメインに担当しています。

その中でも今回は、MonotaROのWebサイトにおける検索結果のパーソナライゼーションについてのMLOpsの取り組みを紹介していきたいと思います。

パーソナライズドランキングとは

検索結果のパーソナライズドランキングは、ユーザーの検索意図に合わせてキーワード検索の結果を並び替える技術です。MonotaROでは2000万以上の商品を扱っており、同じキーワードであってもユーザーごとに求める商品は異なります。今回紹介するパーソナライズド検索API(PSAPI)ではそのようなユーザーの要望に答えるために検索結果の商品リストに対してパーソナライゼーションを行い、ユーザー体験の向上を目指しています。

同じキーワードだが業種ごとに求めるものが違う例

直近ではより高度なパーソナライズ化に向けて機械学習モデルを使用しています。今回は、機械学習モデルによるリアルタイム推論とその運用に焦点を当てた例を紹介します。

MLOpsに取り組むにあたっての背景と課題

上記で紹介した機械学習モデルを使用した案件がABテストで成果を残すことができ、恒久対応することになりました。これにより本格的に機械学習モデルの運用フェーズに入ることになり、運用面の整備が必要になってきます。また今後、機械学習モデルを使用した案件が増えていくことも考えられるため、横展開がし易いように開発周りも整備するなどのMLOpsに取り組む必要性が出てきました。

MLOpsのプロジェクトスタート時

MLOpsはちょうどホットな分野だということもあり、新卒2年目のタイミングでMLOpsのプロジェクトのメインメンバーとして立候補しました。0から始めて主導して進めていくのはこのプロジェクトが初めてだったので、右も左もわからないまま進めたのを覚えています。

最初に現状を把握するために、他社事例やGoogleの提案するMLOpsの資料を読み込み、現状との差分を確認しました。しかし、いろんな事例を確認しても、良さそうなことはわかるものの、弊社とは実行環境が少し異なっていたり、明確に困りごとがなかったりなど、何から取り組むべきかわからない状況でした。当時はプロジェクトのwhyとwhatの部分を考えて書いてみてはやり直しを繰り返していました。

当時の悩み事は以下のとおりです。

  • 運用中の機械学習モデルがまだ少ないこともあり、課題が明確化されていない
    • この辺困りそうというのは肌感でわかるが、今手を付けるべきかの判断が難しい
  • 他社事例を参考にしようにも、自社環境との差分が大きくあまり参考にはならない
    • 分析するための環境やサービスを動かす環境などのインフラ面
    • 事業の差や、求められる機能の違いなどのサービス面
  • アルゴリズムを考えるデータサイエンス側と組織的に分かれていたこともあり、機械学習モデルの学習や評価周りには手を付けづらい
    • MonotaROでは学習〜評価周りがデータサイエンティストの担当で、ユーザーに届ける実装部分は機械学習エンジニアが担当となっている
    • お互いの理解度が低く、お互い課題が見えている状態ではない

漠然とやったほうが良さそうなことはわかっているものの、どこから始めればよいのか、ほんとにそれで正しいのかなどがわからず、なかなかスタートを切ることができないような状況でした。

MLOpsとりあえず始めてみる期

上記の通りどう進めていいかわからないままでしたが、時間は無情にも過ぎていきます。なのでMLOpsの全体像を深く考えず、明確になっている困りごとや、直近で辛くなりそうなことへの対処にフォーカスを当てて進めていくことにしました。

このときの取り組みは以下のとおりです。

  • データサイエンス側が持つ機械学習モデルの学習、評価周りには一旦手を出さず、機械学習エンジニアが単独で触れるシステム(バックエンド、インフラ)の運用最適化に注力する
  • モデルの定期更新や正常に動いているかの監視など、機械学習モデルの外側から埋めていく
  • 今後運用する機械学習モデルが増えることも考えてシステムの横展開の準備を進めていく
  • 確実な100点はわからないので、自分が思いつく100点を試してみる。「だめそうであればすぐやめる」をモットーに進める

これらを踏まえて、自分が取り組んだMonotaROにおけるMLOpsの具体例を紹介したいと思います。

MLOpsの事例紹介

今回は3つの取組みを紹介します。

  • 機械学習モデルをマイクロサービスAPIとして独立させる
  • 機械学習モデル推論用のAPIテンプレートの作成
  • 特徴量ロギングによる学習サイクルの構築と監視

1つ目と2つ目はシステム構成や仕組みにより横展開をしやすくする取り組みで、3つ目は運用にフォーカスした取り組みとなっています。

機械学習モデルをマイクロサービスAPIとして独立させる

背景

初期の段階では、Pythonで書かれたパーソナライズド検索API(PSAPI)の内部で機械学習モデルを読み込み、内部で推論するようなシステム構成で実装していました。ここでいくつかの問題が発覚しました。

  • Kubernetes上でAPIを動かしているが、機械学習モデルの読み込みがボトルネックとなり、コンテナの起動に時間がかかる
    • 必要なリソース(CPUやメモリ)も必然的に高くなる
  • 前提としてPSAPIでは複数のロジックが走っているが、機械学習モデルの更新のたびにPSAPI全体のリリースがなされるため、PSAPIが提供している他のロジックにも影響が出る可能性がある
  • 機械学習モデルは定期的に更新したいがPSAPIとはリリースの頻度が異なる
    • 本来は不要なAPIのリリースをモデル更新時に行う必要性がある。今後モデルが増えると、不要なAPIのリリース頻度が増えていく
    • 例えば週一で更新したいモデルができたらPSAPIのリリース頻度も週一になる

こういった背景からPSAPIの内部で機械学習モデルを読み込むのではなく、別のマイクロサービスAPIとして切り出すことで、リリース頻度や他ロジックへの影響を抑えることにしました。

やったこと

図にすると以下のような構図になります。

マイクロサービス独立の概要図

このマイクロサービス化によって、PSAPI単体のリソースは抑えることができるようになりました。また機械学習モデルのバージョン管理も推論APIのイメージ内に含めることで、イメージタグ=機械学習モデルのバージョンとしてまとめることができ、ロールバックなども比較的簡単にできるようになりました。

実際にこの構成になってから約1年半ほど経ち、月毎にモデルの更新を行っていますが、モデル更新に起因するリリース失敗は発生していません。

機械学習モデル推論用のAPIテンプレートの作成

背景

上記の「機械学習モデルをマイクロサービスとして独立させる」により、単品の機械学習モデルをAPIとして動かして行く方向性が決まりました。しかし案件のたびに機械学習モデルのAPIを0から実装していくとなると、開発コストが高くなってしまいます。そのため横展開がしやすく、機械学習モデルを素早くAPIとして動かす仕組みづくりとして、APIテンプレートというものを作成しました。

やったこと

APIテンプレートを考えるにあたって、まず大事なのが機械学習フレームワークです。Pythonの機械学習フレームワークは多く存在し、それぞれにAPIとして動かす方法があります。例えばTensorFlowであればTensorFlow Servingがありますし、PyTorchであればTorchServeなどが存在します。MonotaROでは様々なフレームワークが使われているため、それらの差分をある程度吸収できることが要件としてありました。

以下、APIテンプレートを作成するにあたって必要だった要件です

  • TensorFlowやPyTorch、XGboost、LightGBMなど複数の機械学習フレームワークを動かせるようにする
  • 機械学習モデルの読み込みや推論処理などを書くだけでAPIとして動くようにする
  • APIとして動かしたときの機械学習モデルの入出力がローカルと一致しているかのテストを行えるようにする
  • APIとしての最低限の機能は保証できるようにする
    • リクエストパラメータなどを含んだアクセスログを吐き出せる
    • health check用のエンドポイントが存在する(Kubernetesのhealth check用)
    • エラーハンドリングなどを適切に行う

自分達はこれらの要件を満たすために、FastAPIを使用したAPIテンプレートを作成しました。

APIテンプレートの全体構成図

それぞれのコンポーネントを説明すると以下のようになります。

  • 青い箇所にはフレームワークに合わせたDockerfileとrequirements.txtを置いており、Dockerイメージを提供
    • 基本的に機械学習モデルを動かすときに必要なライブラリはフレームワークに依存しているため、それぞれのフレームワークに合わせたイメージを用意
    • フレームワーク以外に特別なライブラリが必要な場合は別途イメージを作成
  • 緑の箇所には自作したPythonライブラリを置く
    • FastAPIをベースに作成したもので、 以下のようにすることで最低限のAPIの機能を保持した状態のFastAPIのアプリケーションを使用できる
from api_template import create_app

app = create_app()

@app.post("/post_endpoint")
def post_func()
  • 黄色の箇所はGoogle Cloud Storageで学習済みのモデルを保管
    • テスト用のデータをアルゴリズム開発者に用意してもらい、APIでの推論結果との比較のテストを行う
    • APIでの推論結果の一致を見ることで推論APIとしての機能は満たしていると言える

実際にAPIテンプレートを使用した推論に使用するコードサンプルです。

from pydantic import BaseModel

from api_template.app import create_app

# templateからappのインスタンスを作成
app = create_app()


# モデルの読み込み(サンプル)
MODEL = load_model(MODEL_PATH)

# 機械学習モデルへの入力の特徴量定義
class Instance(BaseModel):
    """特徴量の列挙""" 

# リクエストスキーマ定義
class RequestBody(BaseModel):
    instances: List[Instance]

# レスポンススキーマ定義
class ResponseBody(BaseModel):
    """sample"""

# 推論関数
@app.post("/predict")
async def model_predict(request_body: RequestBody) -> ResponseBody:
    # データの前処理
    input_data = pre_process(request_body)
    # モデルの予測
    predict = MODEL.predict(input_data)
    # response形式への変換
    response = post_process(predict)
    return response

APIテンプレートを作成したことで開発工数が大幅に削減され、早いと1日経たずにAPIとして開発環境にデプロイできるようになりました。 直近では推論APIの数が増えていますが、データサイエンスのメンバーもAPI実装に参加出来るようになったりと横展開もできています。 また、APIテンプレートをベースとしたCI/CD環境を構築することで、リリースプロセスも改善され、1クリックでビルドからKubernetesのマニフェスト更新までできるようになっています。

特徴量ロギングによる学習サイクルの構築と監視

背景

リアルタイムの推論を行う機械学習モデルの運用において重要な点が2つあります。

  • 現在動いている機械学習モデルが正常に動いているか
    • 時間が経つに連れ入力データの分布が変化し、推論精度が落ちることがある
    • 異常を検知するためには機械学習モデル入出力の監視が必要
  • 機械学習モデルの再学習を行う際に、楽に学習データの準備ができるか
    • API上で前処理した特徴量をSQLで再現することはかなり困難
    • 特に特定日時のデータストアの再現や、リアルタイムのユーザーログを再現し加工する部分はSQLでは表現しづらい

こういったデータの監視やデータ再現をより簡単に行うために特徴量ロギングの仕組みを取り入れました。機械学習モデルの入出力をログとしてすべて保存しておき、それらにラベルをつけることで学習時に再利用したり、入出力の監視を行います。

やったこと

APIテンプレートで作成した機械学習モデルの推論APIで入出力をログとして吐き出し、GCPのログシンクの機能を使用してBigQueryに同期する方法を選びました。MonotaROではほとんどのデータソースをBigQueryで管理しており、特徴量のデータやラベルとなるユーザーログもすべてBigQueryに保存してあります。Bigquery上でリクエスト識別子を使ってユーザーログと結合することで、学習サイクルを構築しています。

特徴量ロギングの全体図

監視についてはBigQueryをデータソースとした定期監視と、Looker Studioのダッシュボードを使って行っています。現時点でなにかアラートが出たみたいなことはありませんが、状況を確認するうえで重宝しています。

学習データに関しても、実際のログを使用できるためオンラインとオフラインでのデータ差分をなくすことができたり、オンラインのデータを使ったデバッグなども行っています。

まとめ

MonotaROにおけるMLOpsの事例を3つほど紹介してみました。内容的にはかなり初歩的な部分ではありますが、エンジニア目線で見て機械学習モデル運用が楽になっています。いまのところ特に大きな失敗はなく進んでいますが、1,2年の経験を通して学んだことをまとめたいと思います。

MLOpsを取り組んでみての感想と学び

  • MLOpsには正解がない
    • 他社の取り組みとかを見てもそれが当社に合うのかが不明
    • 明確になっている課題や、直近で課題になりそうな部分から潰していくしかない
  • 事例数が少ないタイミングで基盤化が難しい
    • 事例が少ない状況で基盤を作ってもユースケースが増えていき、全てに対応出来るのかわからない
    • 事例数も少ないので課題という課題が明確になりづらい
  • MLOpsを進めるためにはアルゴリズム開発とエンジニアリングの両面に立つことが必要
    • これまではエンジニア側に近い部分しか触れていないが、学習〜評価周りにも課題はある
    • アルゴリズム開発と連携し、お互いの困りごとを共有することが大事
  • とりあえずやってみる精神で進めていくことが大事
    • 何が正解かわからないのでとりあえずやってみる
    • やってみてだめだったらすぐに捨てる(撤退のしやすさも大事)

今後やっていきたいこと

せっかくなので自分が考えている今後やっていきたいこともまとめます。

  • より高度な入出力の監視
    • 現時点で入力や出力に異常がありそう(分布のズレがおきた)などは検知できていますが、性能の劣化に対してはあまり監視ができていない
    • 今のバージョンが前のバージョンよりも優れているか、などの機械学習モデルのバージョン管理も含めた監視を行っていきたい
  • 学習~評価周りのパイプライン作成と自動化
    • 冒頭でも話した通り、現状はエンジニア側に近い部分しか触れていないため、アルゴリズム開発をサポートする体制を作っていく
  • VertexAI Predictionsなどのインフラ面のフルマネージド化
    • 現在GKEでマイクロサービスを運用しているが、API数が増えていくにつれ管理コストが上がってきたので、フルマネージドに移行したい

最後に

モノタロウでは機械学習エンジニアの採用募集をしております。 この記事に興味を持っていただけた方や、モノタロウのエンジニアと話してみたい!という方はカジュアル面談 登録フォームからご応募お待ちしております。