본문 바로가기
추천시스템

사용자간 유사도 계산

by 볼록티 2019. 11. 23.
728x90
반응형

 

1. 가장 높은 평점을 갖는 아이템을 추천하는 가장 단순한 방법을 연습합니다.

ratings={
     'Dave':{'달콤한인생':5,'범죄도시':3,'샤인':3},
     'David':{'달콤한인생':5,'범죄도시':1,'샤인':4},
     'Alex':{'달콤한인생':0,'범죄도시':4,'샤인':5},
     'Andy':{'달콤한인생':2,'범죄도시':1,'샤인':5}
}

movies_dict = dict()

for rating in ratings:
    for movie in ratings[rating].keys():
        if movie not in movies_dict:
            movies_dict[movie] = ratings[rating][movie]
        else:
            movies_dict[movie] = (movies_dict[movie] + ratings[rating][movie])
            
for movie in ratings[rating].keys():
    movies_dict[movie] = movies_dict[movie]/4
    
import operator
sorted_x = sorted(movies_dict.items(), key=operator.itemgetter(1), reverse=True)
print(sorted_x)


##결과: [('달콤한인생', 4.5), ('범죄도시', 3.375), ('샤인', 6.375)]

-> 각 사용자가 남긴 평점을 영화별로 평균을 내고 가장 평점 평균이 높은 영화를 찾을 수 있었습니다. 이를 통해 가장 높은 평점 평균을 가진 영화를 사람들에게 추천해주는 단순한 방식입니다.

 

 

 


2. 이번에는 즉 CF 기법을 사용하는 것입니다. 사용자 간의 유사도 까지만 구해보도록 하겠습니다.

 

데이터 셋은 다음과 같습니다. 평점을 입력하지 않은 사용자가 있다는 것을 알 수 있다. 이렇게 불완전한 데이터를 가진 와중에도 서로 유사도를 구할 수 있다니 아직 모르는 게 너무 많은 단계인 듯 합니다...

ratings={
     'Dave':{'달콤한인생':5,'범죄도시':3,'샤인':3},
     'David':{'달콤한인생':2,'범죄도시':1,'샤인':4},
     'Alex':{'범죄도시':4,'샤인':5},
     'Andy':{'달콤한인생':2,'범죄도시':1,'샤인':5}
}

 

 

피타고라스 공식을 통한 유클리드 거리를 활용하여 유사도를 구해봅니다.

 

 

입력하는 i와 j는 평점 간의 차를 넣습니다. 그리고 각각 제곱하여 합에 루트를 씌워줌으로써 유클리디안 거리 값을 구하게 됩니다.

import math
def sim(i,j):
    return math.sqrt(pow(i,2)+pow(j,2))

 

 영화 '샤인', '범죄도시'에 대해서 Alex와 Andy의 유사도를 구해봅니다. 두 분이 동시에 평가한 영화가 저 두개라서 저 둘로 유사도를 측정하게 됩니다. 이런 부분을 만날 때 CF기법의 단점에 대해 한번 생각..만 해보고 넘어갔습니다.🐱‍🚀

 

🐱‍🏍var1, var2는 두 영화의 매겨진 각 평점들의 차이 입니다.

var1= ratings.get("Alex").get("샤인") - ratings.get("Andy").get("샤인")
# 0
var2= ratings.get("Alex").get("범죄도시") - ratings.get("Andy").get("범죄도시")
# 3

둘의 유사도는 3이라는 결과를 얻게 되었습니다.

sim(var1, var2)
#3.0

 

🐱‍🏍그럼 이번에는 Alex님이 평가한 영화 '범죄도시'와 '샤인' 을 모두 평가하신 다른 나머지 모두와의 유사도를 각각 계산해보는 예제를 살펴보겠습니다.

 

이전 만들었던 sim(i, j) 함수를 그대로 사용하였습니다.

for i in ratings:
    if i != 'Alex':
        var1= ratings['Alex']['범죄도시'] - ratings[i]['범죄도시']
        var2= ratings['Alex']['샤인'] - ratings[i]["샤인"]
        print(i, " : ",sim(var1, var2))
        
        
# Dave  :  2.23606797749979
# David  :  3.1622776601683795
# Andy  :  3.0

다른사람과 Alex님 사이의 거리를 알 수 있습니다. Dave가 거리가 가장 멀지 않다는 것을 알 수 있습니다. 좀 더 수치적으로 쉽게 알아보기 위해 정규화를 해보도록 하겠습니다.

 

이곳에서 정규화는 우리가 구한 거리에 1을 더하고 역수로 바꿔주었습니다

 

for i in ratings:
    if i != 'Alex':
        var1= ratings['Alex']['범죄도시'] - ratings[i]['범죄도시']
        var2= ratings['Alex']['샤인'] - ratings[i]["샤인"]
        print(i, " : ",1/(sim(var1, var2)+1))
        
        
# Dave  :  0.3090169943749474
# David  :  0.2402530733520421
# Andy  :  0.25

-> 수치를 보니 아까 거리가 가장 가까웠다고 말했던 Dave 가 정규화한 수치가 가장 높게 나왔습니다. 즉, 상대적으로 다른 사람보다 수치상 크다는 것으로 우리는 Alex와 Dave가 가장 유사하다고 해석을 해볼 수 있게되었습니다.

 

 

이번엔 Dave님의 다른 사람과의 유사도를 측정해봅니다.

for i in ratings:
    if i!='Dave':
        num1 = ratings.get('Dave').get('범죄도시') - ratings.get(i).get('범죄도시')
        num2 = ratings.get('Dave').get('샤인') - ratings.get(i).get('샤인')
        print(i," : ", 1 / ( 1 + sim(num1,num2) ) )
        
# David  :  0.3090169943749474
# Alex  :  0.3090169943749474
# Andy  :  0.2612038749637414

-> David와 Alex와 가장 유사하다는 동일한 수치를 볼 수 있습니다. 이는 행렬을 통해 보면 한눈에 쉽게 알아 볼 수 있습니다.

 

 

🐱‍🚀 유사도 행렬 만들기.

 

import math
import pandas as pd

def sim(i,j):
    return math.sqrt(pow(i,2)+pow(j,2))

def sim_matrix(ratings):
    sim_mat=[]
    for i in ratings:
        tmp=[]
        for j in ratings:
            num1 = ratings.get(i).get('범죄도시') - ratings.get(j).get('범죄도시')
            num2 = ratings.get(i).get('샤인') - ratings.get(j).get('샤인')
            tmp.append(1 / ( 1 + sim(num1,num2)))
        sim_mat.append(tmp)
    return pd.DataFrame(sim_mat, index=list(ratings.keys()), columns=list(ratings.keys()))

sim_matrix(ratings)

-> 자기 자신과는 당연히 동일하므로 정규화 값 1을 갖게 되고 나머지를 통해서 사용자 간의 유사도를 알 수 있습니다. Alex가 만약에 '달콤한인생' 영화에 평점을 주었다면 유사도가 더욱 의미를 갖게 될 것이라 생각해보면서 이 번 장을 마칩니다.

 

 

 

728x90
반응형

댓글