본문 바로가기
주홍의 프로젝트/연습 프로젝트

surprise 라이브러리 algorithm 정리

by orangecode 2022. 6. 29.
728x90
surprise library

surprise library는 python 언어에 기반하여 추천시스템 구현에 특화된 라이브러리 입니다.

scikit-learn API와 비슷한 형태로 제공하는 라이브러리이기도 합니다.

 

Surprise library process : [data loading → model set & train  predict & test]

 1) data loading

  • surprise library를 제작할 때, movielens라는 open DataSet을 이용하여 만들어진 추천시스템입니다.
  • 그러므로 movielens의 format과 같은 'user - item - rating'의 순서에 맞추어 동일하게 맞추어야 라이브러리가 작동하게 됩니다

 2) model set & train

  • surprise library 모델은 KNN(최근접이웃) & SVD(잠재요인) & Baseline 등의 모델을 선정하고 학습합니다.

 3) predict & test

  • 학습된 모델을 이용해서 test 데이터로 예측하고 평가합니다.
surprise algorithm

https://surprise.readthedocs.io/en/stable/prediction_algorithms_package.html

1. Basic algorithms

 ① NormalPredictor

  • NormalPredictor algorithm은 정규화된 trainset을 기반으로 무작위로 점수를 예측합니다. 
  • 가장 기본적인 알고리즘 중 하나

 ② BaselineOnly

  • BaselineOnly algorithm은 주어진 사용자/아이템을 기반으로 추정치를 예측합니다.
  • user_id, content_id 2개의 요소(x값)만으로  평점의 예측치를 계산합니다.
  • 다중 선형회귀 모델(x가 여러개 존재하는 y값 추정하는 모델)
  • μ : 전페 평점 평균
  • b(u) :동일한 사용자에 의한 평점 조정값
  • b(i) : 동일 상품에 대한 평점 조정값  

 

 

2.  K-NN(K-Nearest Neighbor, 최근접이웃) algorithms

KNN은 item/user vector와 유사한 k개의 vector를 이용하여 user의 rating을 예측하는 방법입니다.

 ① KNNBASIC

  • KNNBASIC은 Collaborative filtering 의 기본적인 알고리즘입니다.

algo = KNNBasic(k=40, min_k=1, sim_options={'name': 'msd', 'user_based': True})
  • k : 가까운 이웃의 최대 개수(기본값 :  40개)
  • min_k : 이웃의 최소 개수, 유사도가 0보다 큰 이웃의 최소 개수에 미달할 경우 평점의 기본값으로 추정
  • sim_options : 'user_based' = True or 'item_based' = False 

 

② KNNWithMeans

  • KNNWithMeans은 Collaborative filtering 의 기본적인 알고리즘으로, 각 유저의 평균 점수를 이용합니다.

 

 ③ KNNWithZScore

  • KNNWithZScore은 Collaborative filtering 의 기본적인 알고리즘으로, 각 유저의 z-score 정규화를 이용합니다.
  • Z-score(표준화 점수) : 평균과 표준편차가 정의되었을 때, 데이터가 얼마나 벗어나있는지 측정하는 지표                    데이터의 평균, 표준편차를 계산하여 데이터 평균을 0.0, 표준편차를 1.0으로 만드는 기법입니다.                                  Z-score ↑ 이상치 가능성↑

 

 ④ KNNBaseline

  • KNNBaseline은 Collaborative filtering 의 기본적인 알고리즘으로, Baseline rating을 이용합니다.

 

3. matrix Factorization-based algorithms(use pivot table)

 ① SVD 

  • 가장 유명한 SVD algorithm으로 baseline을 사용하지 않고 확률적 행렬분해를 이용합니다.
  • 사용자 특성, 상품 특성 N개의 요인 중 공통점이 많을수록 평점은 높아집니다.

 ② SVDpp(SVD++)

  • implicit rating을 포함하여 SVD를 계산한 확장형 SVD algorithm
  • implicit data : 유저가 간접적으로 선호,취향을 나타내는 데이터 ex) 검색기록, 구매내역, 장바구니 등... NOPE
  • SVDpp의 imlicit data란 user가 1~5점의 평점을 주어진 행위 자체가 user의 implicit data를 포함하기 때문에 user의 implicit data가 있다고 생각하고 미리 계산하는 방식

 ③ NMF

  • 마이너스 값(부정적인 값)이 없는 Non-negative Matrix Factorization을 베이스로 이용하는 collaborative filtering algorithm

 ④ Slope One

  • new item/ other item 중 사용자 선호의 평균적인 값의 차이에 기반해서 새로운 선호를 추정하는 algorithm입니다.
  • 단순 선호 추정, 유사도 측정 X
  • A, B사이의 평점 차이 계산 ((4.0-2.0)+(3.0-4.0))/2 = +1.0
  해리포터 반지의 제왕
사용자 A 4.0 2.0
사용자 B 3.0 4.0
사용자 C 평가하지 않음
예측평점 : 3.0 _ 1.0 = 4.0
3.0

 ⑤ Co-clustering -...??

  • 공동 클러스터링(군집)기반의 collaborative filtering algorithm
  • Co-clustering : 공동 군집 분석

 

dataset으로 surprise algorithms 평가하기(rmse, mse)

dataset = ml-100k, ml-1m, jester 3개 고정

 

1. 라이브러리 설정하기

# 라이브러리 설정
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import time
import datetime
import random

import numpy as np
import six
from tabulate import tabulate

from surprise import Dataset
from surprise.model_selection import cross_validate
from surprise.model_selection import KFold
from surprise import NormalPredictor, BaselineOnly, KNNBasic, KNNWithMeans, KNNBaseline, SVD, SVDpp, NMF, SlopeOne, CoClustering

 

2. cross-validate할 class 설정하기(성능평가 classes 설정)

# The algorithms to cross-validate
classes = (SVD, SVDpp, NMF, SlopeOne, KNNBasic, KNNWithMeans, KNNBaseline,
           CoClustering, BaselineOnly, NormalPredictor)

surprise algorithm인 SVD, SVDpp, NMF, SlopeOne, KNNBasic, KNNWithMeans, KNNBaseline, CoClustering, BaselineOnly, NormalPredictor을 모두 class에 넣어줍니다.

 

 

3. surprise link로 algorithms 불러오기

# ugly dict to map algo names and datasets to their markdown links in the table
stable = 'http://surprise.readthedocs.io/en/stable/'
LINK = {'SVD': '[{}]({})'.format('SVD',
                                 stable +
                                 'matrix_factorization.html#surprise.prediction_algorithms.matrix_factorization.SVD'),
        'SVDpp': '[{}]({})'.format('SVD++',
                                   stable +
                                   'matrix_factorization.html#surprise.prediction_algorithms.matrix_factorization.SVDpp'),
        'NMF': '[{}]({})'.format('NMF',
                                 stable +
                                 'matrix_factorizaytion.html#surprise.prediction_algorithms.matrix_factorization.NMF'),
        'SlopeOne': '[{}]({})'.format('Slope One',
                                      stable +
                                      'slope_one.html#surprise.prediction_algorithms.slope_one.SlopeOne'),
        'KNNBasic': '[{}]({})'.format('k-NN',
                                      stable +
                                      'knn_inspired.html#surprise.prediction_algorithms.knns.KNNBasic'),
        'KNNWithMeans': '[{}]({})'.format('Centered k-NN',
                                          stable +
                                          'knn_inspired.html#surprise.prediction_algorithms.knns.KNNWithMeans'),
        'KNNBaseline': '[{}]({})'.format('k-NN Baseline',
                                         stable +
                                         'knn_inspired.html#surprise.prediction_algorithms.knns.KNNBaseline'),
        'CoClustering': '[{}]({})'.format('Co-Clustering',
                                          stable +
                                          'co_clustering.html#surprise.prediction_algorithms.co_clustering.CoClustering'),
        'BaselineOnly': '[{}]({})'.format('Baseline',
                                          stable +
                                          'basic_algorithms.html#surprise.prediction_algorithms.baseline_only.BaselineOnly'),
        'NormalPredictor': '[{}]({})'.format('Random',
                                             stable +
                                             'basic_algorithms.html#surprise.prediction_algorithms.random_pred.NormalPredictor'),
        ### ml-100k, ml-1m, jester 3가지 데이터 셋만 가능
        # surprise 라이브러리 고정
        # 고치려면 surprise/dataset.py 소스코드를 변경해야함
        'ml-100k': '[{}]({})'.format('Movielens 100k',
                                     'http://grouplens.org/datasets/movielens/100k'),
        'ml-1m': '[{}]({})'.format('Movielens 1M',
                                   'http://grouplens.org/datasets/movielens/1m'),
        }
### ml-100k, ml-1m, jester 3가지 데이터 셋만 가능
        # surprise 라이브러리 고정
        # 고치려면 surprise/dataset.py 소스코드를 변경해야함
        'ml-100k': '[{}]({})'.format('Movielens 100k',
                                     'http://grouplens.org/datasets/movielens/100k'),
        'ml-1m': '[{}]({})'.format('Movielens 1M',
                                   'http://grouplens.org/datasets/movielens/1m'),

알고리즘을 불러오는 링크 뒤에는 dataset을 불러오는 링크를 작성합니다.

 

surprise의 dataset.py에는 ml-100k, ml-1m, jester dataset을 불러오는 기능이 내장되어 있습니다.

ml-100k, ml-1m, jester

ml-100k

ml-1m

: user_id, item_id, rating, timestamp 4개 data가 담긴 dataset

 

jester 

: video_id, label, frames, label_id, shape, format 6개 data가 담긴 jester

 

위 3개가 아닌 다른 링크나 폴더를 불러오기 할 경우, 알 수 없는 dataset이라고하며 오류가 발생하게 됩니다.

 

4. seed 값 고정하기

# 시드 고정 = 모든 알고리즘이 같은 데이터로 성능평가하기 위함
# set RNG
np.random.seed(0)
random.seed(0)

random값 seed를 고정합니다.

 

모든 알고리즘이 같은 데이터와 순서로 성능평가할 수 있도록 seed값을 고정합니다.

 

5. 데이터셋 불러오기

dataset = 'ml-100k'
data = Dataset.load_builtin(dataset)
kf = KFold(random_state=0)  # folds will be the same for all algorithms.

 

6. algorithm cross-validate & view

table = []
for klass in classes:
    start = time.time()
    out = cross_validate(klass(), data, ['rmse', 'mae'], kf)
    cv_time = str(datetime.timedelta(seconds=int(time.time() - start)))
    link = LINK[klass.__name__]
    mean_rmse = '{:.3f}'.format(np.mean(out['test_rmse']))
    mean_mae = '{:.3f}'.format(np.mean(out['test_mae']))

    new_line = [link, mean_rmse, mean_mae, cv_time]
    print(tabulate([new_line], tablefmt="pipe"))  # print current algo perf
    table.append(new_line)

header = [LINK[dataset],
          'RMSE',
          'MAE',
          'Time']
print(tabulate(table, header, tablefmt="pipe"))

 

반응형

댓글