2023-07-06 ML勉強会
協調フィルタリングとベクトル検索エンジンを利用した商品推薦精度改善の試み - メルカリ エンジニアリングブログ
サマリ
- ベクトル検索ベースの商品推薦アルゴリズムで推薦精度の大幅な改善を実現
- 協調フィルタリングとニューラルネットワーク (以下、NN) を利用した商品推薦アルゴリズムを構築し、コールドスタート問題を回避しつつ、ユーザーの閲覧履歴を活用
- 学習にはPython implicitライブラリを活用
- GPUを利用して膨大な行動ログの計算を高速化
- NNのモデリングではKaggleコンペティションのsolutionなども一部参考にしつつ、極めて軽量なモデルを作成
- モデリングではアクセスログを活用したオフライン評価を行うことで、改善が常に正しい方向へ向かうように工夫しました。
- ベクトル検索エンジンにはVertexAI Matching Engineを採用して、少ない工数でベクトル検索を実現しました。
- VertexAI Matching Engineは本番運用の高負荷にも十分耐えうるものであり、テスト実行後、迅速に本番適用へ移行することが可能でした。
- 実際のABテストでモデリング時に見逃していた重要な特徴量も発見することができました。初回のテストの失敗後、それを迅速に修正し、実ビジネスに貢献する強力な推薦モデルの構築に成功しました。
該当の推薦機能
よく出てくるやつ
ベクトル検索エンジンを利用した商品推薦
- Indexing
- NN(※あとで解説)で商品のベクトルを計算する (i, ii, iii)
- 商品のベクトルを以下2つのGCPサービスに格納する (iv)
- Bigtable [4]: 全ての商品のベクトルを保存する
- Matching Engine: 販売中の商品のベクトルを保存する
- Recommendation
- ユーザーが商品を閲覧した際 (1) に、以下の流れで推薦を行う。
- Bigtable から閲覧中の商品のベクトルを取得する (2, 3)
- Matching Engine を用いて、そのベクトルと似たベクトルを持つ、販売中の商品を近似近傍探索する。(4, 5)
- Matching Engine の検索結果を「この商品を見ている人におすすめ」に表示する (6)
- サービス解説
- Cloud Composer: Apache Airflow
- Cloud Bigtable: NoSQL DB
- Matching Engine: Vertex AIのベクトル類似度検索エンジン
- Bigtableが必要な理由
- 当初、Matching Engine はベクトルをクエリとして受け付けて、それに対して類似商品のIDを返す、という動作しかできなかったため、Bigtableを必要とする構成になっています。
- 現在は Matching Engine のアップデートにより、(ベクトルの代わりに) 商品IDを投げると類似商品を返してくれるようになったため、Bigtableを不要にすることも可能です。
- Matching Engineのインデックス作成にはStreaming Update [5]というものを採用
- 新たに出品された商品のインデックスへの追加や、売り切れてしまった商品のインデックスからの削除を瞬時にインデックスへ反映することができます。
- ものすごい勢いで商品の在庫が入れ替わっていくメルカリでは非常に便利な機能でした。
ABテスト
最初は「おもちゃ」カテゴリを対象にしてABテスト実施(全アイテムは数が多すぎるため)
おもちゃは流行り廃りが激しくて現行の推薦が機能していなかったので改善した時にわかりやすい
ベクトルをどうやって計算するか
- 検証
- word2vec(一般配布の学習済みモデル): オンライン評価で伸び悩む
- 商品数が多くなると細やかな違いを区別できなくなる(表現力が弱い)
- word2vec(自社データで学習): 思ったより精度が伸びない
- 試行錯誤の結果、古典的な協調フィルタリングを利用
- 協調フィルタリング
- 概要
- ユーザの検索履歴や購買情報等から好みが似ているユーザを探し出し、そのユーザたちが購入した商品をオススメに表示するといった手法
- 共起とかジャッカード指数とかが使われたりする
- 共起:
- 商品Xを買った顧客が他の商品を何個買ったかで計算します。ジャッカード指数より精度が低いですが、簡単に計算できます。たとえば商品Xと商品Aの共起値を計算すると顧客Eのみ両商品を購入しているので共起値『1』となります。特徴は計算時間がちょっぱやなこと。
- ジャッジカード指数
- 今回はimplicitライブラリを利用して商品のfactorを計算
- ‣
implictとは
以下は、factor関数の基本的な使い方の例です。
この例では、Movielensデータセットを用いて、トレーニングデータとテストデータに分割し、ALSアルゴリズムを用いた行列分解による推薦モデルを構築しています。また、評価指標としてPrecision@10とRecall@10を用いて、テストデータに対する評価を行っています。最後に、factorize関数を用いて、ユーザー行列とアイテム行列を取得しています。
- implicit ライブラリのpros, cons
- pros
- GPUを使って計算を高速化できるため、数億行のデータを突っ込んでも現実的な時間で計算を完了してくれます
- 差分更新にも対応しており、商品の閲覧履歴が溜まるとより精緻なベクトルに更新していくことが可能です
- cons
- implicit ライブラリのログの取り回しが非常に煩雑
- ライブラリの制約から0始まりのidでデータを扱う必要があり、implicit id と商品idの変換テーブルが必要 (データパイプラインが複雑になって辛い)
- コールドスタート問題
- フリマアプリというサービスの特性上、新しいものに閲覧が集中する。新しい商品では「この商品を見ている人におすすめ」があまり機能しない、というのはユーザー体験の毀損につながってしまう。
- (ただこれは協調フィルタリングという手法そのものの問題なのでimplicit単体の問題ではない)
- NNで協調フィルタリングのベクトル (factor) を再現させちゃおう作戦(ほえー)
- 十分な商品閲覧数をもつ商品に対して協調フィルタリングでベクトル (factor) を計算する
- タイトル、商品説明文などの商品情報を利用してそのベクトルを再現するNNモデルを学習する
- textのembedはw2vで取得
- おもちゃカテゴリの全ての商品に対してNNモデルでベクトルを計算し、それを商品のベクトルとして利用する。
- (数千万商品を処理する必要があるため、初回のテストではBERTなどの重いモデルは利用しませんでした)
SWEM: ‣ Simple Word-Embedding-based Models
KimCNN
SWEMとKimCNNの違い
- テキスト処理において商品タイトルにカテゴリー情報の文字列を足すと言った点は、Kaggleのメルカリコンペ[12] の解法を参照しました。
- 紆余曲折あって協調フィルタリングのfactorをNNで近似するという結構無理やりな問題設定になってしまったので、別の機会にtwo-tower モデルなどのより効果的と思われるモデルのテストを実施したいと思っています。
two-tower モデル
ABテストによる改善
- 一回目は失敗したが、原因を調べたら「出品から長い時間売れ残って放置されている商品をたくさん推薦してしまっていた」
- メルカリユーザーからするとあるあるだが、基本的に即売れないものはずっと置いておいても売れない。これは鉄則
- 商品の新鮮さを考慮するように推薦ロジックを修正した結果、推薦された商品の購買率が一気に向上
GCPまわりのつらみ
- Matching Engineのドキュメントにはまだまだ不足している点が多かったです。(SDKの利用方法、Public Endpointの構成方法など。)
- Google Cloudのサポートチームがチケットで質問に気軽に対応してくれた、とのこと
- (Google的にはメルカリさんはサポート注力対象なのかなぁ)
- Tokyo Region の GPU リソースが不足しているためか、GKEのノード自動プロビジョニング(NAP) [13] で全然GPUを掴めないタイミングが稀によくあった。
- (GPUリソース不足はあるあるだがTokyoRegion限定だと余計に辛そう)
- 結局、NAPを諦めてインスタンスを1個立ててGPUを常に確保した。(画像生成AIの隆盛の影響だったりするのでしょうか…。)
結果
- 商品推薦タップ率が3倍に
感想
- ABテストでシュッと改善している動きがすごい
- レコメンド系って「答え」があるわけではないからABテストで検証が一般的なのかな??(教えて推薦マスター)
- ベクトルの計算をNNにやらせているのが面白かった