구글 코랩을 사용했기 때문에 아래 코드를 통해 드라이브 접근 권한을 허용합니다.
from google.colab import drive
drive.mount('/content/drive')
일단 기본적으로 필요한 모듈을 불러옵니다.
import pandas as pd
import numpy as np
import re
import math
import matplotlib.pyplot as plt
import seaborn as sns
데이터는 캐글이든, 다른 곳이든 어디에서나 볼 수 있는 영화 평점 데이터를 사용했습니다.
rating_data = pd.read_csv('/content/drive/My Drive/Colab Notebooks/RS/ratings.csv')
movie_data = pd.read_csv('/content/drive/My Drive/Colab Notebooks/RS/movies.csv')
평점 데이터와 영화 정보 데이터를 살펴봅시다.
print(rating_data.shape)
rating_data.head()
print(movie_data.shape)
movie_data.head()
위의 두 데이터를 합쳐보겠습니다. 이때 판다스의 merge 함수를 사용하면 특정 컬럼 기준으로 세로로 합쳐줍니다. 이때 어떤 데이터를 기준으로 할지에 따라 merge() 안에 먼저 써주면 됩니다. 저는 rating_data 를 기준으로 했습니다.
df_combined = pd.merge(rating_data, movie_data, on='movieId')
print(df_combined.shape)
df_combined.head()
시각화를 통해 데이터를 자세히 살펴보려고 합니다. 먼저 장르 분포를 알아보기 위해 아래 함수를 통해 "|" 기호를 제거합니다.
genres = {}
def find_genres():
for genre in movie_data['genres']:
words = genre.split('|')
for word in words:
genres[word] = genres.get(word, 0) + 1 # dictionary 안에 word가 없으면 0으로 초기화, 이후 반복되면 + 1
find_genres()
plt.figure(figsize=(13, 5))
key_list = []
value_list = []
for key, value in genres.items():
key_list.append(key)
value_list.append(value)
plt.bar(range(len(genres)), value_list)
plt.xticks(range(len(genres)), key_list, rotation=50)
plt.show()
드라마, 코미디, 스릴러, 액션, 로맨스 순으로 많은 영화가 존재하네요.
아래는 영화별로 평균 평점이 얼마인지, 총 몇 명이 평점을 매겼는지 확인한 것입니다.
df_n_ratings = pd.DataFrame(df_combined.groupby('title')['rating'].mean())
df_n_ratings['total ratings'] = pd.DataFrame(df_combined.groupby('title')['rating'].count())
df_n_ratings.rename(columns={'rating': 'mean_rating'}, inplace=True)
df_n_ratings.sort_values('total ratings', ascending=False).head(10)
# 많은 영화의 평균 평점이 3~4점 주변에 위치함
plt.figure(figsize=(8, 4))
sns.displot(df_n_ratings['mean_rating'], bins=30, kde=True)
plt.xlabel('Mean Ratings')
plt.ylabel('Probability')
plt.show()
sns.jointplot(x = 'mean_rating', y = 'total ratings', data = df_n_ratings)
위 그래프의 각 노드는 영화를 가리키며, total ratings(y축)를 살펴보면 대부분의 영화가 50개 미만의 평점을 받았고, mean_rating을 살펴보면 대체적으로 3~4점 사이의 평점을 기록하고 있음을 알 수 있다.
본격적으로 추천 모델을 생성해보겠습니다. 저는 사이킷런의 surprise 모듈을 사용했고, 설치가 안되어있다면 아래 명령어를 통해 설치해줍니다.
!pip install scikit-surprise
from surprise import Reader, Dataset, SVD
from surprise.model_selection import GridSearchCV, cross_validate
surprise의 함수 Reader, Dataset을 통해 모델에 사용할 데이터 형식을 맞춰줍니다. 파라미터는 공식 홈페이지에서 참고해주세요.
Reader: 데이터 형식이 아래 구조를 따라야 한다
user ; item ; rating ; [timestamp]
reader = Reader(rating_scale=(0.5, 5)) # 소수점 없는 평점이라면 (1, 5)
data = Dataset.load_from_df(rating_data[['userId', 'movieId', 'rating']], reader=reader)
surprise의 순수 SVD() 함수를 사용하여 교차 검증으로 성능(RMSE 기준)을 측정해봅시다.
algo = SVD()
cross_validate(algo=algo, data=data, measures=['RMSE'], cv=5, verbose=True)
정말 간단한 그리드서치 탐색을 해봅니다. 파라미터로 더 많은 것들을 넣을 수 있으며 더 다양한 파라미터는 surprise 공식 홈페이지를 참고해주세요.
param_grid = {'n_factors': [50, 75], 'lr_all': [0.5, 0.05], 'reg_all': [0.06, 0.04]}
gs = GridSearchCV(algo_class=SVD, measures=['RMSE'], param_grid=param_grid)
gs.fit(data)
print('\n###################')
print('Best Score :', gs.best_score['rmse'])
print('Best Parameters :', gs.best_params['rmse'])
print('#####################')
참고로 교차 검증 이외에 아래 코드를 통해 SVD를 학습하고 테스트셋에 대해 평가를 하는 방법도 있습니다. surprise 공식 홈페이지에 잘 나와있습니다.
from surprise import accuracy
from surprise.model_selection import train_test_split
trainset, testset = train_test_split(data, test_size=.25)
algo.fit(trainset)
predictions = algo.test(testset)
# predictions = algo.fit(trainset).test(testset)
accuracy.rmse(predictions)
서프라이즈 패키지가 잘 구성되어 있기 때문에 초반 추천 시스템을 구축하는데 많은 도움이 될 것 같습니다^^ 위에서는 SVD 모델만 사용했는데 이외에도 아래의 다양한 모델을 사용해볼 수 있습니다.
prediction_algorithms package — Surprise 1 documentation
'문돌이 존버 > 데이터 분석' 카테고리의 다른 글
앙상블 학습 - 부스팅(boosting) 알고리즘 개념 잡기 (0) | 2021.04.10 |
---|---|
파이썬, pandas 일자 및 시간 처리 방법, feat. dt (0) | 2021.04.06 |
핸즈온 머신러닝 2 연습 문제 코드(Decision Tree) (0) | 2021.01.03 |
핸즈온 머신러닝 2 복습하기(챕터 6: 결정 트리) (0) | 2021.01.03 |
Contents based recommendation system 파이썬 예제 (0) | 2020.11.17 |