우노
[추천시스템] 유사도(Similarity) 튜토리얼 본문
영화 평점 데이터셋
- https://www.kaggle.com/rounakbanik/the-movies-dataset
- Kaggle에서 notebook 환경 제공
데이터셋 정보
- ratings.csv : 평점데이터
- ratings_small.csv : 평점데이터 (작은버전)
- keywords.csv : 영화 키워드 데이터
- movies_metadata.csv : 영화 정보 데이터
- credits.csv : 영화 제작 정보
- links.csv : imdb와 tmdb에서의 영화 id 정보
- links_small.csv : imdb와 tmdb에서의 영화 id 정보 (작은버전)
목표
- The Movies Data를 이용하여 비슷한 영화 찾기
순서
- Pandas를 이용하여 데이터 불러오기
- Pandas를 이용하여 데이터 정제하기
- Python의 집합을 활용하여 Jaccard 유사도구하기
- Numpy를 이용하여 Pearson 유사도구하기
- 모든 영화에 대해서 추천 점수 계산하고, 가장 추천 점수가 높은 영화 고르기
Pandas
Python 용 데이터 분석 라이브러리
MS Office의 Excel과 같이, 행과 열로 구성된 데이터 객체를 다룸
Pandas 사용하기
Pandas, Numpy 패키지 import
import numpy as np import pandas as pd
Pandas로 Metadata(영화정보데이터) 읽고 정제하기
metadata 읽기
meta = pd.read_csv('/kaggle/input/movies_metadata.csv') meta
metadata에서 필요한 열(column)만 추려내기
meta = meta[ ['id', 'original_title', 'original_language', 'genres'] ] meta
열(column) 이름 변경하기
row를 바꾸기 위해선 rows
column을 바꾸기 위해선 columns
meta = meta.rename(columns={'id':'movieId','original_title': 'title', 'original_language': 'language'}) meta
language가 'en'인 데이터만 솎아내기
loc은 location의 약자
loc 인자의 앞은 행이름 뒤는 열이름, iloc 인자의 앞은 행번호 뒤는 열번호를 넣는다.
meta = meta.loc[meta['language'] == 'en',:] meta
movieId의 타입을 숫자로 변경하기
meta.movieId = pd.to_numeric(meta.movieId) meta.movieId
genre 정제하기
json string을 입력 받아 장르 이름들을 python set으로 변환해 출력하는 함수 구현
def str_to_set(x): genre_set = set() # eval : text 파일을 python 데이터 타입으로 변환해준다. for item in eval(x): genre_set.add(item['name']) return genre_set
meta.genres 열의 모든 값에 str_to_set 함수 적용
meta.genres = meta.genres.apply(str_to_set) meta
Pandas로 Keywords(영화키워드데이터) 읽고 정제하기
keywords 데이터 읽기
keywords = pd.read_csv('/kaggle/input/keywords.csv') keywords
keywords.keywords 열의 모든 값에 str_to_set 함수 적용
keywords.keywords = keywords.keywords.apply(str_to_set) keywords
id 열을 movieId로 이름 변경하고 데이터 타입을 숫자로 변환하기
keywords = keywords.rename(columns={'id': 'movieId'}) keywords.movieId = pd.to_numeric(keywords.movieId) keywords
Keywords와 Metadata 합치기
같은 movieId를 갖는 데이터 합치기
meta = pd.merge(meta, keywords, on='movieId', how='inner') meta
Jaccard Similarity
영화 찾아보고 합쳐서 출력해보기
# title : 'The Dark Knight'인 여러행들 중 첫행 dk = meta.loc[meta.title == 'The Dark Knight'].iloc[0] # title : 'The Dark Knight Rises'인 여러행들 중 첫행 dkr = meta.loc[meta.title == 'The Dark Knight Rises'].iloc[0] # axis=0 하나의 열에 이어 붙이기, axis=1 하나의 행에 이어 붙이기 # concat 후 전치행렬 적용 pd.concat([dk, dkr], axis=1).T
Jaccard 유사도 함수 구현 및 실행
def jaccard_similarity(s1, s2): # 분모가 0이면 계산할 수 없기 때문에 s1s2 합집합의 크기가 0인 경우 return 0 if len(s1|s2) == 0: return 0 # 아닌 경우 교집합/합집합 반환 return len(s1&s2)/len(s1|s2) # dk의 장르와 키워드 합치기 # dkr의 장르와 키워드 합치기 # 두 가지를 자카드 유사도 계산 jaccard_similarity(dk.genres|dk.keywords, dkr.genres|dkr.keywords)
Pandas로 Rating(평점데이터) 읽고 정제하기
Rating 데이터 읽기
ratings = pd.read_csv('/kaggle/input/ratings_small.csv') ratings
Rating의 movieId를 숫자로 변환
ratings.movieId = pd.to_numeric(ratings.movieId)
metadata에서 같은 movieId를 갖는 데이터와 합쳐 ratings 데이터에 title 달아주기
ratings = pd.merge(ratings, meta[['movieId', 'title']], on='movieId', how='inner') ratings
pivot table 만들기 (데이터를 요약하는 통계표)
행은 userID, 열은 title , 요소값은 rating
matrix = ratings.pivot_table(index= 'userId', columns='title', values='rating') matrix
Pearson Correlation Coefficient
Pearson 유사도 함수 구현 (Centered Cosine Similarity)
def pearson_similarity(u1, u2): # Centered 방식 : 각 벡터에서 NaN이 아닌 값들을 평균낸 값을 빼준다. u1_c = u1 - u1.mean() u2_c = u2 - u2.mean() # 여기서부턴 Cosine 유사도 구하기 denom = np.sqrt(np.sum(u1_c ** 2) * np.sum(u2_c ** 2)) if denom != 0: return np.sum(u1_c * u2_c)/denom else: return 0
The Dark Knight와 Prom Night의 Pearson 유사도 구해보기
# dk 평점 벡터 dk_rating = matrix['The Dark Knight'] # pk 평점 벡터 pk_rating = matrix['Prom Night'] # dk와 pk의 pearson similarity pearson_similarity(dk_rating, pk_rating)
비슷한 영화 추천 기능 구현
# input_title : 찾으려고 하는 영화의 제목
# matrix : 이전에 생성한 pivot table
# n : 가장 비슷한 영화를 몇 개 출력할 것인지
# a : 유사도 반영 비율
def find_similar_movies (input_title , matrix, n, alpha):
# input_meta : 입력된 영화의 metadata (id, title, language, genres, keywords)
# input_set : 입력된 영화의 genres와 keyword의 합집합
input_meta = meta.loc[meta[ 'title'] == input_title].iloc[0]
input_set = input_meta.genres | input_meta.keywords
# result : 모든 영화마다 유사도를 계산하여 저장
# 입력된 영화와 동일한 영화는 유사도 계산을 하지 않고 Pass
result = []
# pivot table에서 영화이름을 하나씩 비교
for this_title in matrix.columns:
if this_title == input_title:
continue
# this_meta : 유사도를 계산하려는 영화의 metadata (id, title, language, genres, keywords)
# this_set : 이 영화의 genres와 keywords의 합집합
this_meta = meta.loc[ meta[ 'title'] == this_title].iloc[ 0]
this_set = this_meta.genres | this_meta.keywords
# pearson : 입력 영화와 이번 영화의 pearson 유사도 결과 (영화에 대한 평점 벡터로 계산)
# jaccard : 입력 영화와 이번 영화의 jaccard 유사도 결과 (키워드 집합으로 계산)
pearson = pearson_similarity(matrix[this_title], matrix[input_title])
jaccard = jaccard_similarity(this_set, input_set)
# score : pearson 점수와 jaccard 점수의 가중치 합
# result에 계산 결과 추가
score = alpha * pearson + ( 1-alpha) * jaccard
result.append( (this_title, pearson, jaccard, score) )
# 모든 영화에 대해 유사도 계산이 끝나면 result를 정렬
# 상위 n개를 return
result.sort(key= lambda r: r[3], reverse= True)
return result[:n]
The Dark Knight와 비슷한 영화 추천해보기
# 상위 10개 / pearson에 0.7 / jaccard에 0.3 result = find_similar_movies('The Dark Knight', matrix, 10, 0.3) # 데이터프레임으로 시각화 pd.DataFrame(result, columns = ['title', 'pearson', 'jaccard', 'score'])
'Data > Recommender System' 카테고리의 다른 글
[추천 시스템] Tensor Decomposition (0) | 2021.12.04 |
---|---|
[추천시스템] Latent Factor Model with Pytorch (0) | 2020.10.29 |
[추천시스템] Latent Factor Model (1) | 2020.10.29 |
[추천시스템] 추천 시스템의 개념 및 알고리즘 (0) | 2020.10.16 |
[추천시스템] 유사도(Similarity) 구하는 방법 (0) | 2020.10.12 |