CtoCで協調フィルタリングした記録〜訓練データ作り・評価指標・実装〜

以前、協調フィルタリングをお仕事で使う機会があったので、その時の記録を書きます。

訓練データづくりで注意したこと

「user-item行列においてuser数 が item数に対して十分に多くなるようにする」ということ*1に注意しました。

f:id:sakatoken:20190627230134p:plain
左側のような形状の行列の方がユーザ選好を発見しやすい

userとかitemとか言っておりますが、これらの単語は次のような意味で使っています。

  • item:購入履歴や星5段階ratingなど、評価を下されるところの商品などを指すものです。
  • user:itemに対して評価を下すところの主体に相当するユーザを指すものです。
  • user-item行列:この行列の i ,j成分は、user_i が item_j に下した評価を表します。

例えば「10人のユーザいて、10種類の商品をratingした。ただし1人につきratingは1件しか下さず、なおかつ全てのユーザが相互に異なる商品に対してratingをつけた。」のようなuser-item行列を想定してみます。協調フィルタリングではuser-item行列に対して種々のmatrix-factorizationを用いますが、このように「user同士がitemへの選好を分かち合っていない」ような行列からは、潜在的なuserベクトルとitemベクトルを発見することはできません。

より具体的には、訓練データを準備する際には以下の要件を満たしたい。

  • user数が十分にitem数よりも多い
  • userは十分に多くのitemに評価を下している

特に商品在庫が「一点もの」になりやすいCtoCのwebサービスでは、この問題が起こりやすいと思います。(私はそうなりました)

この問題を解決するために、私が行なった対策は以下です。

  1. itemの単位を「商品id」ではなく「商品細分類」に変える。つまり特徴(テキストや画像など)に基づくクラスタリングを事前に行うということ。
  2. user1人あたりの評価件数に閾値を設けて、ほんの少ししか評価していない人は訓練データから除外する。(閾値の適切さはケースバイケースだと考えています)

私の事例では、協調フィルタリングの訓練段階よりも、むしろ上記の商品細分類を発見する作業の方により多くの労力を使いました。こういう風にitemの本質を量的に表現することはレコメンド以外のタスクにも活きると思うので、おそらく色々な局面で「非構造データからitem特徴を入手する」ようなアプローチが重要なんだと思います。

下記のwantedlyrei kubonaga さんの事例(すごくイケてる!)では、DNNを使って「userとitemの相性の良さを予測する」というアプローチを取られていますが、これも特徴抽出ありきの話ですね。

レコメンデーションのターゲットメトリックスの設定 / ML loft 3 - Speaker Deck

性能評価で注意したこと

オフライン評価(サービスにリリースしたときのビジネス成果による評価ではなく、開発時のテストスコア)におけるレコメンドエンジンの評価方法について、Collaborative Filtering for Implicit Feedback Datasets(2008)という論文が大変示唆に富んでいると思います。(少なくとも私にとっては。もしかしたら2008年よりずっと前から指摘され続けてる話かもしれませんが...)

これを参考に以下の2点に注意しました。

1. テストデータの用意の仕方を実際の問題設定に近づける

この論文では「4週間分のテレビ視聴ログからユーザの選好を学習して、推薦によって次の1週間の視聴内容を言い当てる」という実験を行なっています。(下記は該当箇所の引用)

we use a similarly constructed test set, which is based on all channel tune events during the single week following a 4-week training period. Our system is trained using the recent 4 weeks of data in order to generate pre-dictions of what users will watch in the ensuing week.

単にデータの一部を(色々と条件づけた上で)ホールドアウトすることでテストデータとすることも出来るわけですが、テレビ番組の視聴データで言えば「データ収集期間の途中で新番組が始まった」のようなケースを考えると、未来のデータを使って起こりえない過去を予測するような学習が起こり得ます。テストデータにはあくまで未来のデータを用いた方が、より本番に近い状態で実験が出来ると思います。

2. precisionあまり気にしない(問題設定によるけど)

この実験では「推薦の順位と、実際のユーザ選好の順位の一致度」のようなものを評価指標とするわけですが、この評価指標に関して次のように言及しています。

precision based metrics are not very appropriate, as they require knowing which programs are undesired to a user. However, watching a program is an indication of liking it, making recall-oriented measures applicable.

「テレビ視聴データではユーザが何を好きか(いわば陽性)は分かるが、何を嫌いか(陰性)は分からない。よってprecisionよりもrecall志向で評価する方が適切。」という旨のことを言っています。

例えばこの論文では「あるテレビ番組を視聴していないということは、単に『好きな番組の裏番組だから見れなかった』だけかもしれないし、見ていないというだけで積極的に『嫌い』の烙印を押すのは誤りだ」ということに言及していて、これは言い換えると「本当はレコメンドが正しいのに、ユーザが視聴ログを残してくれなかった」というケースがありえることを意味します。上記で触れたrecall志向とは「推薦されたitemが正しかろうと、実際のuserの視聴ログは歯抜けになっちゃうのだから、推薦上位にやってくるitemが十分なrecallを持つのかどうかに着目しましょう」ということです。

前述のテストセットの用意の仕方と同じく、レコメンドエンジンの使途目的に応じて評価指標を選ぶと、納得のいく実験をデザインできますね。
その上で「具体的に評価指標にどんな尺度を使うのか?」についてはbrainpadさんの↓のブログが大変参考になります。

blog.brainpad.co.jp

実装時に使ったライブラリ・参考にした記事など

私が扱ったデータは「クリック数をuserによるitem選好として解釈する」ようなものでした。これを協調フィルタリングの界隈ではimplicitなデータと呼ぶようです

そこで既に本記事でたびたび引用しているCollaborative Filtering for Implicit Feedback Datasets(2008)で紹介されている手法を扱うことのできる"implicit"というそのものズバリな名前のpythonライブラリを使うことにしました。なおこのライブラリを使う際には、元の論文で登場するαパラメータは手動でチューニングする必要がある点に注意です。

github.com

さらにimplicitを使ったサンプル実装としてこちらの記事が大変参考になりました。この記事が素晴らしいのは、グリッドサーチの実装を公開してくれている点です(下記)。αパラメータ(下記実装ではconfidenceという変数) のチューニングも含めた内容となっています。

Recommending GitHub repositories with Google Big Query and implicit library: https://medium.com/@jbochi/recommending-github-repositories-with-google-bigquery-and-the-implicit-library-e6cce666c77 · GitHub
※implicitのAPI仕様書でcsr_matrixが指定されている箇所で、何故かcoo_matrixを使っている点だけひっかかります。

仕上げにテストスコアを「単純ランキング」と比較する

前節でのグリッドサーチを行うことでベストなパラメータは求まりますが、これはあくまで協調フィルタリングという枠の中における相対的なベストに過ぎません。つまり、ここで言うベストとは推薦内容が受け取り手のユーザにとって優れていることを意味しません。よって、推薦手法の対立候補として「単純な人気ランキングに基づく推薦」などを用意し、テストスコアを比較することで「パーソナライズに意義がある!」という主張のウラを取る必要があります。

(さらに、オフライン評価での成功はただちにKPIへの寄与を意味しないので、この後にはオンライン評価が控えています。)

*1:これはquoraの投稿:https://www.quora.com/How-do-I-speed-up-matrix-factorization-by-sampling-users-without-losing-precision を参考にしました。元ネタの文献が何なのかがわからず。。。