[회고] 신입 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)
  • 즉, 사용자 기반 협업 필터링은 비슷한 영화를 찾을 수는 있지만 점수예측에는 적합하지 않다.