大規模なユーザーとアイテムに対して逐次学習アルゴリズムを利用

atsumu氏(以下、atsumu):本日は「行列分解アルゴリズムの逐次学習化」について発表します。

自己紹介です。iOSアプリ、インフラ、pixiv開発、セキュリティなどを経て、現在はレコメンド改善に取り組んでいるatsumuと申します。

本日は、行列分解によるレコメンドについて簡単に紹介します。行列分解によるレコメンドでは、評価値行列をPとQの2つの行列に分解します。評価値行列とは、例えばブックマーク、フォロー、購入情報を表す疎行列です。2つの行列の積ができるだけRに近付くように学習すると、ユーザーとアイテムの特徴量が手に入ります。

この特徴ベクトル同士の内積や類似度を計算することで、関連度がわかります。

学習はこのように行われます。まずP、Qを乱数で初期化します。その後、どのユーザーがどのアイテムにどのような評価を行ったかという入力を受け取り、実評価と予測値の誤差を計算し、その誤差をもとにP、Qを更新します。これを複数回繰り返します。

pixivの場合、この手法には不都合な点があります。数千万のユーザー、アイテムを扱いつつ、さらにユーザー、アイテムを頻繁に追加したいのですが、この手法では追加の度にすべてを学習し直す必要があります。そこで逐次学習アルゴリズムを利用します。

逐次学習アルゴリズムとして、この論文を参考にしました。主な変更点は入力を受け取りながらP、Qを必要に応じて初期化する部分です。これにより新たなユーザーやアイテムの出現に対応できます。またこの論文の手法では、学習を1イテレーションのみ行っています。

反復学習とBPRを変更

論文どおりの実装を試したところ、うまく学習が進みませんでした。原因は恐らく、問題設定の違いにあります。元の論文では多数のユーザーかつ少数のアイテムを対象としていますが、今回は多数のアイテムに対応するため、いくつか変更を行いました。1つは反復学習、もう1つはBPRです。この2つについて説明します。

まずは反復学習です。学習が進まない原因は、多数のアイテムを対象としているため、1イテレーションでは十分に収束していないことだと予想しました。そこで、Batch SGDと同様に学習を複数イテレーション行うように変更しました。入力を1日単位のチャンクに分割し、チャンクごとに収束するまで反復学習を行いました。

この手法の効果を確認するために、評価値行列と予測値の誤差を計測しました。すると、イテレーションを繰り返した時、評価済みのユーザーとアイテムの組み合わせでは誤差が小さくなっていることがわかりました。一方で、未評価の組み合わせでは大部分を「興味あり」と予測していました。つまり、単純にすべてを「興味あり」と学習してしまっていました。学習が適切であれば未評価の大部分を「興味なし」と予測するはずなので、これでは問題がありました。

反復学習における課題の対策として、未評価の組み合わせをランダム抽出し、「興味なし」とみなして入力しました。ただし実装上、生成した組み合わせが評価済みかどうかは判別していません。狙いは高速化です。大部分が未評価かつ反復学習を行うため、悪影響は出ないと予想しています。同時にランダムを抽出する時に、出現頻度が入力値とほぼ同数になるように確率調整を行っています。評価数に幅があるので、調整しなければ学習に影響があると予想しています。

似た手法としてBPR(Bayesian Personalized Ranking)があります。BPRとは、ユーザーごとに、未評価より評価済みアイテムによる評価値が大きくなるように学習する手法です。しかし今回はBPRを利用しませんでした。今回の手法のほうが実装やチューニングの工数面で有利であり、十分にうまく動いていたことが理由です。

また、チャンク内の反復学習のみでは同一チャンク内には現れないユーザーとアイテム間の距離計算が不正確になると考え、チャンクを跨いだ反復学習も行っています。実装としては過去の入力チャンクを再利用して、再学習を行っています。

この時、新しい入力値ほど高確率で再利用しています。これは新しい入力値が、特徴量の変化しやすい新規のユーザーやアイテムを多く含み、また、再訪可能性の高いユーザー、閲覧頻度の高いアイテムを多く含むためです。計算コスト削減のため、イテレーション数は抑えています。

ここまでの処理を疑似コードで表現するとこのようになります。入力チャンクを複数選択して、チャンクごとに複数回学習イテレーションを実行します。各イテレーション内では、チャンク内の入力を用いて初期化とBPRによる学習を行います。これを毎日実行しています。

今回の手法はNMF(Non-negative Matrix Factorization)にも応用できます。NMFでは、分解後の行列P、Qに負の値が現れないように学習を行います。正の値のみで元行列を近似する結果、トピックが特徴量に現れるためトピック分析として利用できます。今回の手法に対して、いくつかの変更を加えることでNMFの逐次学習が可能です。まず乱数による初期化時に正の値に絞ります。そして学習中、特徴量が負になったら0に置き換えます。

まとめです。行列分解の逐次学習化とpixivへの適用について紹介しました。初期化タイミングの変更による逐次学習化、そして課題解決のための入力のチャンク化と反復学習、偽の負例の利用、チャンクを跨いだ反復学習、最後にNMFへの応用について紹介しました。発表は以上です。ご清聴ありがとうございました。

司会者:「逐次学習の計算コスト的が有利なのは大きいと思うんですが、再現性など、MLOps的な点でのつらさなどはありませんか?」という質問です。いかがでしょう。

atsumu:開発は過去のデータを用いているので、再現性については特に困りません。実際に動かしている時に、想定と違う内容の学習結果になってしまうというのは、今後起こる可能性があると考えています。

けっこう長期間の過去分で学習していて、たぶんそこまで大きな問題にはならないんじゃないかと予想はしているのですが、今後の課題になるかもしれません。

司会者:ありがとうございます。「実装の話が気になる」というコメントがいくつかありました。例えば「インフラのどこで学習しているのか」という質問がありましたが、いかがでしょうか。

atsumu:学習場所に関してはpixivの場合だと旧社屋にサーバーがいくつか置いてあって、そのうちの何台かを機械学習用に確保してもらって、そこで開発を行っています。

これはまだ動かし始めたばかりなので、本番のサーバー上にしっかりと乗せ換えてはいなくて、開発と本番用に動かすのはもう1台のほうのサーバーで行っています。CPU、16コアぐらいで、メモリが100GBちょっとのサーバーで動かしています。

司会者:ありがとうございます。今回のNMFで、BPRの話もチラッと出てきましたが、それに比べて精度はどうだったんでしょうか?

atsumu:最終的な特徴量を近傍探索した結果、目視ではそこまで変わらないかなと思っています。実際に本番で比較するのはまだそこまでしっかりできていない状況です。1つわかっていることは、BPRのほうがより短いイテレーション数で予測するということです。

司会者:ありがとうございます。