SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

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

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

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

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

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
推薦結果の作成

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
現場のAIエンジニアに学ぶ推薦システム入門連載記事一覧

もっと読む

この記事の著者

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

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

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

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

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

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/14321 2021/09/08 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング