検索組織の機械学習実行基盤

検索組織の機械学習実行基盤

こんにちは、APプロダクト開発G Qassチームの内田です。
本稿では、リクルートテクノロジーズの検索チームの取り組みについて紹介します。

Qassチームの取り組み

Qassチームは、リクルートテクノロジーズの検索特化組織です。リクルートグループの提供する様々なサービスに対して、 検索基盤の運用・検索ロジックの改善を行っています。詳しくはこちらの記事をご覧ください。2015年の記事なのでプロダクトに関しては現状と変わっているところもありますが、組織の役割については当時と大きく変わっていません。

今、Qassチームでは今までの検索基盤運用・検索ロジックの改善に加え、新たに機械学習実行基盤の構築を行っています。今回は、この基盤がどのような経緯で構築されることになったのか、またどのような構成で構築を行ったかを紹介します。

機械学習実行基盤

簡単にまとめると

  • 取り組んだ課題:サービスごとに開発環境の違いやオペレーションの属人化が発生していたため、あるサービスで開発された検索ロジックを他のサービスに展開するのに余計な工数が発生していた
  • 解決策:コンテナ技術を使い、ロジックの移植性を向上させた / ワークフローエンジンを採用し簡単に処理を実行できるようにした
  • 結果:サービスを横断した資産の活用が活発になった

なぜ基盤が必要になったのか

Qassチームはグループ各社の多数のサービスと関わりがあるため、サービスごとに担当者を分け、各々検索改善に従事する体制をとっています。それぞれのサービスの検索機能は磨き込まれていったのですが、開発者間の連携が薄くなり、チームとしては以下のような問題が発生していました。

  • 他の開発者の書いたプログラムが自分の環境でなかなか動かない、挙動が微妙に違う
  • 複数のサービスで同じようなロジックが別々に作られて走っている

機能軸組織として事業会社を跨いで検索機能を管轄しているのにも関わらず、うまく機能の横展開ができずサービスごとに車輪の再開発が行われているケースが散見されました。

せっかく検索組織としてチームを組んでいるのだから、他のサービスで既に作られたものは再利用し、新たな機能開発に注力して開発効率を向上させよう、というのが基盤構築に取り組んだきっかけです。過程として開発プロセスの見直しや新たなツールの導入をし、作ったロジックが誰でも実行できる環境を整えました。

Dockerによる実行環境の共有、機能のコンポーネント化

まず取り組んだのが、Dockerを利用して実行環境をコード化し、イメージをチーム内で共有することでした。「他の開発者が動かせない」という状況にならないことが、機能を再利用する上でまず必要だと思ったためです。作ったイメージはGoogle Container Registry (GCR)にpushし、チームの誰でも参照できるようにしました。動かす際はそこからイメージをpullして実行します。これで微妙な環境の違いにより挙動が違う、そもそも動かないという問題が起こらないようにしました。

また、プロダクトの再利用性を高めるための試みとして、各機能をそれぞれ独立のコンポーネントとして切り出すことにしました。それぞれのコンポーネントはそれぞれのコンテナで動作し、ストレージを介してデータを受け渡します。これによりサービスごとに同じ機能が極力再実装されないようにしました。例えば、「データの前処理」「機械学習ロジック」と切り出した場合、別のサービスに適用するに当たってはデータの前処理コンポーネントのみ作ればよく、機械学習ロジックは同一のコンポーネントを利用できます。

データ処理ワークフローのコード化

各機能をコンポーネントとして切り出すと、それらを組み合わせるためにワークフローエンジンが欲しくなりました。

色々なワークフローエンジンが候補に挙がりとても迷ったのですが、

  • Dockerのイメージを指定し、そのコンテナで処理を実行できる
  • 読み書きが容易なコードでワークフローを記述できる
  • 定期的な実行ができる
  • 並列実行ができる

などの機能があり、隣のチームで運用されていた、Digdagを採用することにしました。DigdagではYAMLに似た記法で簡潔にフローを記述でき、各ジョブごとに実行するDockerイメージを指定することができます。

ただ、使ってみて色々ハマったところもあったので簡単にご紹介します。

  • Digdagもコンテナで動かしジョブもコンテナで動かすとジョブ実行時にエラーが起きる → Digdag側のコンテナを立ち上げる際にホストの/tmpをマウントする
  • ジョブのコンテナのENTRYPOINTやWORKDIRが上書きされる → コマンド実行前にcd workdirする(シュッとした解決策があったら教えてください
  • ドキュメントが充実しておらず一部載っていない機能がある → Examples を見てみる
  • py>オペレータを利用する場合、Pythonの型アノテーションやkeyword-only引数が使われているとエラーになる → sh>オペレータで実行する

GCPの活用

DockerイメージをGCRで共有するようになったほか、もともとGoogle BigQueryに様々なデータを集約していたこともあり、ジョブを動かす環境はGoogle Cloud Platform (GCP)に構築することになりました。

CI/CDツールとしてDroneを採用し、Google Kubernetes Engine (GKE)に構築しました。Droneは、弊社オンプレサーバ上のGitLabにpushされたコードから、Dockerのイメージをbuildしたり、 Digdagへワークフローを登録します。パイプライン処理をYAMLで記述することができ、それぞれのジョブの実行は指定したDockerコンテナで行われます。

同じくGKE上に先述のDigdagも構築しました。Digdagは1クリックでワークフローを実行できる他、スケジュール実行で日次の処理を行うことも可能です。簡単なジョブならば、Digdagが動いているマシンの上で走らせてしまえばいいのですが、計算資源が必要な大きめのジョブに関しては別途クラスタを立ちあげるようにしました。

全体図を以下に示します。最終的な演算結果はGoogle Cloud Storageに保存し、そこから我々が運用している検索基盤に取り込まれます。

  • Dockerによるコンポーネント化
  • DroneによるDockerイメージのビルド、ワークフローの登録
  • Digdagによるワークフローのコード化
  • BigQueryにデータを集約
  • GCS, BigQueryに出力

おわりに

本稿では、我々の直面した課題とそれらに対するアプローチについて紹介しました。現在では、あるサービスのために開発した機能を他のサービスに適用するなど、予定通り機能の横展開が進んでいます。また、インターンシップに来てくれた学生さんにもこの仕組みの上で運用できるようにロジックを開発してもらっています。

取り組みの結果、このようになりました。

  • あるサービスで検索精度を向上させたロジックを容易に他のサービスに横展開できるようになり、開発リードタイムが短縮された
  • 専門的なスキルを持った機械学習エンジニアが開発したロジックを、チームの誰でも動かせるようになり、資産を組み合わせてソリューションを作れるようになった
  • 開発したロジックをリポジトリにPushするだけで、自動でデプロイが行われ、1クリックでの処理の実行や定期実行ができるようになった

今回紹介した内容はまだ始まったばかりの取り組みで試行錯誤の段階なので、次にご紹介するときにはもしかしたらまた違うものになっているかもしれません。実際に運用していく中で使いにくい点や欲しい機能がどんどん出てくると思うので、継続的に磨き込みを行いたいと思っています。