好きな事で生きていく

統計的機械学習、因果推論、マーケティングサイエンス

pixivの2019春インターンでレコメンドモデルを作りました

概要

2/20から3/8にかけて、Pixiv Spring Boot Campというpixivの春インターン機械学習コースに参加しました。pixiv本体(イラスト交流サイト)のレコメンドモデルを改良するための技術検証を行いました。

選考フロー

書類選考+面接でした。コーディング試験があって、AtCoderのB問題相当*1の問題をホワイトボードに解きました。面接帰りに自分が書いたコードの間違いに気づいて落ち込みましたが、結果的に通ったのは幸いでした。

インターンでやった事

pixiv本体のレコメンドモデルを作りました。
pixivでは

などでベクトル化したイラストの類似度を計算して、イラスト毎に距離が近いイラストを推薦しています。 協調フィルタリングや行列分解は「このイラストをブックマークした人がブックマークしそうなイラストを推薦する」というカテゴリベースの推薦で、CNNを使ったものは「このイラストと見た目が近いイラストを推薦する」という見た目ベースの推薦です。
インターンでは、カテゴリベースの推薦と見た目ベースの推薦に使用する特徴量を全て投入する事で、カテゴリと見た目双方を考慮したハイブリッドで優れたレコメンドモデルを作成する事を目指しました。
Big QueryからAWSのEC2にデータを落として、SQLiteの学習済データと合わせて、Jupyter notebook上でTensorflowのモデルを構築して、モデルから取り出したイラストベクトルを元に社内ツール上でレコメンド結果を定性的に評価する、という流れです。

モデル

最終的に作ったのは「あるユーザーがあるイラストをブックマークするかどうか」を分類するモデルです。このモデルを学習させた後、イラストに対応するベクトル(Dense(20)の出力)を取り出して距離を計算します。

f:id:hanon05:20190309153145p:plain
model

イラスト特徴量とイラストタグは既に計算されたものを使います。イラストはVGG, タグはword2vecが使われたそうです。

工夫点1

ユーザーIDとイラストIDのEmbeddingの内積を計算するNNのモデルは行列分解と式の形が似ています。そのため、行列分解と同じようなタスクを解く事が出来ると考えました。しかも今回作ったNNのモデルは最終的にユーザーIDのベクトルとイラストIDのベクトルの長さが揃えば良いので、イラストIDのEmbedding以外の特徴量を投入する事ができます。今回は時間が無くて試せませんでしたが、ユーザー情報も入れればもっと精度が上がるかもしれません。

工夫点2

作成モデルは分類精度を最大化するのが目的ではなく、イラストに対応するベクトルを取り出すのが目的です(アイテムベースのレコメンドを行いたいから)。なので、ユーザーベクトルとイラストベクトルを合わせる場所はDenseではなく内積を用いて、イラスト特徴量系は内積計算前に投入してDenseでよしなに整えます。イラストに関する情報が内積直前のDense(20)に集約される事を期待しました。
このモデルでは「あるユーザーがあるイラストをブクマするかどうか」の確率が得られるのでパーソナライズされたレコメンドが可能ですが、サービスに組み込む際の実装コストを考慮して、今回はイラスト情報のみによるレコメンドを想定しました。

工夫点3

イラストIDの重み行列は対応するイラストIDが学習データに含まれている部分しか誤差が逆伝搬しないので、イラスト特徴量やイラストタグに比べて学習が遅くなります(多分)。学習の際にバッチサイズが128や512だと、イラスト特徴量ばかり更新されたりその下のDense(20)がイラスト特徴量のみを重要視するように学習されたせいか、valid lossが落ちず直ぐに上がっていきました。しかし、バッチサイズを16000くらいにするとvalid lossがちゃんと下がりました。Embeddingレイヤーの重み更新がミニバッチに対してどのような実装になっているかはっきりとした事は言えませんが、ミニバッチ内で使用されたイラストID毎にそのイラストIDを経由して計算されたLossの平均を逆伝搬していると仮定すると、バッチサイズを上げればバッチ内で使用されるイラストIDの数が増えて、イラストIDの重み行列全体におけるそれなりの割合が同一の逆伝搬計算で更新されるはずです。するとイラスト特徴量の重み行列の更新回数との差分が縮小し、イラストIDの重み行列の学習が進んだ可能性があります*2。 イラストIDのEmbeddingの重み行列を他の特徴量の重みと揃えていい感じに学習させるには、イラストIDのユニーク数などを考慮してバッチサイズをかなり大きめにとると良いかもしれません*3

工夫点4

学習に使ったデータはどのユーザーがどのイラストをブックマークしたか、というデータのみを用いました。そうすると負例が用意出来ず、どんな(ユーザー, イラスト)の組を入れても1を返すモデルが出来てしまいます。そこで、ランダムに選んだユーザーとイラストの組を正例と同じ数用意し、負例を作成しました。ブックマークした(ユーザー, イラスト)の組は負例から弾いた方が良いのですが、計算時間の短縮を優先しました。最初は「ただのノイズを加えてるだけでは?」とモヤモヤしていましたが、「ランダムに選んだ(ユーザー, イラスト)の組はブックマークが観測されている(ユーザー, イラスト)の組よりブックマーク確率が低いだろう」という順位を学習する枠組みがあるらしく、ある程度理論的な裏付けが取れて安心しました。Bayesian Personalized Rankingというらしいです。

結果

レコメンドイラストは載せられませんが、カテゴリベースと見た目ベースの推薦のいいとこ取りをしたモデルが出来ました。
カテゴリベースのレコメンドモデルは推薦元のイラストとカテゴリが近いイラストや人気度(ユーザーがそのイラストを見たときにブックマークをする確率の高さ)の高いイラストをレコメンド出来ます。一方で、コールドスタート問題があったり、同じカテゴリでもあまりにもかけ離れた見た目のイラストを推薦してしまうことがあります。
見た目ベースのレコメンドモデルは、風景など見た目で人気度がわかりやすいイラストはしっかりと推薦出来るのでコールドスタート問題に対応出来ます。一方で、人気度を考慮出来なかったり、似た見た目でも全く異なるジャンルのイラストを推薦する場合があります。
作成モデルは、見た目ベースのレコメンドをベースにしつつ、カテゴリ推薦の成分が混ざることでカテゴリー的に離れすぎずより人気度の高いイラストを推薦するようになりました。カテゴリベースと見た目ベースの良いとこどりが出来たので、どんなタイプのイラストに対してもそれなりに安定して推薦をしていました。めっちゃいい感じになって嬉しかったです。

感想

インターン内容について

他のコースと比べて一週間長い17日間のインターンでしたが、時間的にはギリギリでした。まともな結果が出たのが16日目の午前11時で、インターン期間があと1日短かったら何も結果が出ないまま終わる所でした。最後までやってみないと結果が出るかわからないのは、開発系と比べた機械学習やデータ分析の難しい所です。作業をもっと小さく回してピボットを頻繁に行えばもう少し確実に成果を出せたかもしれません。

機械学習全般やSQLの基礎知識はあったもののレコメンドエンジンを作るのは初めてだったので、良い勉強になりました。またBQやSQLite, TF eagerの使い方の知見も貯まりました。

メンターの方がDeep Learningにとても詳しく、モデルの作成や拡張でとてもお世話になりました。ありがとうございます。

レコメンドモデルは奥が深いですね、、。ユーザーのどのような体験を実現したいかによってどのようなレコメンドをするべきかであったり、どの指標を最大化するべきかが変わってくると感じました。また、アプリ内の場所によって行うべき推薦が変わりそうです。例えば、トップページの推薦はカテゴリベースの推薦を行なっていても、イラスト詳細ページの推薦は見た目が似ているイラストを一緒に推薦した方がユーザーは嬉しいかもしれません。アルゴリズムを高度化するだけでなく、ユーザーの目線に立ってUXまで考えられたら最強だなと思いました。

過去のレコメンド履歴のログは取れていたので、レコメンドされたイラストをブックマークしたかどうかを上手く捉えられればより高精度なモデルが学習出来るかと思ったのですが、あまり精度が上がりませんでした。一つのモデルで高精度な推薦を行うより、ユーザーに推薦するイラストの候補を検索するモデルと、ユーザーがそれらのイラスト群のどれを好むかを順位付けするモデルに分割した方が良さそうです。

その他諸々

オフィスがめっちゃカラフルでした。TeamLabが監修しているそうです。
徘徊するルンバ
趣味人な方がとても多かったです。終業時刻後もどこかしらで何かしらの会が開かれていて、絵描きの会や麻雀の会、Aqoursライブ上映会などをやってました。会社のつよつよPCでゲームする人、ギターを弾く人、セグウェイを乗り回す人など、賑やかな雰囲気が印象的でした。

美味しいご飯を何度かご馳走になりました。特に美味しかったのはタルタルステーキです。冷たい牛肉のタルタルと熱いポテトの組み合わせが最高でした。

f:id:hanon05:20190309175521j:plain
タルタルステーキ、何が何だかわからないけどとにかく美味しい

tabelog.com

事業会社がどのような考えで事業をやっているのかというお話を聞けたのも大きな収穫でした。自社がサービスの提供を通じて価値を生んでいる領域の将来を予測して、予測した将来からどの段階でどのような事業が必要かを逆算して、その状況を実現するために現在のサービス開発や開発をするための最適な人員配置を考えていくお話はとてもワクワクしました。世の中のサービスにはこのような前提を踏まえて提供されているものがあるという発想は普段あまりしてこなかったので、新しい発見でした。GAFAの上層部は一体どこまで先の未来が見えているのでしょうか。

終わりに

とても充実した17日間でした。楽しかったです!
他にもいろんな事業会社回ってみたいなぁ。

*1:個人の感想です

*2:バッチサイズが小さいと1epochの中で過学習が起きていただけの可能性もあります

*3:今回たまたまそれで上手く行っただけかもしれませんが