推薦システムとimplicitライブラリの利用方法

推薦システムは、ユーザーの嗜好や行動を分析し、個別に最適なアイテムを推薦するシステムである。その中で、Pythonのライブラリであるimplicitは特に有名であり、効率的な計算と使いやすさが特徴である。ここでは、implicitライブラリの使い方と、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の設定する。

%matplotlib inline
%config InlineBackend.figure_format = 'svg'
import random
import numpy as np
import pandas as pd

import implicit

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
implicit: 0.7.0

Watermark: 2.4.3

推薦システムで利用されるimplicitの実装例

推薦システムとは

推薦システムとは、ユーザーの嗜好に基づいてアイテムを推薦するシステムである。例えば、Netflixではユーザーが視聴した映画に基づいて新しい映画を推薦する。推薦システムの種類には、大きく分けて協調フィルタリングとコンテンツベースフィルタリングがある。

協調フィルタリング

協調フィルタリングは、ユーザーの過去の行動や評価に基づいてアイテムを推薦する手法である。具体的には、ユーザー行動行列を用いる。

コンテンツベースフィルタリング

コンテンツベースフィルタリングは、アイテムの特徴や属性に基づいてアイテムを推薦する手法である。

implicitライブラリの概要

implicitはPythonで書かれたライブラリであり、特に協調フィルタリングのアルゴリズムを実装するために用いられる。implicitは主に以下のアルゴリズムをサポートしている。

  • ALS(Alternating Least Squares)
  • BPR(Bayesian Personalized Ranking)

Movielens-100kデータセットの概要

Movielens-100kは映画の評価データセットであり、100,000件の評価データが含まれている。このデータセットを用いることで、推薦システムの性能を評価することができる。

ALSアルゴリズムの詳細と実装

ALS(交互最小二乗法)は、ユーザーとアイテムの行列を因子分解する手法である。ALSでは、ユーザー行列とアイテム行列を交互に更新することで、予測行列を近似する。

ALSの表式

ALSの基本的な考え方は、ユーザー行列$\mathbf{U}$とアイテム行列$\mathbf{I}$を求めることである。評価行列$\mathbf{R}$は次のように近似される。

$$ \mathbf{R} \approx \mathbf{U} \mathbf{I}^T $$

ここで、ALSは次の最小化問題を解く。

$$ \min_{\mathbf{U}, \mathbf{I}} || \mathbf{R} - \mathbf{U} \mathbf{I}^T ||^2_F + \lambda ( || \mathbf{U} ||^2_F + || \mathbf{I} ||^2_F ) $$

ここで、$|| \cdot ||_F$はフロベニウスノルムを表し、$\lambda$は正則化パラメータである。

ALSの実装

次に、implicitライブラリを用いたALSの実装例を示す。

import implicit
from scipy.sparse import coo_matrix
from pprint import pprint

# データの読み込みと前処理
df = pd.read_csv("./ml-100k/u.data", sep="\t", names=["user_id", "item_id", "rating", "timestamp"])
rows = df["user_id"].astype(int)
cols = df["item_id"].astype(int)
values = df["rating"].astype(float)

df.head()
user_iditem_idratingtimestamp
01962423881250949
11863023891717742
2223771878887116
3244512880606923
41663461886397596
# 評価行列を作成
R = coo_matrix((values, (rows, cols)))

# coo_matrixをcsr_matrixに変換
R = R.tocsr()

# ALSモデルの訓練
model = implicit.als.AlternatingLeastSquares(factors=20, regularization=0.1, iterations=50)
model.fit(R)

# ユーザーとアイテムの行列
U = model.user_factors
I = model.item_factors

# 結果の表示
pprint(U.round(2))
pprint(I.round(2))
  0%|          | 0/50 [00:00<?, ?it/s]


array([[ 0.  ,  0.  ,  0.  , ...,  0.  ,  0.  ,  0.  ],
       [ 0.65,  1.81,  0.38, ...,  1.04, -0.1 ,  2.  ],
       [ 0.31,  0.4 ,  0.77, ..., -0.69,  0.15, -0.02],
       ...,
       [ 0.12, -0.03,  0.29, ..., -0.41,  0.63, -0.01],
       [ 0.89, -0.79, -0.77, ..., -0.89,  1.31,  0.18],
       [ 0.78,  0.97,  0.26, ...,  1.08,  0.38,  0.3 ]], dtype=float32)
array([[ 0.  ,  0.  ,  0.  , ...,  0.  ,  0.  ,  0.  ],
       [ 0.08, -0.1 , -0.04, ..., -0.08,  0.07, -0.  ],
       [ 0.06,  0.07,  0.14, ...,  0.06,  0.08,  0.04],
       ...,
       [ 0.  ,  0.  , -0.01, ...,  0.01,  0.  , -0.  ],
       [-0.  ,  0.01,  0.01, ..., -0.01,  0.01,  0.  ],
       [-0.01,  0.01, -0.  , ...,  0.01, -0.01,  0.01]], dtype=float32)

BPRアルゴリズムの詳細と実装

BPRは、ランキングを最適化するための手法である。BPRは、ユーザーのpaire-wiseな選好を最大化することを目的とする。

BPRの数式

BPRは、ユーザーがあるアイテムを他のアイテムよりも好む確率を最大化する。具体的には、次の対数尤度関数を最大化する。

$$ \sum_{(u,i,j) \in D} \ln \sigma (\hat{x}_{u,i} - \hat{x}_{u,j}) + \lambda || \Theta ||^2 $$

ここで、$\sigma$はシグモイド関数、$\hat{x}_{u,i}$はユーザー$u$がアイテム$i$に対して持つスコア、$D$はデータセット、$\Theta$はモデルパラメータである。

BPRの実装

次に、implicitライブラリを用いたBPRの実装例を示す。

import implicit
from scipy.sparse import coo_matrix
from pprint import pprint

# データの読み込みと前処理
df = pd.read_csv("./ml-100k/u.data", sep="\t", names=["user_id", "item_id", "rating", "timestamp"])
rows = df["user_id"].astype(int)
cols = df["item_id"].astype(int)
values = df["rating"].astype(float)

df.head()
user_iditem_idratingtimestamp
01962423881250949
11863023891717742
2223771878887116
3244512880606923
41663461886397596
# 評価行列を作成
R = coo_matrix((values, (rows, cols)))

# coo_matrixをcsr_matrixに変換
R = R.tocsr()

# BPRモデルの訓練
model = implicit.bpr.BayesianPersonalizedRanking(factors=20, regularization=0.1, iterations=50)
model.fit(R)

# ユーザーとアイテムの行列
U = model.user_factors
I = model.item_factors

# 結果の表示
pprint(U.round(2))
pprint(I.round(2))
  0%|          | 0/50 [00:00<?, ?it/s]


array([[ 0.  ,  0.  ,  0.  , ...,  0.  ,  0.  ,  1.  ],
       [-0.02, -0.01,  0.13, ..., -0.12,  0.09,  1.  ],
       [-0.01,  0.03, -0.32, ...,  0.29, -0.21,  1.  ],
       ...,
       [ 0.1 , -0.03, -0.15, ...,  0.12, -0.18,  1.  ],
       [-0.2 ,  0.08, -0.04, ...,  0.04,  0.15,  1.  ],
       [ 0.24, -0.1 ,  0.26, ..., -0.24, -0.  ,  1.  ]], dtype=float32)
array([[ 0.  ,  0.  ,  0.  , ...,  0.  ,  0.  ,  0.  ],
       [ 0.12, -0.04, -0.  , ..., -0.01, -0.1 ,  0.61],
       [ 0.2 , -0.07,  0.18, ..., -0.16, -0.02, -0.1 ],
       ...,
       [-0.  ,  0.  , -0.03, ...,  0.03, -0.03, -0.05],
       [ 0.  ,  0.  , -0.01, ...,  0.  , -0.02, -0.07],
       [-0.01, -0.02, -0.01, ..., -0.  , -0.01, -0.12]], dtype=float32)

実装例

具体的な実装例として、movielens-100kデータセットを用いて、ALSおよびBPRモデルを構築する。以下にその手順を示す。

データの準備

まず、データを読み込み、前処理を行う。

import implicit
import pandas as pd
import numpy as np

from scipy.sparse import coo_matrix
from sklearn.model_selection import train_test_split

# データの読み込みと前処理
df = pd.read_csv("./ml-100k/u.data", sep="\t", names=["user_id", "item_id", "rating", "timestamp"])

# トレーニングとテストデータに分割
# stratifyをTrueに設定し、データ分割後も分布が同一になるようにする
train, test = train_test_split(df, test_size=0.2, stratify=df["user_id"], shuffle=True, random_state=seed)

# 評価行列を作成
train_matrix = coo_matrix((train["rating"], (train["user_id"], train["item_id"])))
test_matrix = coo_matrix((test["rating"], (test["user_id"], test["item_id"])))

# coo_matrixをcsr_matrixに変換
train_matrix = train_matrix.tocsr()
test_matrix = test_matrix.tocsr()

ALSモデルの訓練と評価

次に、ALSモデルを訓練し、評価する。

def get_precision(true_matrix, pred_matrix, k=10):
    """
    精度を計算する関数

    Parameters:
    - true_matrix (coo_matrix): 実際の評価行列
    - pred_matrix (ndarray): 予測された評価行列
    - k (int): 精度を計算する際のtop_k itemの数

    Returns:
    - precision (float): 精度
    """
    # 実際の評価行列をリストに変換
    true_items = true_matrix.tolil().rows

    # 予測されたアイテムのインデックスを取得
    pred_items = np.argsort(-pred_matrix, axis=1)[:, :k]

    # ユーザーごとの精度を計算
    precisions = []
    for user_id in range(len(true_items)):
        true_set = set(true_items[user_id])
        pred_set = set(pred_items[user_id])

        if len(true_set) > 0:
            precision = len(true_set & pred_set) / min(len(true_set), k)
            precisions.append(precision)

    # 平均精度を計算
    return np.mean(precisions)
# ALSモデルの訓練
als_model = implicit.als.AlternatingLeastSquares(factors=20, regularization=0.1, iterations=50)
als_model.fit(train_matrix)

# テストデータに対する予測
test_predictions = als_model.recommend_all(test_matrix)


# 使用例
true_matrix = test_matrix  # テストデータの実際の評価行列
pred_matrix = als_model.recommend_all(test_matrix)  # ALSモデルによる予測

precision = get_precision(true_matrix, pred_matrix)
print(f"ALSモデル Precision: {precision:.3f}")
  0%|          | 0/50 [00:00<?, ?it/s]


ALSモデル Precision: 0.039

BPRモデルの訓練と評価

同様に、BPRモデルを訓練し、評価する。

# BPRモデルの訓練
bpr_model = implicit.bpr.BayesianPersonalizedRanking(factors=20, regularization=0.1, iterations=50)
bpr_model.fit(train_matrix)

# テストデータに対する予測
test_predictions = bpr_model.recommend_all(test_matrix)

# 精度の評価
precision = get_precision(test_matrix, test_predictions)
print(f"BPRモデル Precision : {precision:.3f}")
  0%|          | 0/50 [00:00<?, ?it/s]


BPRモデル Precision : 0.039

結論

この記事では、implicitライブラリを用いてALSおよびBPRアルゴリズムを実装し、movielens-100kデータセットでの具体例を紹介した。 基本的には自分用のメモだが、誰かの参考人になれば幸いである。

参考文献

  • “Collaborative Filtering for Implicit Feedback Datasets”, Hu, Y., Koren, Y., and Volinsky, C., 2008.
  • “BPR: Bayesian Personalized Ranking from Implicit Feedback”, Rendle, S., Freudenthaler, C., Gantner, Z., and Schmidt-Thieme, L., 2009.
  • Movielens Dataset: https://grouplens.org/datasets/movielens/100k/

メモ

LIL形式の疎行列を作成 (3x3の行列)

import numpy as np
from scipy.sparse import lil_matrix

# Numpy配列の作成
dense_array = np.array([[1, 0, 0], [0, 0, 3], [4, 0, 0]])

# Numpy配列をLIL形式の疎行列に変換
lil_matrix = lil_matrix(dense_array)

print(lil_matrix)
  (0, 0)	1
  (1, 2)	3
  (2, 0)	4