推薦システムにおける行列分解の話
概要
この記事では、推薦システムにおける行列分解の手法について解説する。
行列分解の定義や性質、応用例を具体的な数式とPythonのコードを用いて示す。
また、行列分解のメリットとデメリットについても論じ、具体的な利用例として「movielens-100k」データセットを用いた実装例を紹介する。
ソースコード
github
- jupyter notebook形式のファイルはこちら
google colaboratory
- google colaboratory で実行する場合はこちら
実行環境
OSはmacOSである。LinuxやUnixのコマンドとはオプションが異なりますので注意していただきたい。
!sw_vers
ProductName: macOS
ProductVersion: 13.5.1
BuildVersion: 22G90
!python -V
Python 3.9.17
基本的なライブラリをインポートし watermark を利用してそのバージョンを確認しておきます。 ついでに乱数のseedの設定をします。
import random
import pandas as pd
import numpy as np
seed = 123
random_state = 123
random.seed(seed)
np.random.seed(seed)
from watermark import watermark
print(watermark(python=True, watermark=True, iversions=True, globals_=globals()))
Python implementation: CPython
Python version : 3.9.17
IPython version : 8.17.2
numpy : 1.25.2
pandas: 2.0.3
Watermark: 2.4.3
行列分解の定義と性質
行列分解(Matrix Factorization)は、与えられた行列を二つの低ランク行列に分解する手法である。推薦システムでは、ユーザーとアイテムの行列を分解することで、潜在因子を抽出し、推薦を行う。
数式表現
ユーザー $u$ とアイテム $i$ の評価行列 $\mathbf{R}$ を次のように分解する。
$$ \mathbf{R} \approx \mathbf{P} \mathbf{Q}^T $$
ここで、$\mathbf{P}$ はユーザー行列、$\mathbf{Q}$ はアイテム行列である。各行列の次元は以下の通りである。
$$ \mathbf{P} \in \mathbb{R}^{m \times k}, \quad \mathbf{Q} \in \mathbb{R}^{n \times k} $$
ここで、$m$ はユーザー数、$n$ はアイテム数、$k$ は潜在因子の次元である。目標は、評価行列 $\mathbf{R}$ と予測行列 $\mathbf{P} \mathbf{Q}^T$ との差の二乗和を最小化することである。
最適化問題
最適化問題は以下のように定式化される。
$$ \min_{\mathbf{P}, \mathbf{Q}} \sum_{(u,i) \in \mathcal{K}} \left( r_{ui} - \mathbf{p}_u \cdot \mathbf{q}_i^T \right)^2 + \lambda \left( |\mathbf{p}_u|^2 + |\mathbf{q}_i|^2 \right) $$
ここで、$\mathcal{K}$ は評価が存在するユーザーとアイテムのペアの集合、$\lambda$ は正則化パラメータである。この正則化項により、過学習を防ぐ。
応用例と実装
行列分解は、様々な推薦システムに応用されている。以下に、具体的な応用例として「Movielens-100k」データセットを用いた実装を示す。
データセットの準備
まず、「Movielens-100k」データセットをロードし、評価行列を準備する。 Movielens-100kのデータセットはml-100kというディレクトリに格納されていると仮定する。
from scipy.sparse.linalg import svds
# データセットの読み込み
ratings = pd.read_csv("./ml-100k/u.data", sep="\t", header=None, names=["user_id", "movie_id", "rating", "timestamp"])
ratings = ratings.pivot(index="user_id", columns="movie_id", values="rating").fillna(0)
# 評価行列の作成
R = ratings.values
user_ratings_mean = np.mean(R, axis=1)
R_demeaned = R - user_ratings_mean.reshape(-1, 1)
行列分解の実装
次に、評価行列を行列分解する。ここでは、SVD(特異値分解)を用いる。
# 特異値分解
U, sigma, Vt = svds(R_demeaned, k=50)
sigma = np.diag(sigma)
# 予測行列の作成
all_user_predicted_ratings = np.dot(np.dot(U, sigma), Vt) + user_ratings_mean.reshape(-1, 1)
predicted_ratings = pd.DataFrame(all_user_predicted_ratings, columns=ratings.columns)
# ユーザー1に対する映画推薦
user_id = 1
user_row_number = user_id - 1 # 行番号は0から始まるため
sorted_user_predictions = predicted_ratings.iloc[user_row_number].sort_values(ascending=False)
# 元の評価と予測評価の表示
user_data = ratings.loc[user_id]
user_full = pd.concat([user_data, sorted_user_predictions], axis=1)
user_full.columns = ["Original Rating", "Predicted Rating"]
display(user_full.head(5))
Original Rating | Predicted Rating | |
---|---|---|
movie_id | ||
1 | 5.0 | 6.488436 |
2 | 3.0 | 2.959503 |
3 | 4.0 | 1.634987 |
4 | 3.0 | 3.024467 |
5 | 3.0 | 1.656526 |
結論
この記事では、推薦システムにおける行列分解(特異値分解)について詳述した。
具体的な定義や数式、Pythonコードを用いた具体例を示し、メリットとデメリットを論じた。
この手法は、多くの推薦システムで応用され、高精度な推薦を実現する。
参考文献
- Wikipedia: Matrix Factorization