edo1z blog

プログラミングなどに関するブログです

協調フィルタリング(ユークリッド距離)

これを読んでます。集合知プログラミング

集合知プログラミング

参考:協調フィルタリングでアイテムの推薦をする

協調フィルタリングとは?

参考:協調フィルタリング

協調フィルタリング(きょうちょうフィルタリング、Collaborative Filtering、CF)は、多くのユーザの嗜好情報を蓄積し、あるユーザと嗜好の類似した他のユーザの情報を用いて自動的に推論を行う方法論である

ユークリッド距離とは?

参考:ユークリッド距離

数学におけるユークリッド距離(ユークリッドきょり、英: Euclidean distance)またはユークリッド計量(ユークリッドけいりょう、英: Euclidean metric; ユークリッド距離函数)とは、人が定規で測るような二点間の「通常の」距離のことであり、ピタゴラスの公式によって与えられる。

つまりただの2点間の距離です。ピタゴラスの定理は、直角三角形の斜辺cは、他の2辺a、bとすると、が成り立ちます。よって、となります。

ユークリッド距離で嗜好が似てる人を探す

AさんとBさんがいて、芸能人あと、芸能人いがいるとします。Aさんは、あの好き度が10で、いの好き度が5です。Bさんは、あの好き度が3で、いの好き度が3です。AさんとBさんの嗜好の類似度をユークリッド距離で計算するには、x軸にあの好き度、y軸にいの好き度をおいて、Aさん、Bさんをプロットします。Aさん座標とBさん座標の距離が短ければ嗜好が似ていることになります。好き度が完全に同じであれば距離は0になります。座標同士の距離をピタゴラスの定理で計算できます。

でもこれだと2人の芸能人に対する嗜好の類似度しか測れません。複数の場合は、それぞれの芸能人の好き度の差の2乗を全部足せばいいらしいです。平方ユークリッド距離というらしいです。

Pythonでやってみる

データを用意する。

data = {
    'A': {'あ': 10, 'い': 5, 'う': 3, 'え': 4, 'お': 9, 'か': 6},
    'B': {'あ': 3, 'い': 3, 'う': 3, 'え': 4, 'お': 3, 'か': 4},
    'C': {'あ': 2, 'い': 10, 'う': 4, 'え': 2, 'お': 4},
    'D': {'あ': 6, 'い': 3, 'う': 5, 'え': 2, 'お': 5},
    'E': {'あ': 8, 'い': 4, 'う': 3, 'え': 2, 'お': 9},
    'F': {'あ': 1, 'い': 2, 'う': 9, 'え': 3, 'お': 8},
    'G': {'あ': 9, 'い': 4, 'う': 3, 'え': 4, 'お': 9, 'か': 5},
    'H': {'あ': 10, 'い': 0, 'う': 1, 'え': 2, 'お': 5},
    'I': {'あ': 9, 'い': 10, 'う': 10, 'え': 5, 'お': 3},
    'J': {'あ': 3, 'い': 8, 'う': 8, 'え': 1, 'お': 5},
    'K': {'あ': 7, 'い': 4, 'う': 1, 'え': 2, 'お': 9},
    'L': {'あ': 8, 'い': 3, 'う': 1, 'え': 4, 'お': 1},
    'M': {'あ': 1, 'い': 5, 'う': 4, 'え': 5, 'お': 5},
    'N': {'あ': 1, 'い': 5, 'う': 4, 'え': 6, 'お': 3},
    'O': {'あ': 10, 'い':4}
}

AさんとBさんの似ている度を、あ・いを対象に計測してみる

score = 1 / (1 + sqrt(pow(data['A']['あ'] - data['B']['あ'], 2) + pow(data['A']['い'] - data['B']['い'], 2)))
print(score)

0.12077134402462537と表示されます。

Aさんと嗜好が似ている人順に表示する

#person1とperson2の嗜好似ている度を返す
#高い方が似ている。0〜1の間の数値を返す
def euclid_distance(data, person1, person2):
    item_list = []
    for item in data[person1]:
        if item in data[person2]:
            item_list.append(item)

    if len(item_list) < 1: return 0
    return 1 / (1 + sum([pow(data[person1][item] - data[person2][item], 2) for item in item_list]))

#meに嗜好が似ている人順に並んだリストを返す
def get_similar_list(data, me):
    person_list = {}
    for person in data:
        if person == me: continue
        person_list[person] = euclid_distance(data, me, person)

    return sorted(person_list.items(), key = itemgetter(1), reverse = True)

a_sim = get_similar_list(data, 'A')
for person in a_sim:
    print(person[0], person[1])

下記のように表示されます。

O 0.5 G 0.25 E 0.1 K 0.05263157894736842 D 0.022222222222222223 H 0.02 L 0.012987012987012988 B 0.010638297872340425 M 0.01 J 0.009174311926605505 I 0.008849557522123894 C 0.008333333333333333 N 0.008130081300813009 F 0.007751937984496124