らくがき入門

機械学習を始めとしたコンピュータサイエンスを主に扱っています。

アイテムベースの推薦アルゴリズム「Slope One」

今回は比較的シンプルなアイテムベースの推薦アルゴリズムながら、比較的良い推薦精度を示すSlope One予測について説明します。 概要を説明して、数式での説明を加えて、Pythonで実装するという流れで進めます。

Slope One予測とは

Slope One予測の考えとしては、ユーザーのアイテムに対する「人気度の差異」に基づいています。

Slope One予測は、2組のアイテムに対してあるアイテムの評価値を別のアイテムの評価値から予測するための関数を見つけるという考えに基づいて、 未知の評価値を算出します。 具体的には、ここのユーザーのアイテム評価値間の差分を算出し、全ユーザーについて算出されたアイテム評価値間差分の平均値を算出し、この算出結果を基に結果を算出していきます。

Slope One 予測の定式化

シンプルなSlope One予測の定式化

Slope One予測は以下のように定式化できます。

  • アイテム間での平均偏差を求めます。

 \displaystyle
dev _ {j, i} = \Sigma _ {(u _ {j}, u _ {i}) \in S _ {j, i}(R)} \frac{u _ {j} - u _ {i}}{|S _ {j, i}(R)|}

通常評価値データベース全体を Rで示し、あるユーザーの評価値は不完全な配列 uで構成されます。  u _ {i} uのアイテム iに対する評価値(つまりあるユーザーのアイテム iに対する評価値)です。  S _ {j, i}(R)はアイテム i jを両方評価したユーザーの集合 S _ {j, i}(R)の要素数を表現しています。

  • 共に評価付されたアイテムの平均値を計算します。

 \displaystyle
pred(u, j) = \frac{\Sigma _ {i \in Relevant(u, j)} (dev _ {j, i} + u _ {i})}{|Relevant(u, j)|}

関数 Relevant(u, j)はユーザー uによって jと共に少なくとも1回は評価されたアイテムと定義します。

重み付けしたSlope One予測の定式化

直感的には、1人よりも10人のユーザーと行ったようにより多くのユーザーの評価値を基に算出したほうが、信頼性の高い予測になると考えられます。 そこで個々の偏差の重みを共に評価された回数として、重み付けを行うことを考えます。

 \displaystyle
pred(u, j) =  \frac{\Sigma _ {i \in S(u) - \{j\}}(dev _ {j, i} + u _ {i} \times |S _ {j, i}(R)|)} {\Sigma _ {i \in S(u) - \{j\}} |S _ {j, i}(R)|}

Pythonでの実装

以下のような、評価値データベースを考えます。

item1 item2 item3
alice 2 5
user1 3 2 5
user2 4 3

この評価値データベースでaliceのitem3の評価値を予測することを目的とします。 今回は、上で示した重み付けをしたSlope One予測で評価値を予測します。

この例では、

  • 共に評価されている回数が2回であるitem1とitem3があります。 user1においては、item3に対してitem1よりも2ポイント高い評価付けをしています。( 5 - 3 = 2) user2においては、item3に対してitem1よりも1ポイント低い評価付をしています。( 3 - 4 = -1) よって、これらの平均距離は、 (2 + (-1)) / 2 = 0.5です。

  • 共に評価されている回数が1であるitem2とitem3があります。 user1においては、item3に対してitem2よりも3ポイント高い評価付けをしています。( 5 - 2 = 3) よって、これらの平均距離は、 3 / 1 = 3です。

総合的な予測値は、共に評価された回数を考慮に入れるため、重み付けをして計算します。  \displaystyle
pred(alice, item3) = \frac{2 \times 2.5 + 1 \times 8}{2 + 1} = 4.33

これをPythonで実装したコードを以下に示します。

import numpy as np


arr = np.array([[2, 5, np.nan], 
                [3, 2, 5], 
                [4, np.nan, 3]])

target_user_index = 0
target_item_index = 2
exist_rat_target = np.flatnonzero(~np.isnan(arr[:, target_item_index]))

top_value = 0
bottom_value = 0
for idx in range(arr.shape[1]):
    if idx != target_item_index:
        comp_arr = arr[:, target_item_index][exist_rat_target] - arr[:, idx][exist_rat_target]
        rat_cnt = np.count_nonzero(~np.isnan(arr[:, idx][exist_rat_target]))
        target_user_rat = arr[target_user_index, idx] 
        top_value += rat_cnt * (target_user_rat + np.nansum(comp_arr) / rat_cnt)
        bottom_value += rat_cnt

print('alice rating of item3: {:.2f}'.format(top_value / bottom_value))

まとめ

Slope One予測はかなり直感的でわかりやすいにも関わらず、一般的なユーザーベースやアイテムベースよりも比較的良い精度をだすみたいです(もう少しサーベイは必要だとは思いますが・・・)。 今度はMovieLensのデータセットで精度評価をしてみたいですね。 気が向けば追記します。

qiita.com

情報推薦システム入門 -理論と実践-

情報推薦システム入門 -理論と実践-