非負値行列因子分解を用いて、Kaggleコンペを推薦する仕組みを考えました。
手法
いくらでも高度なやり方はあり得ますが、手っ取り早い方法として行列分解を試しました。行列分解は基本的な推薦手法の一つです*1。今回用いる手法を試している記事*2もネットに多く公開されています。
具体的には、次のような手続きを考えました。
- 過去のユーザのコンペ参加記録から「ユーザ×コンペ」の行列を作成(値は参加した場合にのみ1を与える)
- 行列を非負値行列因子分解して、2つの行列を得る
- 2つの行列を掛け合わせた結果を初期の行列と比較し、推薦結果を出力する
図は Non-negative matrix factorization, Wikipedia*3 より引用。
初期の行列(上)と、2つの行列を掛け合わせた結果(下)を以下に示します。行列を一度分解した上で掛け合わせることで、元々は0だった箇所にも0より大きい値が入っていると分かります。今回はこの値を「参加確率」と見なすことで、推薦に利用するといった枠組みになります。
実験
データセット
過去のユーザのコンペ参加記録は、Kaggle公式が公開している「Meta Kaggle」*4から作成しました。いくつかのテーブルを結合・加工し、「ユーザ×コンペ」の行列を作成します。
実装
実装のコードは、次のNotebookで確認できます。非負値行列因子分解はsklearnの実装*5を利用しました。
非負値行列因子分解の該当部分は下記で、とても簡単な記述で済んでいます。
model = NMF(n_components=2, init='random', random_state=0) W = model.fit_transform(X) H = model.components_ R = np.dot(W, H)
デモアプリ
実装内容のデモアプリは、pythonスクリプトから手軽にWebアプリを実装できる「Streamlit」*6を用いました。ソースコードはGitHubで公開済*7です。
サクッと必要最低限のデモを作った。streamlitはこういうときにメッチャ便利。 pic.twitter.com/5gETKUk7q4
— u++ (@upura0) August 28, 2020
今後の展望
コールドスタート
当然のことながら、行列分解の手法では(本来最も推薦を欲しているはずの)コンペ参加が少ない人への推薦が難しくなります。このコールドスタート問題に如何に対処するかは重要な課題です。
デプロイ
本システムをデプロイするに当たって、「ユーザ×コンペ」の行列が非常に大きいことが問題になりました。joblibで圧縮したファイルでも数GBの大きさになります。単純に大きいインスタンスを利用するという手もありますが、実運用に当たっては様々に考慮すべき要素が出そうです。(データベースをどこで持つか、更新をどうするか、どこまでをサービス側で持つか、などなど)
おわりに
本記事では、非負値行列因子分解を用いたKaggleコンペ推薦の方法について述べました。自分の手を動かして推薦システム構築に取り組むと、デプロイも含めて様々な課題に直面して学びが深いと感じています。Kaggleコンペ推薦はドメイン知識もあって取っつきやすいタスクなので、今後もゆるりと取り組んでいきたいところです。