Gunosyデータ分析ブログ

Gunosyで働くデータエンジニアが知見を共有するブログです。

広告用の機械学習ワークフローを刷新しました

こんにちは。データサイエンス部 ML チームの村田です。スプラトゥーンで新シーズンがはじまりましたね。

この記事は Gunosy Advent Calendar 2023 の 10 日目の記事です。前回の記事は otake さんの パーソナライズド動画推薦システムをつくる でした。

はじめに

弊社では Gunosy Ads というサービスを提供しているのですが、Gunosy で配信している広告効果を上げるために各広告に対するユーザーの CTR や CVR を推定しています。

最近これらの推定モデルでニューラルネット系のモデルを使用するために、学習パイプラインを再構築したため構築をする上で考慮した点を紹介させてもらいます。

今回作成した学習パイプラインは弊社でのドメインに依存して決定されている部分を多分に含むため、一般的な機械学習のベストプラクティスとは限らないことに注意してください。

新規広告推論モデル学習パイプライン

はじめに広告に対する推論をする上で学習パイプラインがどこの立ち位置であるかを示すために、学習パイプラインを含む推論サーバーにモデルや特徴量を渡すための外観を紹介します。

システム外観

  1. 機械学習用のレポジトリでリリースタグを切ると GitHub Action 経由で Docker Image が build されて ECR にプッシュされる
  2. Digdag がバージョンに対応した Docker Image のイメージを Pull する
  3. Digdag はワークフローの定義ファイルに従って ECS Task として上記 Docker Image を実行をしている。主に定期実行とタスク失敗時のリトライ処理の責務を持っている
  4. 学習済みモデルの ONNX ファイルを S3 に配置する
  5. 上記のモデルが使用する各ベクトルは RDS に保存される
  6. 推論サーバーが更新に合わせて上記を読み込み、モデル推論に活用する

学習パイプラインは主に 3 ~ 5 の部分に該当します。後述している学習パイプライン内の ETL ~ CI までは Digdag でもワークフローとして管理可能ですが、その部分の管理は Python 側で gokart (https://github.com/m3dev/gokart) により管理をしています。学習パイプラインの Digdag に対する依存度を下げるためにそのような決定をしています (Image を実行できる環境であれば、今後どのような環境になってもコスト低く移行できるようにするため) 。

学習パイプライン外観

次に上記の工程で Digdag から ECS Task として kick された Image の内部で何が行われているかの外観を紹介します。

  • ETL
    • AWS Athena を使用して必要なデータの取得を行う
      • 学習に使用するユーザーの行動ログ
      • 広告やユーザーの特徴量
  • Train
    • AWS Athena で行う実装コストが高い特徴量の作成
    • モデルの学習
    • モデルの評価
      • slack に評価用データセットでの性能を通知
  • Release
    • 推論サーバーで使用しやすいように特徴量のデータ形式の変換
    • モデル (ONNX) を S3 に配置。特徴量は S3 や RDS に配置
  • CI
    • Release において適切にデータが置かれているか確認
    • 推論サーバーとローカルのモデルでの推論に差分が無いかを確認 (後述)

機械学習用のレポジトリの環境の構築には Docker + Poetry を使用しています。今回の刷新にともない、全体の構成にはあまり手は加えておらず双方ともに社内での利用率も高いため使いやすいものを使いました。最近だと開発者の PC の M1 Mac 使用率が高くなり、M1 Mac 用の Image と実運用環境用の Image でのギャップを吸収するコストが高くつきそうだったため、ローカルの環境合わせは無理に Docker は強制せず Poetry に任せていることも多いです。

機械学習ワークフロー構築時に意識したこと

刷新に伴って意識したことについていくつか紹介させてもらいます。紹介するにあたって、モデル作成の要件や A/B テスト等を意識したバージョン管理についての前提について整理します。

前提

  • モデルを一日のうちに何度も更新している。
    • 使用している特徴量の性質上、コールドスタートの影響が大きいので新規の広告への性能を上げるために新しいモデルで再学習している。
  • A/B テストのために複数バージョンのモデルが同時に更新、デプロイされる

まとめると、モデルや特徴量の継続的な改善のために A/B テストを行うのですが、そのため A/B 対象の各モデルは1日のうちに独立に何度も更新をされている状態です。

ETL

特徴量の作成をバージョンごとの学習パイプラインから分離

従来の学習パイプラインでは AWS Athena を使用して広告ログの集計を行い、複数のバージョンがそれぞれ特徴量の作成をしていました。メリットは特徴量がバージョンに対して独立になっているので、改善のための特徴量の破壊的な変更が比較的自由にできる点でした。その反面、特徴量の改善以外の新しいバージョンにおいては、完全に同じ特徴量を作成しているのにも関わらず倍のコストをかけながら作成していました。データの量が多くなったり、同時実行数が多くなるにつれて AWS Athena 側の制限にもかかることが多くなり、あるバージョン数以上は同時に A/B 検証ができないというような制約にもなっていました。

ETL offline feature

今回新しく作ったものでは特徴量の作成は特徴量のバージョン管理を別に行い、モデルのバージョンとは独立して作成をするようにしました。これによりモデルのバージョンがいくつ増えたとしても特徴量の作成の処理が共通しているのであれば、単一の作成コストで特徴量を作ることができます。しかし、共通化したために特徴量のカジュアルな変更は仕組みが複雑になっているため、そのあたりは改善施策に占める特徴量の破壊的な変更の割合に応じて適切な選択は異なるように思います。

Train

モデル作成の実行時パラメータに対する冪等性の確保

我々のモデルは一日の内何度も更新しているので、時には未知の原因によりモデルが異常値を出し始めたりすることがあります。異常値は別のロジックでそのまま結果を反映しないようにしている部分もありますが、何にしても原因を特定する必要があります。その際、実行時のパラメータに対しての冪等性が確保されていないと、再度同じパラメータでモデルを作成してみたら異常は起こらなかった等で原因の解明に時間がかかったりします。他にも正常だった時期のモデルを再現しようとしたら、同様の異常が発生して復旧に時間がかかることもありえます。

今回のパイプラインではその責務を gokart のキャッシュに持ってもらいました。gokart は各タスクのパラメータに依存してキャッシュを持ってくれます。過去の実行のキャッシュが残っていれば、例えば広告ステータスの最新の状態を取得する (最新の状態はその時によって異なる可能性がある) といったコードが ETL の一部等に含まれていて冪等性が失われているような状態でも ETL のキャッシュの結果から取ってこれるので、当時の結果が再現できます。理想的にはすべてのコードがパラメータに対して冪等で、外部の状態に依存しないように作成・管理できていれば問題ないと思います。今回はその品質管理が漏れる可能性を考慮して、キャッシュ無しで再実行したら冪等ではないかもしれないが、キャッシュが残っていれば冪等なモデル等を再現できる設計にしました。

CI

学習パイプラインと推論サーバーで推論に差分が無いか確認

我々は機械学習の学習パイプラインを Python で書いています。それに対して実際の推論を行うサーバーのコードは Rust で書いています。学習時と推論時で同様のコードを使用できる仕組みを作っているところもあるとは思いますが、弊社では比較的自由にモデルを作っている Python のコードと、速度重視でモデルによる推論等を動かす Rust のサーバーのコードで分かれています。これはそれぞれの記述の自由度が高くなる反面、学習時と推論時で何らかの差分が発生する危険があります。

これに対して我々は毎回の更新時に Python での推論結果とサーバーを介しての推論結果に差分が無いかをデプロイのたびに確認する仕組みを作成しています。推論をするサーバー側は指定した広告・ユーザーの推論結果を提供できるようにしておきます。また、学習側では作ったモデルがある広告・ユーザーに対してどのような推論をするかの正解を用意します。この両者を比較して差分が出る場合はデプロイを止めてエラーが出るので、どこかしらに実装の差分が出ているのを検知しています。一度差分の無いシステムを構築してしまえば、差分が出たときは大体の場合、直近の変更に起因していることが多いので、当たりをつけて修正をすることが多いです。

サーバー側の推論結果の提供部分の詳細はすでに記事になっているのでそちらもご覧ください。 広告システムにおける機械学習モデルの推論差分検知について

まとめ

推定モデルでニューラルネット系のモデルを使用するために、学習パイプラインを再構築したため構築をする上で考慮した点を紹介させてもらいました。

今回紹介はできませんでしたが、広告レコメンドでIncrementalトレーニングを実践し、学習コストを大幅に削減した話で紹介した Incremental Training と Batch Training を実運用に載せるための設計変更等も行いました。機会があればそちらも紹介したいと思います *1

明日は takuji さんが Gunosy Advent Calendar 2023 の 11 日目として LLM を使ったクレカの使い道の分析について書いてくれるそうです!どうぞお楽しみに!

*1:設計的に動かせてはいるのですが、もうちょっとブラッシュアップをすべきかもしれない 😢