ML/Recommendation

[Recommendation] 유사도와 KNN을 활용한 예측값 계산 및 추천 목록 생성

dokylee 2020. 3. 6. 18:46

 

 

 

 

유사도를 구하는 것까지 구현을 해봤으니, 이제 추천 목록을 생성해보자!

 

 

 

유사도와 KNN을 활용한 예측값 계산 및 추천 목록 생성 기법


- 사용자들 간의 유사도를 이용하여 모든 아이템에 대한 예측 평점을 계산하고, 높은 값을 갖는 상위 N개의 추천 목록 생성

 

 

- Nearest Neighbors(KNN) 가중치 예측 기법

  • 사용자(또는 아이템)와 유사도가 큰 k개의 사용자(또는 아이템) 벡터를 사용해 가중 평균을 구해서 가중치를 예측

 

 

 

 

KNN Basic


평점들을 단순히 가중 평균Nk는 유사도가 큰 벡터 상위 k개의 집합

 

 

 

  • Python 구현

 

 

 

 

ratings_expand = {
    '마동석': {
        '택시운전사': 3.5,
        '남한산성': 1.5,
        '킹스맨:골든서클': 3.0,
        '범죄도시': 3.5,
        '아이 캔 스피크': 2.5,
        '꾼': 3.0,
    },
    '이정재': {
        '택시운전사': 5.0,
        '남한산성': 4.5,
        '킹스맨:골든서클': 0.5,
        '범죄도시': 1.5,
        '아이 캔 스피크': 4.5,
        '꾼': 5.0,
    },
    '윤계상': {
        '택시운전사': 3.0,
        '남한산성': 2.5,
        '킹스맨:골든서클': 1.5,
        '범죄도시': 3.0,
        '꾼': 3.0,
        '아이 캔 스피크': 3.5,
    },
    '설경구': {
        '택시운전사': 2.5,
        '남한산성': 3.0,
        '범죄도시': 4.5,
        '꾼': 4.0,
    },
    '최홍만': {
        '남한산성': 4.5,
        '킹스맨:골든서클': 3.0,
        '꾼': 4.5,
        '범죄도시': 3.0,
        '아이 캔 스피크': 2.5,
    },
    '홍수환': {
        '택시운전사': 3.0,
        '남한산성': 4.0,
        '킹스맨:골든서클': 1.0,
        '범죄도시': 3.0,
        '꾼': 3.5,
        '아이 캔 스피크': 2.0,
    },
    '나원탁': {
        '택시운전사': 3.0,
        '남한산성': 4.0,
        '꾼': 3.0,
        '범죄도시': 5.0,
        '아이 캔 스피크': 3.5,
    },
    '소이현': {
        '남한산성': 4.5, 
        '아이 캔 스피크': 1.0,
        '범죄도시': 4.0
    }
}
def get_KNN_recommend(data, name, n=3, sim_function=sim_pearson):
    
    result = top_match(data, name, n, sim_function)
    
    score_dic = dict()
    sim_dic = dict()
    li = []
    
    for sim, user in result:
        print(sim, user)
        if sim<0: continue
        for title in data[user]:
            if title not in data[name]:
                score = sim * data[user][title]
                score_dic.setdefault(title, 0)
                score_dic[title] += score
                
                sim_dic.setdefault(title, 0)
                sim_dic[title] += sim
                
    for title in score_dic:
        score_dic[title] /= sim_dic[title]
        li.append((score_dic[title], title)) # (예측평점, 영화제목)
        
    li.sort(reverse=True)
    
    print('\n** Recommendation')
    for rating, score in enumerate(li):
        print(rating+1, '등: ', score[1], ', ', score[0], '점')
    
    return li
get_KNN_recommend(ratings_expand, '소이현')
Out:
...

** Recommendation
1 등: 꾼 , 3.6903300787431923 점
2 등: 택시운전사 , 3.0 점
3 등: 킹스맨:골든서클 , 1.9168062051113846 점

 

 

 

 

KNN with Means


평점들을 평균값 기준으로 가중 평균

 

 

  • Python 구현
sum = 0
count = 0

for name in ratings_expand:
    for title in ratings_expand[name]:
        sum += ratings_expand[name][title]
        count += 1
    
    ratings_expand[name]['avg'] = sum/count
    
ratings_expand
Out:
{'마동석': {'택시운전사': 3.5,
                '남한산성': 1.5,
                '킹스맨:골든서클': 3.0,
                '범죄도시': 3.5,
                '아이 캔 스피크': 2.5,
                '꾼': 3.0,
                'avg': 2.8333333333333335},
'이정재': {'택시운전사': 5.0,
               '남한산성': 4.5,
               '킹스맨:골든서클': 0.5,
               '범죄도시': 1.5,
               '아이 캔 스피크': 4.5,
               '꾼': 5.0,
               'avg': 3.1666666666666665},
'윤계상': {'택시운전사': 3.0,
               '남한산성': 2.5,
               '킹스맨:골든서클': 1.5,
               '범죄도시': 3.0,
               '꾼': 3.0,
               '아이 캔 스피크': 3.5,
               'avg': 3.0277777777777777},
'설경구': {'택시운전사': 2.5,
               '남한산성': 3.0,
               '범죄도시': 4.5,
               '꾼': 4.0,
               'avg': 3.1136363636363638},
'최홍만': {'남한산성': 4.5,
               '킹스맨:골든서클': 3.0,
               '꾼': 4.5,
               '범죄도시': 3.0,
               '아이 캔 스피크': 2.5,
               'avg': 3.185185185185185},
'홍수환': {'택시운전사': 3.0,
               '남한산성': 4.0,
               '킹스맨:골든서클': 1.0,
               '범죄도시': 3.0,
               '꾼': 3.5,
               '아이 캔 스피크': 2.0,
               'avg': 3.106060606060606},
'나원탁': {'택시운전사': 3.0,
               '남한산성': 4.0,
               '꾼': 3.0,
               '범죄도시': 5.0,
               '아이 캔 스피크': 3.5,
               'avg': 3.1842105263157894},
'소이현': {'남한산성': 4.5,
               '아이 캔 스피크': 1.0,
               '범죄도시': 4.0,
               'avg': 3.182926829268293}}
def get_KNNmeans_recommend(data, name, n=3, sim_function=sim_pearson):
    
    result = top_match(data, name, n, sim_function)
    
    score_dic = dict()
    sim_dic = dict()
    li = []
    
    for sim, user in result:
        print(sim, user)
        if sim<0: continue
        for title in data[user]:
            if title not in data[name]:
                score = sim * (data[user][title]-data[user]['avg'])
                score_dic.setdefault(title, 0)
                score_dic[title] += score
                
                sim_dic.setdefault(title, 0)
                sim_dic[title] += sim
                
    for title in score_dic:
        score_dic[title] = data[name]['avg']+(score_dic[title]/sim_dic[title])
        li.append((score_dic[title], title)) # (예측평점, 영화제목)
        
    li.sort(reverse=True)
    
    print('\n** Recommendation')
    for rating, score in enumerate(li):
        print(rating+1, '등: ', score[1], ', ', score[0], '점')
    
    return li
get_KNNmeans_recommend(ratings_expand, '소이현')
Out:
...

** Recommendation
1 등: 꾼 , 3.765320026911711 점
2 등: 택시운전사 , 3.0487901210241017 점
3 등: 킹스맨:골든서클 , 1.9561449029750133 점

 

 

 

 

 

 

 

 

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