CodeZine(コードジン)

特集ページ一覧

行列分解を用いた推薦システムを実装してみよう!【推薦システム入門】

  • LINEで送る
  • このエントリーをはてなブックマークに追加

 データをもとに、ユーザーが気に入りそうなアイテムを推薦する推薦システムは、通販サイトや求人サイトなど、生活のいたるところで利用されています。本連載では推薦システムについて学びたい開発者やデータサイエンティスト、およびプロダクトのユーザー体験を向上させたいと考えている方向けに、接触履歴情報のみを用いる「暗黙的フィードバック」を使った推薦システムの概要と代表的なアルゴリズム、およびそれらの長所と短所を解説します。前回は、接触履歴情報のみのデータである「暗黙的フィードバック」を用いた推薦システムのうちで、最もポピュラーな手法である行列分解(Matrix Factorization)について説明しました。今回は、行列分解のアルゴリズムを適用する具体的なコードの説明を通して、行列分解の理解を深めていきましょう。

目次

行列分解を用いた推薦のコード例

 前回は、行列分解アルゴリズムとして特異値分解(Singular Value Decomposition、以下SVD)と重み付き行列分解(Weighted Matrix Factorization、以下WMF)を紹介しました。

 SVDは行列分解で最も基本的な手法であり、推薦システムに限らず、さまざまな統計パッケージで実装されている一般的なアルゴリズムです。WMFは、暗黙的フィードバックで接触があった組み合わせを特に重視して学習する行列分解手法であり、SVDよりも暗黙的フィードバックのデータに適した改良が加わった手法です。

 ここからは、具体的なデータに対してSVDとWMFを適用し推薦を実施する流れを、コード例を交えつつ見ていきましょう(下記コードは scikit-learn 0.24.0, scipy 1.6.0, matplotlib 3.3.4で作成しました)。

 まずは前回で紹介したレストラン訪問履歴を表す行列のSVDを考えます。この履歴では、対象となる行列の行がユーザー(レストランの客)であり、列がアイテム(レストラン)に対応していました。

from sklearn.utils.extmath import randomized_svd
from matplotlib import pyplot as plt
import scipy.sparse as sps

R = sps.csr_matrix([[1, 0, 1], [0, 1, 1], [1, 1, 1]])

# SVD 実行
O, Sigma, QT = randomized_svd(R, n_components=2)
P = O * Sigma
Q = QT.transpose()

print(P.dot(QT))

  SVDにはさまざまな実装がありますが、ここではSVDを計算するための手法の一つ、scikit-learnのrandomized_svdを用いています。randomized_svdは、大規模な行列\(R\)が与えられていて、計算の結果得られる特徴ベクトルの次元\(n\)が小さい場合に、高速で結果を得ることができます。また一般に接触履歴の行列のサイズが大きい場合でも、特徴ベクトルの次元\(n\)は数十から高々数百程度あれば十分な精度が得られる傾向にあります。このレストラン訪問履歴のような3x3程度の行列では計算コストに大きな違いは生じませんが、接触履歴の行列のサイズが大きい場合には大きな差が生まれますので、特徴ベクトルの次元が小さければ計算速度が速くなるrandomized_svdのようなアルゴリズムを利用することが適切であることが多いです。

 それでは、得られた\(P\),\(Q\)をプロットしてみましょう:

figure, ax = plt.subplots()
ax.set_aspect("equal")
for user_index, user_vector in enumerate(P):
    ax.annotate(
        f"User {user_index+1}",
        xy=(0, 0),
        xytext=user_vector,
        arrowprops=dict(arrowstyle="<-", color="blue"),
    )

for item_index, item_vector in enumerate(Q):
    ax.annotate(
        f"Item {item_index + 1}",
        xy=(0, 0),
        xytext=item_vector,
        arrowprops=dict(arrowstyle="<-", color="orange"),
    )

 上記コードで得られるのは、前回示した次の図を45°時計回りに回転させた結果になります(実は前回も著者が回転を施していました)。行列分解では\(P\),\(Q\)を同時に回転させても結果は変わらないので、このようなことが可能になります。

SVDで計算されたユーザー/アイテムのベクトルを2次元にプロットした結果

SVDで計算されたユーザー/アイテムのベクトルを回転して2次元にプロットした結果


  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:現場のAIエンジニアに学ぶ推薦システム入門

著者プロフィール

  • 大槻 知貴(株式会社ビズリーチ)(オオツキ トモキ)

     株式会社ビズリーチ(Visionalグループ) CTO室 AIグループ所属  NTTデータ数理システム、AIベンチャーを経て、2018年にビズリーチ入社。データサイエンス業務に従事し、Visionalグループにおける機械学習関連機能のR&amp;Dを担当。理学博士(物理学)。

  • 中江 俊博(株式会社ビズリーチ)(ナカエ トシヒロ)

     株式会社ビズリーチ(Visionalグループ) CTO室 AIグループ所属  NTTデータ数理システムにてデータ分析の受託案件を多数担当。その後、IoTスタートアップを経て、2019年にビズリーチ入社。レコメンドシステムなど機械学習関連の実装作業に従事。

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5