推薦システム(数式を追加しました) | python3Xのブログ

python3Xのブログ

ここでは40代、50代の方が日々の生活で役に立つ情報や私の趣味であるプログラム、Excelや科学に関する内容で投稿する予定です。

学食でも社内食堂でも良いのですが、カレー、ラーメン、かつ丼、天丼、牛丼、うどん

それぞれのメニューに5点満点で7人の人に評価してもらいました。

もちろん、一度も食べたことがないメニューは各自データには出ていません。

dataset = {
    '佐々木': {'カレー': 2.5,
           'ラーメン': 3.5,
           'かつ丼': 3.0,
           '天丼': 3.5,
           '牛丼': 2.5,
           'うどん': 3.0},
    '吉田': {'カレー': 3.0,
           'ラーメン': 3.5,
           'かつ丼': 1.5,
           '天丼': 5.0,
           'うどん': 3.0,
           '牛丼': 3.5},
    '伊藤': {'カレー': 2.5,
           'ラーメン': 3.0,
           '天丼': 3.5,
           'うどん': 4.0},
    '宮本': {'ラーメン': 3.5,
           'かつ丼': 3.0,
           'うどん': 4.5,
           '天丼': 4.0,
           '牛丼': 2.5},
    '野村': {'カレー': 3.0,
           'ラーメン': 4.0,
           'かつ丼': 2.0,
           '天丼': 3.0,
           'うどん': 3.0,
           '牛丼': 2.0},
    '遠藤': {'カレー': 3.0,
           'ラーメン': 4.0,
           'うどん': 3.0,
           '天丼': 5.0,
           '牛丼': 3.5},
    '中野': {'ラーメン': 4.5,
           '牛丼': 1.0,
           '天丼': 4.0}}

 

テストとして上のデータを読み出してみる。

from recommendation_data import dataset
from math import sqrt
print(("佐々木さんのカレーの評価 : {}".format(
    dataset['佐々木']['カレー'])))
print(("佐々木さんのうどんの評価 : {}\n".format(
    dataset['佐々木']['うどん'])))
print(("伊藤さんのカレーの評価: {}".format(
    dataset['伊藤']['カレー'])))
print(("伊藤さんのうどんの評価: {}\n".format(
    dataset['伊藤']['うどん'])))
print("中野さんのレイティング: {}\n".format((dataset['中野'])))
佐々木さんのカレーの評価 : 2.5
佐々木さんのうどんの評価 : 3.0
伊藤さんのカレーの評価: 2.5
伊藤さんのうどんの評価: 4.0
中野さんのレイティング: {'ラーメン': 4.5, '天丼': 4.0, '牛丼': 1.0}
 
強調フィルタリングを実装する
(この商品を買った人はこれこれの商品も買っていますというような)
類似尺度にユークリッド距離を用いると
from recommendation_data import dataset
from math import sqrt
def similarity_score(person1, person2):
    # 戻り値は person1 と person2 のユークリッド距離
    both_viewed = {}  # 双方に共通のアイテムを取得
    for item in dataset[person1]:
        if item in dataset[person2]:
            both_viewed[item] = 1
    # 共通のアイテムを持っていなければ 0 を返す
    if len(both_viewed) == 0:
        return 0
    # ユークリッド距離の計算
    sum_of_eclidean_distance = []
    for item in dataset[person1]:
        if item in dataset[person2]:
            sum_of_euclidean_distance.append(
                pow(dataset[person1][item] - dataset[person2][item], 2))
    total_of_euclidean_distance = sum(sum_of_euclidean_distance)
    return 1 / (1 + sqrt(total_of_euclidean_distance))
print("佐々木さんと野村さんの類似度 (ユークリッド距離)",
      similarity_score('佐々木', '野村'))
佐々木さんと野村さんの類似度 (ユークリッド距離) 0.4142135623730951
 
次にピアソン相関係数を用います。正規化がなされていない状況では、
ユークリッド距離より良い結果が得られる場合が多いとされています。
from recommendation_data import dataset
from math import sqrt
def pearson_correlation(person1, person2):
    # 両方のアイテムを取得
    both_rated = {}           # Σx と y共通の項目 i
    for item in dataset[person1]:
        if item in dataset[person2]:
            both_rated[item] = 1
    number_of_ratings = len(both_rated)       # Σの i の最後の値(最大値 N)
    # 共通のアイテムがあるかチェック、無ければ 0 を返す
    if number_of_ratings == 0:
        return 0
    # 各ユーザーごとのすべての好みを追加
    person1_preferences_sum = sum(
        [dataset[person1][item] for item in both_rated])   # Σx_i
    person2_preferences_sum = sum(
        [dataset[person2][item] for item in both_rated])   # Σy_i
    # 各ユーザーの好みの値の二乗を計算
    person1_square_preferences_sum = sum(
        [pow(dataset[person1][item], 2) for item in both_rated])   # Σx_i^2
    person2_square_preferences_sum = sum(
        [pow(dataset[person2][item], 2) for item in both_rated])   # Σy_i^2
    # アイテムごとのユーザー同士のレイティングを算出して合計
    product_sum_of_both_users = sum(
        [dataset[person1][item] * dataset[person2][item] for item in both_rated])   # Σx_i * y_i
    # ピアソンスコアの計算
    # preference:好み、denominator:分母
    numerator_value = product_sum_of_both_users - \
        (person1_preferences_sum * person2_preferences_sum / number_of_ratings)   # Σ(x_i * y_i )/N
    denominator_value = sqrt((person1_square_preferences_sum - pow(person1_preferences_sum, 2) / number_of_ratings) * (  # √{{Σx_i^2 - (Σx_i)^2 }/N]*√[{y_i^2 - (Σy_i)^2}/N}
        person2_square_preferences_sum - pow(person2_preferences_sum, 2) / number_of_ratings))
    if denominator_value == 0:                                                     # 分母が0になるのを避ける
        return 0
    else:
        r = numerator_value / denominator_value      # Σ(x_i * y_i )/[√{Σx_i^2 - (Σx_i)^2 }*√{y_i^2 - (Σy_i)^2}]
        return r
print("佐々木さんと野村さんの類似度 (ピアソン相関係数)",
      (pearson_correlation('佐々木', '野村')))
佐々木さんと野村さんの類似度 (ピアソン相関係数) 0.5940885257860044