[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🛠 기타/Data & AI

기초 추천시스템 - 사용자 기반 협업 필터링

inu 2020. 8. 23. 14:00
반응형
  • 협업 필터링은 다른 사용자들을 통해 현 사용자의 취향을 추측한다.
  • 협업 필터링은 사용자 기반 필터링과 아이템 기반 필터링으로 나뉘어진다.
  • 먼저 사용자 기반 협업 필터링을 사용해보겠다.

데이터 불러오고 처리하기

import pandas as pd
import numpy as np

ratings = pd.read_csv('../data/ratings.csv')
movies = pd.read_csv('../data/movies.csv')

pd.set_option('display.max_columns', 6)
pd.set_option('display.width', 300)

movie_ratings = pd.merge(ratings, movies, on='movieId')
print(movie_ratings)

title_user = movie_ratings.pivot_table('rating', index='userId', columns='title')
title_user.fillna(0, inplace=True)
  • pivot_table을 이용해 유저의 영화별 평점을 담은 데이터프레임을 만든다.(title_user)
  • 결측치는 0으로 처리한다.

유사도 측정 및 비슷한 유저 찾기

from sklearn.metrics.pairwise import cosine_similarity

# 유저와 유저 간의 유사도
user_based_collab = cosine_similarity(title_user, title_user)
user_based_collab = pd.DataFrame(user_based_collab, index=title_user.index, columns=title_user.index)

print(user_based_collab[1].sort_values(ascending=False)[:10])
userId
1      1.000000
325    0.371852
634    0.194093
341    0.162819
310    0.157524
207    0.152746
35     0.130585
195    0.122647
485    0.114021
130    0.112817
Name: 1, dtype: float64
  • 방금 만들어놓은 유저의 영화별 평점 데이터프레임(title_user)을 활용해 각 유저간 유사도를 측정한다
  • 유저 수 X 유저 수 크기의 유저간 유사도 데이터프레임이 생성된다.(user_based_collab)
  • 해당 유사도는 2차원 리스트형태로 되어 있다. 따라서 이를 사용하기 용이하도록 데이터프레임 형태로 바꿨다.
  • 첫번째 유저를 뽑아 내림차순으로 정리하고, 그 중 상위 10개를 가져왔다.

가장 비슷한 유저가 감상한 영화 추천

# 가장 유사한 유저를 뽑아서 해당 유저가 본 영화 추천
user = user_based_collab[1].sort_values(ascending=False)[:10].index[1]
print(title_user.loc[user].sort_values(ascending=False))
title
Dangerous Minds (1995)                  4.5
Beverly Hills Cop (1984)                4.5
Brady Bunch Movie, The (1995)           4.0
My Best Friend's Wedding (1997)         4.0
Star Trek: The Motion Picture (1979)    3.5
                                       ... 
Patriot Games (1992)                    0.0
Patriot, The (2000)                     0.0
Patton (1970)                           0.0
Paul (2011)                             0.0
"Great Performances" Cats (1998)        0.0
Name: 325, Length: 9064, dtype: float64

별점 예측하기

# 1번 유저와 유사한 유저들 9명을 뽑아서, 그 유저들이 어떤 영화에 대해서 부여한 평점에
# 유사도만큼의 가중치를 부여해서 이걸 토대로 1번 유저가 부여할 평점을 계산/예측
# 가중치 -> 유저 9명 유사도의 합 중에서 해당 유저가 차지하는 유사도
user_index_list = user_based_collab[1].sort_values(ascending=False)[:10].index.tolist()
user_weight_list = user_based_collab[1].sort_values(ascending=False)[:10].tolist()
  • 유저간 유사도를 담고있는 user_based_collab에서 첫번째 유저의 다른 유저들에 대한 유사도들을 내림차순으로 정렬하고, 이 인덱스를 뽑아 리스트화 한다. (즉, 각 유저의 ID, [1, 325, 634, 341, 310, 207, 35, 195, 485, 130])
  • 유사도도 내림차순으로 정렬후 리스트화한다. (즉, 각 유저와의 유사도, [1.0, 0.3718515795200445, 0.19409305170790575, 0.16281928881328767, 0.1575243302750048, 0.15274612900892096, 0.13058496348265256, 0.12264701454037472, 0.11402063453702121, 0.11281730419223501])
  • 여기서 'sum(user_weight_list)-1'를 한다면 이는 곧 첫번째 유저와 유사한 유저들의 유사도를 전부 더한 것이 된다.
# 유사한 유저들조차도 평점을 부여하지 않았다면
# 1번 유저가 부여할 평점을 예측할 수 없다...
movie_title = 'Dark Knight, The (2008)'
weighted_sum = []
for i in range(1, 10):
    weighted_sum.append(title_user[movie_title][user_index_list[i]] * user_weight_list[i]/(sum(user_weight_list)-1))
print(sum(weighted_sum))
  • 'Dark Knight, The (2008)'에 대한 예측 별점을 측정하고자한다.
  • weighted_sum에 (각 유저의 해당영화에 대한 평점) * (첫번째 유저와의 유사도) / (전체 유사도합) 을 더한뒤
  • 마지막에 sum으로 값을 출력한다.
  • 결과는 0.0이 나온다. 왜 그렇게 될까?
  • 첫번째 유저와 마찬가지로 첫번째 유저와 유사한 9명의 유저들도 'Dark Knight, The (2008)'를 보지 않았기 때문이다. (평점이 0)
  • 즉, 사용자 기반 협업 필터링은 비슷한 영화를 찾을 수는 있지만 점수예측에는 적합하지 않다.
반응형