少し古いがWEB+DB PRESS Vol.129のレコメンドエンジンの記事を読んでいて、勉強のために記事とは異なるデータセットで試したいと思った。
記事ではMovieLensのデータセットで使っているが、アニメレビューのデータセットを使って試してみた。
モチベーション
見たいアニメを探すときに、全体で人気の高い作品よりも、自分に似たユーザが高く評価している作品を探せると、より自分に合った作品を探すことができる。
そのような場合に、レコメンドエンジンを使うと、多数のユーザのレビューの情報から、自分に合った作品を探すことができる。
データセット
アニメレビューのデータセットを探したところ、Kaggleに「Anime Recommendations Database vol.2」というデータセットを見つけた。
Anime Recommendations Database vol.2 | Kaggle
MyAnimeList.netからスクレイピングして作成したデータセットのようだ。
作品ごとのレーティングと、ユーザごとの作品のレーティングが含まれる。
ユーザごとの作品のレーティングには、11,039,694件のデータがある。
アルゴリズム
レコメンドエンジンのアルゴリズムには、古典的には協調フィルタリングがあるが、データ数が多いと次元の呪いの問題に合い計算量が指数的に増加してしまう。
次元圧縮を行う方法に、行列分解により行列を近似する非負値行列因子分解(Non-negative Matrix Factorization; NMF)がある。
行列分解による方法は、2008年のNetflix Prizeで優勝したアルゴリズムで使われてた方法である。
テストデータ
データセットのデータで評価しても面白くないので、自分で選んだ作品を元に、新しい作品を推薦できるか試すことにした。
傾向が似ていそうな以下のアニメを選択し、これらのレーティングを10とした。
anime_id | title |
---|---|
8769 | Ore no Imouto ga Konnani Kawaii Wake ga Nai |
5680 | K-On! Music,Slice of Life,Comedy,School |
1195 | Zero no Tsukaima |
21273 | Gochuumon wa Usagi Desu ka? |
31953 | New Game! |
実装
データ準備
データセットのユーザごとの作品のレーティングを読み込み、テストデータを追加する。
テストデータのユーザIDは使用されていない0とする。
import pandas as pd import numpy as np ratings = pd.read_csv('ratings.csv') myratings = pd.DataFrame([{'user_id': 0, 'anime_id': anime_id, 'rating': 10} for anime_id in [31953, 8769, 5680, 1195, 21273]]) ratings = pd.concat([ratings, myratings])
疎行列作成
from scipy.sparse import csr_matrix matrix_data = csr_matrix((ratings['rating'], (ratings['user_id'], ratings['anime_id'])))
NMFによる行列分解
潜在因子の次元に圧縮して因子行列を得る。
from sklearn.decomposition import NMF latent = 50 nmf = NMF(n_components=latent) # ユーザ因子行列 W = nmf.fit_transform(matrix_data) # アイテム因子行列 H = nmf.components_
処理時間は、3分くらいかかった。
行列復元
因子行列を復元することでレコメンドのためのスコアデータを得る。
WH = np.dot(W, H)
レコメンド
テストデータ(ユーザID=0)の行をソートして、上位10件の作品を取得する。
top10 = np.argsort(WH[0])[::-1][:10] animes[animes['anime_id'].isin(top10)]
結果
rank | anime_id | title | rating |
---|---|---|---|
1 | 66 | Azumanga Daioh | 7.97 |
2 | 355 | Shakugan no Shana | 7.5 |
3 | 356 | Fate/stay night | 7.32 |
4 | 1195 | Zero no Tsukaima | 7.3 |
5 | 1691 | Kaze no Stigma | 7.24 |
6 | 1840 | Zero no Tsukaima: Futatsuki no Kishi | 7.47 |
7 | 1887 | Lucky☆Star | 7.76 |
8 | 2787 | Shakugan no Shana II (Second) | 7.6 |
9 | 2966 | Ookami to Koushinryou | 8.26 |
10 | 2993 | Rosario to Vampire | 6.82 |
テストデータに選んだ作品のシリーズや、ラノベ作品が比較的多く選ばれた。
テストデータ2
別のデータでも試してみた。
anime_id | title |
---|---|
16498 | Shingeki no Kyojin |
6336 | Mobile Suit Gundam Unicorn |
30 | Neon Genesis Evangelion |
1575 | Code Geass: Hangyaku no Lelouch |
14719 | JoJo no Kimyou na Bouken (TV) |
結果
上位10件は、以下のような結果になった。
rank | anime_id | title | rating |
---|---|---|---|
1 | 20 | Naruto | 7.92 |
2 | 30 | Neon Genesis Evangelion | 8.32 |
3 | 31 | Neon Genesis Evangelion: Death & Rebirth | 7.45 |
4 | 32 | Neon Genesis Evangelion: The End of Evangelion | 8.52 |
5 | 33 | Kenpuu Denki Berserk | 8.49 |
6 | 43 | Koukaku Kidoutai | 8.29 |
7 | 47 | Akira | 8.17 |
8 | 199 | Sen to Chihiro no Kamikakushi | 8.81 |
9 | 227 | FLCL | 8.03 |
10 | 269 | Bleach | 7.8 |
今度は、だいぶ作品の傾向が変わり、少年漫画系の作品が多く選ばれている。
単にレーティングが高い作品が選ばれているわけでないことが確認できた。