機械学習とシステムデザイン

澁井雄介 氏(以下、澁井):メルカリの澁井と申します。今日は「機械学習とシステムデザイン」というテーマで話させてください。

先ほどマクロミルさんがデータ分析基盤の話をされましたが、私はデータ分析基盤の出口のほうでやっている機械学習とそれによるサービスの改善をやっています。出口のほうでどういった仕組みを作っているのか、メルカリの事例から共有できることをお話したいと思っています。

まずは自己紹介をさせてください。澁井と申します。写真検索とEdge AIのチームを兼務しておりまして、基盤エンジニア兼、MLエンジニア兼、最近アノテーションしているという感じの人です。最近遊んでいるものはAndroidとかARとかで、いろいろとアプリを作っています。

私の写真と飼い猫の写真を物体検出してみるとこんな感じで、猫がちょっと犬っぽかったり、私がちょっとゴリラっぽかったりするらしいです。

「ARとかAndroidとか」と言いましたけど、それで最近遊んでいるものとして、顔認識で人間かゴリラを判定するアプリを作って遊んでいます。

要はAndroidの中でFace Detectionとディープラーニングを実行して人の顔を認識して分析するというので、ジョークで自分の顔を人間かゴリラかで判定してみたというものです。会場に行ったらウケるかどうか見ながら話したかったんですけど、(オンライン開催のため)ぜんぜんわからないという怖い状況ですね(笑)。

司会者:運営は笑ってます!(笑)。

澁井:ありがとうございます。

本題に入ります。メルカリが2017年頃から作ってきた機械学習を使ったいろいろなシステムの中から、データ基盤の連携も含めてデザインパターンが作れるんじゃないかという想いがあって、そういった汎用的に使えそうな構成を共有したいなというのが今日の趣旨になります。

出品商品の配送サイズを正確に選べる人は意外と少ない

まずはメルカリの紹介をさせてください。メルカリをご存知の方がどのくらいで、使ったことがあるという方はどれくらいいらっしゃいますでしょうか? ……はい、オンラインだと挙手がわからないですね。

スマホのアプリとして提供している、誰でも簡単に売り買いが楽しめるC2Cマーケットプレイスがメルカリです。みなさまのお部屋の中にも着なくなった服や使わなくなったスマホ、買ったけれどあまり使わないソファーみたいなものがいろいろあると思うんですけど、そういったものを出品して他の方に売ることができるというのがメルカリ。中古品市場みたいなものです。

プレイヤーとしては出品者と購入者がいまして、出品者は先ほど申し上げたとおりで、購入者は売っているものを探して買うというものです。購入者の立場からすると普通のECアプリに見えるかもしれません。

最後に出品者から実際に配送していただくというのがメルカリで行われるユーザとアクティビティです。お客様みなさまに対して、メルカリを使う上で出品するとか探すというアクティビティをどれだけ簡単にユーザフレンドリーにするのか、我々のチームは機械学習を通してチャレンジしています。

その中の機能の1つとして「配送推定システム」というのがあります。物を売ってそれを配送するという経験は、リテールや小売業をやっていない方はあまり経験がないんじゃないかなと思います。メルカリでは出品者に物を売って配送していただくという仕組みになるので、初めての方には意外とこのへんが使いにくいんじゃないかなというのがあるんです。

その1つが配送で、どういうサイズを配送するのかという課題です。簡単なものとしては、スマホや本であれば小物サイズの封筒+クッションみたいな入れ方になるでしょうし、ソファーみたいな大きさになるとたぶん大型便となると思います。難しいものとしては、靴・ブーツ。靴のサイズはわかると思うのですけど、箱はどういったサイズなのかというのを意識している方はあまりいないんじゃないかなと思うんです。

利用するデータを前処理することで推論速度を向上させる

そういった難しさに対して、このサイズで出品すればよいというのを推定してレコメンドするのが配送推定システムです。モデルとしてはナイーブベイズを使っています。機械学習的には既存の類似商品でこのサイズで配送したものを集めてきてナイーブベイズで推論するということをやっています。実行されるタイミングは出品前の商品情報を入力しているときです。この配送推定システム自体、すごくシンプルな仕組みで、Web API1つです。

出品1回に対して推論1回。「ソファー」というのを入れて説明文を書いたら「たぶん、大型便だよね」であるとか、本を出品しようとしたら「封筒サイズのものがいいんじゃないか」というのを推論として返すという、すごくシンプルなAPIを用意しています。Webサービスで機械学習を使うとき、一番シンプルに始める場合、たぶんREST APIの中にモデルが1個入っている構成が意外と多いと思います。

この構成の利点としては開発も運用もすごく簡単でしたし、スケールアウトや障害箇所の特定も簡単でした。唯一注意点があるとしたら、今回はナイーブベイズなのでそうでもないのですが、例えばXGboostやニューラルネットワークが入ってきたときにレスポンス速度が遅くなって、それをどうカバーするのかというのが課題になったりします。

これだけなら普通のWeb APIなのですが、一工夫凝らしているのが、既存の出品に対する配送サービスを元に推論をしているので、すべての推論に対してデータを取ってくる必要があります。メルカリの既存データはデータウェアハウス、具体的に言うとBigQueryに入っていて数百億行ぐらいになっていて、毎回クエリするのはそもそも無理なサイズなんです。

というわけでREST APIの近くにデータベース……キャッシュでもよかったんですけどデータベースを1つ置いておいて、定期的に前処理したデータをデータベースの中に入れるというパイプラインを書いています。

こういう前処理を含めてやっているので、対象になるデータは基本的にNLPを使って形態素解析であるとかトークナイズをした状態のデータが入っていて、すぐに推論に使える状態にしていくということを、この配送推定システムでやっています。

こう言ってみると機械学習の推論のためにWeb+データベースという、Webではよくある仕組みを作っているんですけど、機械学習を組み合わせるというときにも同じ仕組みが使えるということがこの配送推定で話したかったことです。

これが配送推定システムです。次はもうちょっと複雑な仕組みとしての「違反検知システム」について説明をします。

並列的なマイクロサービスの利点と注意点

メルカリで、数年前に1万円札が出品されてニュースになっていたことがあると思います。「この中で1万円札を売ろうとしたことがある方はどれくらいいますか?」ということを会場だと聞くんですけど、反応がわからないですね(笑)。売っちゃダメです。そもそも違法なんですけど、誰でも物を売ったりできるという仕組みなので、やっぱりちょっと悪いことを考える方がたまにいます。

そういった悪い出品であるとか違反を検知するというのが違反検知システムです。仕組み自体は1違反に対して1モデルを用意しています。1つの2値分類モデルで、例えば1万円札に対して1万円札を検知する仕組みのモデルというのを用意して、検知対象が増えるごとにモデルを用意していくという戦略を取っています。

このモデルが実行されるのは出品したあとです。アーキテクチャとしてはマイクロサービス構成にしておりまして、クライアントがその出品を実行するメルカリアプリだと思っていただければいいんですけど、その出品が発生したあとに、プロキシ経由で各違反モデルに対してリクエストを送るという構成にしています。

1つの出品イベントをトリガーにして複数のモデルを並列に推論させるという仕組みになっています。モデルの数は違反の数に比例して増えていく構成なので、各モデルを他のモデルと依存しないかたちで完全に分離して用意して、プロキシ側でアクセスする数、アクセスする違反のモデルを制御するというかたちにしています。環境変数にアクセス先を1つ増やすだけでアクセスしたり減らしたりということができる仕組みにしています。

利点としてはリソースの調整であるとか更新、モデルによって使うCPUやメモリのサイズがぜんぜん違うので、モデルごとのチューニングも可能になっています。何か1つ壊れたとしても壊れる箇所は違反Aモデルであったり違反Bモデルであったりと局所化されるので、そういった追加・削除などのメンテナンスの容易さというのがあります。

その一方で注意点としては、モデルが増えたらそのままリソースが増えるのでコストが増えるというのと、1つのモデルで推論が失敗した場合にどうやってリトライするのかという戦略が意外と難しいというのがあります。要は、すべてのモデルに対して再度推論を掛けるのか、それともどこかにキャッシュしておくのかという選択があって、その辺も機械学習のモデルをマイクロサービス化して並列にしたときに発生した難しさの1つでした。

出品と違反検知のアクティビティは分離する

もう1つ、この仕組みで工夫しているのが非同期に推論しているという部分です。出品という行為に対して違反検知という行為自体は必ずしも同期的に同時に発生させる必要はないというのがこの仕組みの理由です。出品に対して同期的に違反検知をしているとたくさんのモデルを一気に走らせて、それが返ってくるまで出品が完了しなくなり、ユーザとしては待ち時間が発生してあまり好ましくない体験になってしまいます。

そこをメッセージングやキューを挟むことによって疎結合にしたというのが、こちらの非同期に推論している理由です。

出品を行ったらそのまま「出品できましたよ」と、返す。その後ろでキューに溜まった出品されたものに対して違反か違反じゃないかというのを分類して出力先のデータベースに溜めていくという仕組みにしています。その出力先のデータベースが、また新しいモデルの改善するためのデータのリソースになっていくというのが、この違反検知のライフサイクルです。

利点としては、出品というアクティビティと違反検知というアクティビティを分離して、それぞれにそれぞれのライフサイクルを作ることができるというところです。出品したあとに違反検知で障害が発生したとしてもリトライできるという利点もあります。

ただ、こういった非同期な推論にしてしまうと、複数のデータがあってそれを順番に推論していかないといけないというワークフローがあった場合、もうちょっと複雑なことをやらないと順番を保証できなかったりします。

写真検索は同期的かつ複数モデルを動かして実現

もう少し同期的、かつ複数のモデルを動かしている写真検索の仕組みを話させてください。

写真検索というのは似た画像の商品を探すというのが目的です。メルカリでもAmazonでもそうだと思うんですけど、普通は何か商品を探すとき、まずはカテゴリで探すかキーワードで探すかどちらかから始まると思うんです。

だけど必ずしもその探したい商品の名前を知っているとは限りません。または、探したい商品の名前を知っていたとしても、例えば一言で「ソファー」と探すよりも見た目であるとか、1人用みたいなのがわかっているソファーの写真があるんだったらそこから探したほうが似ているものがすぐに探せるというので、この「写真検索システム」というのを用意しています。

メルカリの中で売られている似た見た目の商品を探すというのが写真検索です。この写真検索の仕組みは入力は画像なんですけど、その画像に対して物体検出と特徴抽出、類似ベクトル検索というものを組み合わせて実現しています。

発生するタイミングとしては物を探すというときもあれば、買いたいものを探すというときもあれば、実は売りたいものを探すというときにも意外と使われているっぽくて、「こういった見た目の商品はどのくらいの価格で売られているんだろう?」と言ったものを探すために使われていることが最近わかりました。写真検索が狙っているのと違った使い方をしていておもしろいなと、この写真検索システムをやってて楽しんでいるところです。

仕組みとしては複数のモデルを連続的に動かすということをやっています。同期的に動かしています。結果を出すまでに複数のモデルを経由しないといけない、複数のロジックを経由しないといけないサービスはいくらでもあると思います。それがたまたま画像で、たまたま検索システムだった場合というのが、この写真検索システムです。

写真から検索するときは、何か撮影するときに被写体だけが写っているとは限りません。家の中で携帯や時計の写真を撮ったときにペンが写ったりとか家族の顔が写ったりとかってありえると思うんです。そうしたときのために物体検出をやって被写体にフォーカスして、その被写体に対して特徴を抽出、ベクトル化して、そのベクトルに対して既存の写真で似たベクトルのものを検索してお客様に返すということをやっています。

複数のモデルを並べて動かしているので複雑な処理もできるようになっているんですけど、その一方で前のモデルに依存して後ろのモデルが決まるという難しさがあります。とくに特徴抽出と類似検索の間でベクトル化するのが特徴抽出に依存してしまっているので、そこを変えるのが難しいというのが、この写真検索の難しさです。

継続的なインデックス追加は学習パイプラインの安定性が重要

この仕組み自体が、データ基盤や学習基盤とのオンラインの連携が必要な仕組みになっています。どういったことかと言うと、新しい商品が出品されたらその商品画像も検索対象にしなければいけないというのが写真検索システムの難しさの1つです。仕組みとして検索対象の写真というのはベクトル化されてないといけないというのが、検索上、必要な条件になります。

そうしたときに画像のベクトルというのを常時追加し続けるというのが必要で、出品画像と出品データのパイプラインから機械学習の学習パイプラインにつなぎ、さらに特徴抽出と検索のためのインデックス化をパイプラインでつなげる仕組みとしています。

データを溜めてそこからの出口に、また新しい機械学習のパイプラインが動いているというのが、この写真検索の全体像です。新しい検索対象のインデックスについては毎時間とか毎日、新しいマイクロサービスとして立ち上げます。その立ち上げたインデックスを検索対象にするかどうかは、サービスディスカバリという仕組みでカバーしています。

新しいインデックスが追加されたらそれをアクセス先のリストとして持ってきてリクエストを投げると。いらなくなったものを消していくことによって自動的に古い写真は検索対象にしないという仕組みにしています。出品というデータパイプラインの安定性もそうなんですけど、図中の学習のパイプライン、特徴抽出、インデックス化の右上の部分ですね。

ここの安定性が肝になる仕組みでして、ここが止まると新しい画像の検索もできなくなってしまうので、そういった難しさがあるのが写真検索システムです。そこのための監視やリトライの仕組みを用意して動かしています。

トライ・アンド・エラーを繰り返すことが必要

以上、2018年ぐらいからメルカリの中でいろんなMLのシステムを作ってきて、トライ・アンド・エラーばかりだったんですけど、なんとかいろいろと共有できる知見が溜まってきたなというのが、今日話したかったものです。

MLの仕組みとデータの仕組みとの連携というのがすごく重要なんですが、データが変化すればそのモデルを更新することが必要になるんですけど、それに対応するもののために中間にデータベースを置いたり、サービスディスカバリーでなんとかしたりということをやっています。

それも最終的にはスピードであるとかユーザに何をどう見せるのか、ビジネスロジックやUI/UXとの整合性のためにトライ・アンド・エラーと全体に関するパイプラインを作っていくというのが必要になってきているというのが、今までいろいろと配送推定だったり違反検知だったり写真検索を作ってきて学んできたことになります。

本日の私からの話は以上で、最後になりますが今回コロナウイルスに負けずに諦めずにイベントを開催していただいた運営のみなさまに称賛と感謝を申し上げたいと思います。あとはTECH PLAYのサイトにある登壇者紹介に、これは私の確認なくいきなり掲載されていたんですけど、その大胆さにも称賛を申し上げたいと思います。

ありがとうございました。