온라인 강의 자료모음 기업교육

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

이해하기 쉽고, 장황하지 않은 자료를 기반으로 강의를 진행합니다.
AI · 풀스택 · 데이터 로드맵 Dave Lee 한 강사가 설계부터 강의까지 모두
사이트 바로가기

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

  • 사용자들 간의 유사도를 바탕으로 모든 항목에 대해 예측 값을 계산하고 높은 예측 값을 갖는 상위 N개의 추천 목록을 생성한다.

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

  • 유사도가 구해지면 평점을 예측하고자 하는 사용자(또는 상품)와 유사도가 큰 k 개의 사용자(또는 상품) 벡터를 사용하여 가중 평균을 구해서 가중치를 예측

4.1. KNNBasic

  • 평점들을 단순히 가중 평균한다. 다음 식에서 $N^k$는 $k$개의 가장 유사도가 큰 벡터의 집합이다.
$$ \hat{r}_{ui} = \frac{ \sum\limits_{v \in N^k_i(u)} \text{sim}(u, v) \cdot r_{vi}} {\sum\limits_{v \in N^k_i(u)} \text{sim}(u, v)} $$

또는 $$ \hat{r}_{ui} = \frac{ \sum\limits_{j \in N^k_u(i)} \text{sim}(i, j) \cdot r_{uj}} {\sum\limits_{j \in N^k_u(j)} \text{sim}(i, j)} $$

  1. 대상과 가장 유사도가 높은 k의 대상의 영화 평점과 유사도를 통해 추측평점((유사도 x (타인의)영화평점)을 구한다.

  2. 추측평점의 총합을 구한 후,

  3. 추측평점 총합계/유사도 합계를 통해 예상평점을 뽑아낼 수 있다.

데이터 분석 기초를 체계적으로 익힐 수 있는 온라인 강의입니다

처음하는 파이썬 데이터 분석

pandas, plotly 시각화, 데이터 전처리 기본

In [27]:
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
    }
}
In [32]:
def getRecommendation (data, person, k=3, sim_function=sim_pearson):
    
    result = top_match(data, person, k)
    
    score = 0 # 평점 합을 위한 변수
    li = list() # 리턴을 위한 리스트
    score_dic = dict() # 유사도 총합을 위한 dic
    sim_dic = dict() # 평점 총합을 위한 dic

    for sim, name in result: # 튜플이므로 한번에
        print(sim, name)
        if sim < 0 : continue #유사도가 양수인 사람만
        for movie in data[name]: 
            if movie not in data[person]: #name이 평가를 내리지 않은 영화
                score += sim * data[name][movie] # 그사람의 영화평점 * 유사도
                score_dic.setdefault(movie, 0) # 기본값 설정
                score_dic[movie] += score # 합계 구함

                # 조건에 맞는 사람의 유사도의 누적합을 구한다
                sim_dic.setdefault(movie, 0) 
                sim_dic[movie] += sim

            score = 0  #영화가 바뀌었으니 초기화한다
    
    for key in score_dic: 
        score_dic[key] = score_dic[key] / sim_dic[key] # 평점 총합/ 유사도 총합
        li.append((score_dic[key],key)) # list((tuple))의 리턴을 위해서.
    li.sort() #오름차순
    li.reverse() #내림차순
    return li
In [33]:
getRecommendation(ratings_expand, '소이현')
0.9330597055272909 홍수환
0.8909876971472571 최홍만
0.8452277090445156 나원탁
Out[33]:
[(3.675468553454334, '꾼'),
 (3.0000000000000004, '택시운전사'),
 (1.976934805357391, '킹스맨:골든서클')]
* getRecommendation를 사용해서 최홍만 과 가장 유사한 사용자는?
* 단 유사도 함수를 sim_cosine, k는 2를 사용하시오
In [50]:
getRecommendation(ratings_expand, '최홍만', k=2, sim_function=sim_cosine)
0.9608329054174726 홍수환
0.9517663735117331 소이현
Out[50]:
[(3.0, '택시운전사')]

데이터 분석/과학 전문가가 되기 위한 체계적인 로드맵입니다

가장 빠른 데이터 분석/과학 풀로드맵 (2025)

데이터 수집 → 분석 → 머신러닝/딥러닝 전과정

4.2. KNNWithMeans

  • 평점들을 평균값 기준으로 가중 평균한다.
$$ \hat{r}_{ui} = \mu_u + \frac{ \sum\limits_{v \in N^k_i(u)} \text{sim}(u, v) \cdot (r_{vi} - \mu_v)} {\sum\limits_{v \in N^k_i(u)} \text{sim}(u, v)} $$

또는 $$ \hat{r}_{ui} = \mu_i + \frac{ \sum\limits_{j \in N^k_u(i)} \text{sim}(i, j) \cdot (r_{uj} - \mu_j)} {\sum\limits_{j \in N^k_u(i)} \text{sim}(i, j)} $$

In [34]:
for name in ratings_expand:
    sum = 0
    count = 0
    for movies in ratings_expand[name]:
        sum += ratings_expand[name][movies]
        count += 1
    ratings_expand[name]['avg'] = sum / count
In [35]:
ratings_expand
Out[35]:
{'나원탁': {'avg': 3.7,
  '꾼': 3.0,
  '남한산성': 4.0,
  '범죄도시': 5.0,
  '아이 캔 스피크': 3.5,
  '택시운전사': 3.0},
 '마동석': {'avg': 2.8333333333333335,
  '꾼': 3.0,
  '남한산성': 1.5,
  '범죄도시': 3.5,
  '아이 캔 스피크': 2.5,
  '킹스맨:골든서클': 3.0,
  '택시운전사': 3.5},
 '설경구': {'avg': 3.5, '꾼': 4.0, '남한산성': 3.0, '범죄도시': 4.5, '택시운전사': 2.5},
 '소이현': {'avg': 3.1666666666666665, '남한산성': 4.5, '범죄도시': 4.0, '아이 캔 스피크': 1.0},
 '윤계상': {'avg': 2.75,
  '꾼': 3.0,
  '남한산성': 2.5,
  '범죄도시': 3.0,
  '아이 캔 스피크': 3.5,
  '킹스맨:골든서클': 1.5,
  '택시운전사': 3.0},
 '이정재': {'avg': 3.5,
  '꾼': 5.0,
  '남한산성': 4.5,
  '범죄도시': 1.5,
  '아이 캔 스피크': 4.5,
  '킹스맨:골든서클': 0.5,
  '택시운전사': 5.0},
 '최홍만': {'avg': 3.5,
  '꾼': 4.5,
  '남한산성': 4.5,
  '범죄도시': 3.0,
  '아이 캔 스피크': 2.5,
  '킹스맨:골든서클': 3.0},
 '홍수환': {'avg': 2.75,
  '꾼': 3.5,
  '남한산성': 4.0,
  '범죄도시': 3.0,
  '아이 캔 스피크': 2.0,
  '킹스맨:골든서클': 1.0,
  '택시운전사': 3.0}}
In [41]:
def getRecommendation (data, person, k=3, sim_function=sim_pearson):
    
    result = top_match(data, person, k)
    
    score = 0 # 평점 합을 위한 변수
    li = list() # 리턴을 위한 리스트
    score_dic = dict() # 유사도 총합을 위한 dic
    sim_dic = dict() # 평점 총합을 위한 dic

    for sim, name in result: # 튜플이므로 한번에
        print(sim, name)
        if sim < 0 : continue #유사도가 양수인 사람만
        for movie in data[name]: 
            if movie not in data[person]: #name이 평가를 내리지 않은 영화
                score += sim * (data[name][movie] - data[name]['avg']) # 그사람의 영화평점 * 유사도
                score_dic.setdefault(movie, 0) # 기본값 설정
                score_dic[movie] += score # 합계 구함

                # 조건에 맞는 사람의 유사도의 누적합을 구한다
                sim_dic.setdefault(movie, 0) 
                sim_dic[movie] += sim

            score = 0  #영화가 바뀌었으니 초기화한다
    
    for key in score_dic: 
        score_dic[key] = data[person]['avg'] + (score_dic[key] / sim_dic[key]) # 평점 총합/ 유사도 총합
        li.append((score_dic[key],key)) # list((tuple))의 리턴을 위해서.
    li.sort() #오름차순
    li.reverse() #내림차순
    return li
In [42]:
getRecommendation(ratings_expand, '소이현')
0.9661614876137535 홍수환
0.9438405255065262 최홍만
0.9437757473484876 설경구
Out[42]:
[(3.91667234143915, '꾼'),
 (2.7989920841776077, '택시운전사'),
 (2.0343626946178484, '킹스맨:골든서클')]

풀스택 개발자가 되기 위한 체계적인 로드맵입니다

가장 빠른 풀스택 개발 로드맵 (2025)

파이썬 → Flask → FastAPI → Flutter 전과정

* getRecommendation를 사용해서 최홍만 과 가장 유사한 사용자는?
* 단 유사도 함수를 sim_cosine, k는 2를 사용하시오
In [43]:
getRecommendation(ratings_expand, '최홍만', k=2, sim_function=sim_cosine)
0.9652129842456227 홍수환
0.9438405255065262 소이현
Out[43]:
[(3.75, '택시운전사')]

여기까지가 추천시스템에서 사용되는 가장 일반적인 방법임