또르르's 개발 Story

[24-3] 추천 시스템 using surprise library 본문

부스트캠프 AI 테크 U stage/실습

[24-3] 추천 시스템 using surprise library

또르르21 2021. 2. 26. 02:23

1️⃣ 설정

 

Surprise 모듈을 설치합니다.

!pip install surprise

필요한 모듈을 import 합니다.

import numpy as np 

import pandas as pd

from surprise import SVD          # 잠재 인수 모형

from surprise.model_selection import train_test_split

from surprise.dataset import DatasetAutoFolds

from surprise.model_selection import cross_validate     # hyperparameter 찾기

from surprise import Dataset, Reader

from surprise import accuracy

 

 

2️⃣ 데이터셋 확인하기

 

데이터셋은 다음과 같은 형태로 구성되어 있습니다.

 

  • ratings.csv : 평점
### Rating Dataset Format ###

   userId  movieId  rating  timestamp
0       1        1     4.0  964982703
1       1        3     4.0  964981247
2       1        6     4.0  964982224
3       1       47     5.0  964983815
4       1       50     5.0  964982931
  • movies.csv : 영화
### Movie Dataset Format ###

Columns of Movie Dataset :  Index(['movieId', 'title', 'genres'], dtype='object')

   movieId  ...                                       genres
0        1  ...  Adventure|Animation|Children|Comedy|Fantasy
1        2  ...                   Adventure|Children|Fantasy
2        3  ...                               Comedy|Romance
3        4  ...                         Comedy|Drama|Romance
4        5  ...                                       Comedy

 

Dataset의 User, Movie 수를 확인하면 다음과 같습니다.

n_users = df_ratings.userId.unique().shape[0]   # user의 유일한 값의 개수

n_items = df_ratings.movieId.unique().shape[0]


>>> print("num users: {}, num items:{}".format(n_users, n_items))

num users: 611, num items:9724

 

 

3️⃣ 데이터 전처리

 

영화 ID로부터 영화 제목을 얻는 과정입니다.

movie_set = set()     

ratings = np.zeros((n_users, n_items))

for (_, movie_id, _) in df_ratings.itertuples(index=False):

    movie_set.add(movie_id)
    

movie_id_to_name=dict()

movie_id_to_genre=dict()


for (movie_id, movie_name, movie_genre) in df_movies.itertuples(index=False):

    if movie_id not in movie_set:              # 어떤 영화가 rating data에 없는 경우 skip
    
        continue
        
    movie_id_to_name[movie_id] = movie_name 
    
    movie_id_to_genre[movie_id] = movie_genre

pandas dataframe을 surprise dataset 형태로 바꿔준 후, train set과 test set을 split 해줍니다.

reader = Reader(rating_scale=(0, 5)) # rating_scale : 평점의 최저점과 최고점

data = Dataset.load_from_df(df_ratings[['userId','movieId','rating']], reader=reader)   # 사용자 col, movie col, 점수 col


train, test = train_test_split(data, test_size=0.2, shuffle=True)   # 훈련 데이터, test 데이터를 나눠줌

Grid Search를 위해 surprise.trainset 형태의 데이터를 surprise.dataset으로 변경해줍니다.

iterator = train.all_ratings()

train_df = pd.DataFrame(columns=['userId', 'movieId', 'rating'])

i = 0

for (uid, iid, rating) in iterator:

    train_df.loc[i] = [train.to_raw_uid(int(uid)), train.to_raw_iid(iid), rating]
    
    i = i+1
    

train_data = Dataset.load_from_df(train_df, reader=reader)

 

 

4️⃣ 모델 설정 및 학습

 

Grid Search를 통해 최적의 Hyperparameter를 찾는 과정입니다.

from surprise.model_selection import GridSearchCV

param_grid = {'n_factors': [10,15,20,30,50,100]}     # embedding 공간의 차원 6개를 시도할 것 

다음 위의 param_grid의 6개 차원을 하나씩 돌려본 후 제일 좋은 성능의 차원으로 선택해서 hyperparameter를 설정합니다.

# SVD 모형 사용

# 탐색을 할 때 기준점으로 rmse, mae를 사용, rmse, mae를 기준으로 제일 좋은 hyperparameter 차원을 선택

# cv : cross validation을 할 때 4등분을 함

grid = GridSearchCV(SVD, param_grid, measures = ['rmse', 'mae'], cv=4)

grid.fit(train_data)

 

여기서 CV = 4는 Cross-validation의 약자로 다음과 같은 역할을 수행합니다.

평가 데이터를 제외한 train data 중 4등분을 해서 hyperparameter를 결정하는 방법입니다.

 

https://www.edwith.org/bcaitech1

 

1,2,3번 데이터를 SVD train 시켜서 4번 데이터 평점을 가지고 가장 좋은 hyperparameter을 결정합니다.

한 번만 하는 것이 아닌,

  • 1,2,3 -> 4번
  • 1,2,4 -> 3번
  • 2,3,4 -> 1번
  • 1,3,4 -> 2번

총 4번을 각각 수행합니다.

Cross-validation을 사용하면 많은 데이터를 기반으로 학습을 할 수 있습니다.

 

가장 좋은 차원과 그때의 score는 다음과 같습니다.

>>> print(grid.best_params['rmse'])

{'n_factors': 10}


>>> print(grid.best_score['rmse'])

0.8760004730634828

이후, 가장 좋았던 차원을 입력으로 SVD를 사용해 학습시킵니다.

# grid.best_params['rmse']['n_factors'] : rmse 기준 가장 좋았던 차원을 입력으로 SVD에 집어넣음

# 이후 SVD로 학습을 수행

algorithm = SVD(grid.best_params['rmse']['n_factors'])

algorithm.fit(train)

 

 

5️⃣ 모델 예측

 

모델 예측은 SVD의 test 함수를 사용해 test data를 집어넣고 예측합니다.

prediction = algorithm.test(test)

for p in prediction[:5]:            # prediction 결과값 5개 미리보기

    print(p)                        # r_ui : 실제 rating 값, est: 예측된 rating 값
    
    

user: 318        item: 48         r_ui = 3.50   est = 3.34   {'was_impossible': False}
user: 68         item: 6944       r_ui = 4.50   est = 3.28   {'was_impossible': False}
user: 414        item: 5553       r_ui = 4.00   est = 3.17   {'was_impossible': False}
user: 298        item: 8641       r_ui = 4.00   est = 2.41   {'was_impossible': False}
user: 600        item: 1371       r_ui = 3.50   est = 2.60   {'was_impossible': False}

새로운 user, 새로운 item에 대한 prediction 값을 다음과 같이 구할 수 있습니다.

uid = 800

iid = 8368

prediction_user_item = algorithm.predict(uid, iid)    # 학습한 객체로 predict 수행


>>> print(prediction_user_item)   

user: 800        item: 8368       r_ui = None   est = 3.98   {'was_impossible': False}
Comments