ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Recommendation] Collaborative Filtering : MSD, Cosine, Pearson 유사도
    ML/Recommendation 2020. 3. 6. 18:16

     

     

     

     

    이전 포스팅에서 구현에 사용했던 유클리디안 유사도 이외의 다른 유사도(Similarity)들을 알아보자.

     

     

     

    1. 평균제곱차이 유사도 (Mean Squared Difference Similarity)


    • User-based Collaborative Filter

    - Iuv는 사용자 u와 사용자 v 모두에 의해 평가된 상품의 집합
    - |Iuv|는 사용자 u와 사용자 v 모두에 의해 평가된 상품의 수
    - 동일한 item에 대해 점수를 매긴 user 사이의 유사도

     

     

     

    • Item-based Collaborative Filter

    - Uij는 상품 i와 상품 j 모두를 평가한 사용자의 집합이고 |Uij|는 상품 i와 상품 j 모두를 평가한 사용자의 수
    - 동일한 user에 대해 점수가 매겨진 item 사이의 유사도

     

     

     

    • Mean Squared Difference Similarity

    - Mean Squared Difference(msd)의 역수 -> msd가 클 수록 Similarity 값은 작아짐
    - MSD가 0이 되는 경우를 대응하기 위해 분모에 1을 무조건 더함

     

     

     

    • Python 구현

    def sim_msd(data, name1, name2):
        
        diff = 0
        count = 0
        
        for title in data[name1]:
            
            if title in data[name2]:
    #             print('commonly watched movie ->', title)
                diff += pow(data[name1][title] - data[name2][title], 2)
                count += 1
                
        sim_msd = 1/( 1 + (diff/count) )
        
        print('** msd similariy = ', sim_msd)
        
        return sim_msd
    ratings = {
         'doky':{'Anne_with_an_E':5,'Strange_Things':3,'The_Escape':3},
         'steve':{'Anne_with_an_E':5,'Strange_Things':1,'The_Escape':4},
         'jamie':{'Anne_with_an_E':0,'Strange_Things':4,'The_Escape':5},
         'bobby':{'Anne_with_an_E':2,'Strange_Things':1,'The_Escape':5},
         'cookie':{'Anne_with_an_E':2,'Strange_Things':1}
    }
    sim_msd(ratings, 'doky', 'cookie')
    Out:    ** msd similariy = 0.13333333333333333

     

     

     

     

    2. 코사인 유사도 (Cosine Similarity)


    • 코사인 유사도 값의 범위 : -1 ~ 1

     

    • 내적 공식 이용하여 도출

    내적 공식

     

     

    • 사용자 u와 사용자 v간의 Cosine Similarity

    - 두 사용자가 모두 평가한 상품의 평점을 사용해서 계산
    - 분모는 내적값을 구하는 것과 같음
    - 분자는 벡터의 크기를 구하는 것과 같음

     

     

     

    • 상품 i와 상품 j간의 Cosine Similarity

    - 두 상품의 평점을 사용해서 계산
    - 분모는 내적값을 구하는 것과 같음
    - 분자는 벡터의 크기를 구하는 것과 같음

     

     

     

    • Python 구현
    import math
    def sim_cosine(data, name1, name2):
        vsize_name1 = 0
        vsize_name2 = 0
        product_sum = 0
        
        for title in data[name1]:
            if title in data[name2]:
                vsize_name1 += pow(data[name1][title], 2)
                vsize_name2 += pow(data[name2][title], 2)
                
                product_sum += data[name1][title]*data[name2][title]
                
        sim_cosine = product_sum/(math.sqrt(vsize_name1)*math.sqrt(vsize_name2))
        
        print("** cosine similarity : ",sim_cosine)
        
        return sim_cosine
    sim_cosine(ratings, 'doky', 'cookie')
    Out:    ** cosine similarity : 0.9970544855015815

     

     

     

     

    3. 피어슨 유사도 (Pearson Similarity)


    • 피어슨 유사도 값의 범위 -1 ~ 1

     

    • 특정인물의 점수기준이 극단적으로 너무 낮거나 높을 경우 유사도에 영향을 크게 주기 때문에, 이를 막기 위해 상관계수 사용

     

    • 사용자 u와 사용자 v간의 Pearson Similarity

    - μu는 사용자 u의 평균 평점

     

     

     

    • 상품 i와 상품 j간의 Pearson Similarity

    - μi는 상품 i의 평균 평점

     

     

     

    • Python 구현
    def sim_pearson(data, name1, name2):
        sum_name1 = 0
        sum_name2 = 0
        count = 0
        
        for title in data[name1]:
            if title in data[name2]:
                sum_name1 += data[name1][title]
                sum_name2 += data[name2][title]
                count += 1
                
        avg_name1 = sum_name1/count
        avg_name2 = sum_name2/count
        
        vsize_name1 = vsize_name2 = product_sum = 0
        
        for title in data[name1]:
            if title in data[name2]:
                vsize_name1 += pow(data[name1][title]-avg_name1, 2)
                vsize_name2 += pow(data[name2][title]-avg_name2, 2)
                product_sum += (data[name1][title]-avg_name1)*(data[name2][title]-avg_name2)
                
        sim_pearson = product_sum / (math.sqrt(vsize_name1)*math.sqrt(vsize_name2))
        print('** Pearson similarity', sim_pearson)
        return sim_pearson
    sim_pearson(ratings, 'doky', 'cookie')
    Out:    ** Pearson similarity 0.9999999999999998

     

     

     

     

    4. 유사도 상위 N 출력


    • Python 구현
    def top_match(data, name, n=3, sim_function=sim_pearson):
        top_list = []
        for user in data:
            if user != name:
                top_list.append((sim_function(data, name, user), user))  # ({유사도}, {사람})
        top_list.sort(reverse=True)
        
        topN_list = top_list[:n]
        
        for idx, user in enumerate(topN_list):
            print(idx+1, '등 : ', user)
        
        return topN_list
    print('[msd]')
    top_match(ratings, 'doky', 3, sim_msd)
    print('-'*50)
    print('[cosine]')
    top_match(ratings, 'doky', 3, sim_cosine)
    print('-'*50)
    print('[pearson]')
    top_match(ratings, 'doky', 3, sim_pearson)
    Out:   
    [msd]            
    ** msd similariy = 0.37499999999999994
    ** msd similariy = 0.09090909090909091
    ** msd similariy = 0.15
    ** msd similariy = 0.13333333333333333
    1 등 : (0.37499999999999994, 'steve')
    2 등 : (0.15, 'bobby')
    3 등 : (0.13333333333333333, 'cookie')
    --------------------------------------------------
    [cosine]
    ** cosine similarity : 0.9412416106700233
    ** cosine similarity : 0.6430394361098802
    ** cosine similarity : 0.7795844649455863
    ** cosine similarity : 0.9970544855015815
    1 등 : (0.9970544855015815, 'cookie')
    2 등 : (0.9412416106700233, 'steve')
    3 등 : (0.7795844649455863, 'bobby')
    --------------------------------------------------
    [pearson]
    ** Pearson similarity 0.6933752452815364
    ** Pearson similarity -0.9819805060619655
    ** Pearson similarity -0.2773500981126146
    ** Pearson similarity 0.9999999999999998
    1 등 : (0.9999999999999998, 'cookie')
    2 등 : (0.6933752452815364, 'steve')
    3 등 : (-0.2773500981126146, 'bobby')

     

    이런 식으로 유사도가 높은 top N명을 찾을 수 있다!

     

     

     

     

     

     

     

    reference : https://www.fun-coding.org/recommend_basic2.html

     

     

    댓글

dokylee's Tech Blog