추천 시스템 개요 및 이론, Surprise Package

Page content

I. 개요

  • 대고객 대상으로 한 대부분의 플랫폼 서비스 업체들은 고객 개개인에게 맞춤형의 추천 서비스를 도입하고 있음
    • 전자상거래 업체, 유투브, 애플 뮤직 등
  • ML의 여러 알고리즘 중 비즈니스 관점에 부합하는 기법이 추천 시스템.
  • 추천 시스템의 진정한 묘미는 사용자 본인도 모르는 취향 발견, 재구매로 연결하도록 설계
  • 누가 필요할까?
    • 모든 플랫폼 서비스
    • 이유1: 플랫폼은 다수의 판매자와 소비자를 필요로 함, 문제는 카테고리와 메뉴구성이 복잡해지면 소비자의 제품 선택에 부작용
    • 이유2: 만족도가 떨어지면 고객은 그 플랫폼을 떠날 가능성이 크며, 이는 플랫폼 서비스의 매출 하락과 직결
    • 모든 플랫폼 서비스는 기본적으로 추천서비스를 장착하고 싶어함
  • 영화 데이터를 기준으로 추천시스템을 단계별로 구현함을 목표로 함

II. 추천시스템의 유형 및 역사

  • 추천시스템의 유형과 간단한 역사에 대해 배워보도록 한다.

(1) 유형

  • 크게 세가지로 구분됨.
    • Demographic Filtering
    • 콘텐츠 기반 필터링 (Content Filtering)
    • 협업 필터링 (Collaborative Filtering)
      • 최근접 이웃(Nearest Neighbor)
      • 잠재 요인(Latent Factor)

(2) 역사

  • 초창기: 콘텐츠 기반 필터링 또는 최근접 이웃 기반 협업 필터링이 주로 사용됨.
  • 중기: 넷플릭스 추천 시스템 경연 대회에서 행렬 분해 (Matrix Factorization) 기법을 이용한 잠재요인 협업 필터링 방식으로 우승한 뒤, 유명해짐.
  • 최근: 개인화 특성을 강화하기 위해서 하이브리드 형식으로 콘텐츠 기반과 협업 기반을 적절히 결합해 사용하는 경우도 늘고 있음

III. Surprise 패키지

  • Surprise 패키지는 추천시스템 패키지이다.
  • 설치 방법은 다음 문서를 참조하기를 바란다.
  • 해당 패키지를 활용하면 보다 쉽게 API를 활용해서 추천 시스템을 구축할 수 있다.
    • 다양한 추천 알고리즘들이 해당 패키지에 내재되어 있다.

(1) 패키지 개요

  • 영어 원문에는 아래와 같이 설명되어 있다.

    • Give users perfect control over their experiments. To this end, a strong emphasis is laid on documentation, which we have tried to make as clear and precise as possible by pointing out every detail of the algorithms.

    • Alleviate the pain of Dataset handling. Users can use both built-in datasets (Movielens, Jester), and their own custom datasets.

    • Provide various ready-to-use prediction algorithms such as baseline algorithms, neighborhood methods, matrix factorization-based ( SVD, PMF, SVD++, NMF), and many others. Also, various similarity measures (cosine, MSD, pearson…) are built-in.

    • 위 글에서 보는 것처럼, 최근접이웃과 같은 다양한 알고리즘들이 내장되어 있는 것을 볼 수 있다.

    • Make it easy to implement new algorithm ideas.

    • Provide tools to evaluate, analyse and compare the algorithms performance. Cross-validation procedures can be run very easily using powerful CV iterators (inspired by scikit-learn excellent tools), as well as exhaustive search over a set of parameters.

  • Surprise 패키지의 API는 사이킷런의 핵심 API와 유사하다.

(2) 데이터 수집

(3) 데이터 설명

  • 여러 데이터들이 있는데, 관련 내용은 캐글 본문의 것을 그대로 사용한다.
    • movies_metadata.csv: The main Movies Metadata file. Contains information on 45,000 movies featured in the Full MovieLens dataset. Features include posters, backdrops, budget, revenue, release dates, languages, production countries and companies.
    • keywords.csv: Contains the movie plot keywords for our MovieLens movies. Available in the form of a stringified JSON Object.
    • credits.csv: Consists of Cast and Crew Information for all our movies. Available in the form of a stringified JSON Object.
    • links.csv: The file that contains the TMDB and IMDB IDs of all the movies featured in the Full MovieLens dataset.
    • links_small.csv: Contains the TMDB and IMDB IDs of a small subset of 9,000 movies of the Full Dataset.
    • ratings_small.csv: The subset of 100,000 ratings from 700 users on 9,000 movies.

(4) 구글 드라이브와 연동

  • 구글 드라이브와 연동하여 pandas를 활용하여 데이터를 수집한다.
  • 구글 드라이브와 연동하는 방법에 대해서는 Colab + Drive + Github Workflow에서 확인한다.
from google.colab import drive # 패키지 불러오기 
from os.path import join  

ROOT = "/content/drive"     # 드라이브 기본 경로
print(ROOT)                 # print content of ROOT (Optional)
drive.mount(ROOT)           # 드라이브 기본 경로 Mount

MY_GOOGLE_DRIVE_PATH = 'My Drive/Colab Notebooks/your/path' # 프로젝트 경로
PROJECT_PATH = join(ROOT, MY_GOOGLE_DRIVE_PATH) # 프로젝트 경로
print(PROJECT_PATH)
/content/drive
Go to this URL in a browser: https://accounts.google.com/o/oauth2/auth?client_id=947318989803-6bn6q

Enter your authorization code:
··········
Mounted at /content/drive
/content/drive/My Drive/Colab Notebooks/your/path
%cd "{PROJECT_PATH}"
/content/drive/My Drive/Colab Notebooks/inflearn/Python/Kaggle_Edu/05_recommendation
!ls
data  source
import pandas as pd
import pandas_profiling
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
import seaborn as sns

from IPython.core.display import display, HTML
from pandas_profiling import ProfileReport
/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
  • 이번에 수집하는 데이터는 사용자 ID와 영화 ID를 기반으로 한 ratings_small 데이터를 활용한다.
import pandas as pd
ratings = pd.read_csv('data/ratings_small.csv', low_memory=False)
print(ratings.head(3))
   userId  movieId  rating   timestamp
0       1       31     2.5  1260759144
1       1     1029     3.0  1260759179
2       1     1061     3.0  1260759182

IV. 협업 필터링

  • 추천시스템에서 획기적인 전기를 마련한 회사가 넷플릭스 회사이다.
  • 크게 두가지 방식이 있다.
    • 최근접 이웃 협업 필터링
    • 잠재 요인 협업 필터링
  • 본 포스트에서는 잠재 요인 협업 필터링에 대해 기술하고 활용한다.

(1) 잠재 요인 협업 필터링의 이해

  • 사용자-아이템 평점 매트릭스 속에 숨어 있는 잠재 요인을 추출해 추천 예측을 할 수 있게 하는 기법
  • 이 때 문제는 잠재 요인이 어떤 것인지는 명확히 정의할 수 없음.
  • 다음 수식을 본다.

$$ R \approx PQ^{2} $$

  • R: m명의 사용자들의 n개의 아이템에 대한 평점 행렬

  • P: m명의 사용자와 k요인에 대한 관계 행렬

  • Q: n개의 아이템과 k요인에 대한 관계 행렬

    • 여기에서 문제는 Matrix Factorization을 활용하려면 기본적으로 모든 사용자들이 모든 상품들에 대해 평점을 매기는 조건이 필요하다.
    • 그런데, 현실적으로 행렬 R은 대부분 희소 행렬(Sparse Matrix)이 된다.
    • 이렇게 다차원의 희소 행렬인 사용자-아이템 행렬 데이터를 저차원의 밀집 행렬의 사용자-잠재요인 행렬과, 아이템-잠재요인 행렬로 분해한 뒤, 두 행렬의 내적(뜻: 벡터의 곱으로 결과값이 상수를 가짐)을 통해 새로운 예측 사용자-아이템 평점 행렬 데이터를 만들어서 사용자가 아직 평점을 부여하지 않는 아이템에 대한 예측 평점을 생성하는 것이 잠재 요인 협력 필터링 알고리즘의 뜻
    • 이 때 사용되는 기법이 SVD, NMF 등이 있음.

(2) Simple Code

  • Surprise 패키지에는 이러한 알고리즘이 내재되어 있다. 필수 모듈을 설치한 뒤 간단하게 구현해본다.
  • 우선 Reader() 클래스를 활용한다.
  • Reader()는 다음과 같은 구조를 필요로

user ; item ; rating ; [timestamp]

  • 만약 위와 같이 되어 있지 않다면 데이터를 수정해야 한다. Reader 형태로 데이터를 변환하는 의미로 생각하면 된다.
  • Dataset.load_from_df는 판다스의 DataFrame에서 데이터를 로딩한다. 마찬가지로 반드시 3개의 칼럼인 사용자 아이디, 아이템 아이디, 평점 순으로 칼럼 순서가 정해야 있어야 하며, Reader로 파일의 포맷을 지정한다.
!pip install scikit-surprise
Collecting scikit-surprise
[?25l  Downloading https://files.pythonhosted.org/packages/f5/da/b5700d96495fb4f092be497f02492768a3d96a3f4fa2ae7dea46d4081cfa/scikit-surprise-1.1.0.tar.gz (6.4MB)
     |████████████████████████████████| 6.5MB 6.2MB/s 
[?25hRequirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise) (0.15.1)
Requirement already satisfied: numpy>=1.11.2 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise) (1.18.5)
Requirement already satisfied: scipy>=1.0.0 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise) (1.4.1)
Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from scikit-surprise) (1.12.0)
Building wheels for collected packages: scikit-surprise
  Building wheel for scikit-surprise (setup.py) ... [?25l[?25hdone
  Created wheel for scikit-surprise: filename=scikit_surprise-1.1.0-cp36-cp36m-linux_x86_64.whl size=1675756 sha256=a46585f526f96128004380c19928286fd41a0c79822adcd4ed9717572e0ffd93
  Stored in directory: /root/.cache/pip/wheels/cc/fa/8c/16c93fccce688ae1bde7d979ff102f7bee980d9cfeb8641bcf
Successfully built scikit-surprise
Installing collected packages: scikit-surprise
Successfully installed scikit-surprise-1.1.0
from surprise import Reader, Dataset, SVD, accuracy
from surprise.model_selection import cross_validate
from surprise.model_selection import train_test_split

# 데이터 변환
reader = Reader()
data = Dataset.load_from_df(ratings[['userId', 'movieId', 'rating']], reader)

# 훈련 및 테스트 데이터 분리
trainset, testset = train_test_split(data, test_size=.25, random_state=0)
# SVD 모형 학습
algo = SVD()
algo.fit(trainset)
<surprise.prediction_algorithms.matrix_factorization.SVD at 0x7f0d4ae07d68>
# 모형 테스트 
predictions = algo.test(testset)
print('prediction tpe:', type(predictions), ' size:', len(predictions))
print('prediction 결과의 최초 5개 추출')
predictions[:5]
prediction tpe: <class 'list'>  size: 25001
prediction 결과의 최초 5개 추출





[Prediction(uid=30, iid=254, r_ui=3.0, est=3.6828864855270904, details={'was_impossible': False}),
 Prediction(uid=652, iid=2626, r_ui=5.0, est=4.362735526468264, details={'was_impossible': False}),
 Prediction(uid=466, iid=2174, r_ui=3.0, est=3.5455084961831242, details={'was_impossible': False}),
 Prediction(uid=561, iid=3438, r_ui=4.5, est=3.2762003911403488, details={'was_impossible': False}),
 Prediction(uid=529, iid=34552, r_ui=4.0, est=3.3783094948422194, details={'was_impossible': False})]
  • uid는 사용자의 아이디
  • iid는 영화(또는 아이템) 아이다
  • r_ui는 실제 평점 정보
  • est는 예측 평점

(3) 예측

  • predict() 함수를 활용하여 추천 평점 예측을 해본다.
uid = str(194)
iid = str(302)
pred = algo.predict(uid, iid)
print(pred)
user: 194        item: 302        r_ui = None   est = 3.54   {'was_impossible': False}
svd.predict(1, 302, 3)
Prediction(uid=1, iid=302, r_ui=3, est=2.8156662920105133, details={'was_impossible': False})

(4) 모형 평가

  • SurpriseAccuracy 모듈은 RMSE, MSE 등의 방법으로 추천 시스템의 성능 평가 제공함
accuracy.rmse(predictions)
RMSE: 0.8910





0.8909960969933768

V. Conclusion

  • 이론은 다소 복잡할 지 모르지만, 구현하는 것은 생각보다 어려운 것은 아니다.
  • 각 함수에 맞춰서 데이터셋만 맞춰주면 된다. (물론 이게 어렵다)
  • 개별 알고리즘에 대한 이론 공부는 병행해야 하지만, Manual에 사실 설명서가 다 있기 때문에, 크게 걱정을 하지 않았으면 좋겠다.
  • 아직 끝이 아니다. 여기서 추가적으로 진행하지 못한 부분은 metadata와 합친 것은 아니기 때문이다. 이러한 부분은 추후에 전체 리뷰 시 다시 기술하겠다.
    • 별도의 함수를 만들어야 하는 작업이 있는데, 이에 대한 추가 설명이 필요하다. (Comming Soon)

VI. Reference

Ibtesama. (2020, May 16). Getting Started with a Movie Recommendation System. Retrieved June 21, 2020, from https://www.kaggle.com/ibtesama/getting-started-with-a-movie-recommendation-system