추천 시스템이란?
이전 내용
Surprise 패키지
◆ Surprise 패키지
파이썬 기반의 추천 시스템 구축을 위한 전용 패키지 중 하나로, 파이썬 기반에서 사이킷런과 유사한 API 와 프레임워크를 제공한다. 따라서 추천 시스템의 전반적인 알고리즘을 이해하고 사이킷런 사용 경험이 있으면 쉽게 사용할 수 있다.
[Surprise 패키지의 주요 장점]
- 다양한 추천 알고리즘을 쉽게 적용해 추천 시스템을 구축할 수 있다.
- 다양한 추천 알고리즘의 예: 사용자 또는 아이템 기반 최근접 이웃 협업 필터링, SVD, SVD++, NMF 기반의 잠재 요인 협업 필터링 등
- Surprise의 핵심 API는 사이킷런의 핵심 API와 유사한 API명으로 작성되었다.
- API 예: fit(), predict() API로 추천 데이터 학습과 예측, train_test_split() 으로 추천 학습 데이터 세트와 예측 데이터 세트 분리, cross_validate(), GridSearchCV 클래스를 통해 추천 시스템을 위한 모델 셀렉션, 평가, 하이퍼 파라미터 튜닝 등 제공
[Surprise 패키지 사이트]
https://surprise.readthedocs.io/en/stable/
[Surprise 패키지 설치]
Surprise는 conda나 pip를 통해 설치할 수 있다.
- conda - Anaconda Prompt
- pip - Prompt 창 (cmd 를 실행할 때 '관리자 권한으로 실행')
conda install -c conda-forge scikit-surprise
중간에 proceed [Y/N] 이 나오면 'Y' 를 입력하면 설치(Done)가 완료된다.
<Windows Prompt>
pip install scikit-surprise
※ Windows에 Surprise 패키지를 설치할 때는 Microsoft Visual Studio Build Tools 2015 이상의 버전이 필요하다.
[다운로드 방법 안내]
Surprise를 이용한 추천 시스템 구축
◆ 간단한 예제를 통한 사용법 익히기
아래 예제는 추천 데이터를 학습용과 테스트용 데이터 세트로 분리한 뒤 SVD 행렬 분해를 통한 잠재 요인 협업 필터링을 수행한다.
[Surprise 관련 모듈 임포트, 추천을 위한 데이터 세트 로딩]
Surprise에서 데이터 로딩은 Dataset 클래스를 이용해서만 가능하며, Surprise는 Movie Lens 데이터 세트의 사용자-영화 평점 데이터 포맷과 같이 userId(사용자 ID), movieId(영화 ID), rating(평점)과 같은 주요 데이터가 로우 레벨 형태로 돼있는 포맷의 데이터만 처리한다.
Surprise는 무비렌즈 사이트에서 제공하는 과거 버전의 데이터 세트를 가져오는 API를 제공한다.
Surprise Dataset 클래스의 load_builtin()은 무비렌즈 사이트에서 제공하는 과거 버전 데이터 세트인 'ml-100k'(10만 개의 평점 데이터) 또는 'ml-lm'(100만 개 평점 데이터) 데이터를 아카이브 사이트로부터 내려받아 로컬 디렉터리에 저장한 뒤 이를 로딩한다.
이렇게 로딩한 데이터 세트를 Surprise 패키지의 train_test_split() API를 이용해 학습 데이터 세트와 테스트 데이터 세트로 분리한다.
from surprise import SVD
from surprise import Dataset
from surprise import accuracy
from surprise.model_selection import train_test_split
data = Dataset.load_builtin('ml-100k')
# 수행 시마다 동일하게 데이터를 분할하기 위해 random_state 값 부여
trainset, testset = train_test_split(data, test_size=.25, random_state=0)
처음에 load_builtin('ml-100k') 을 적용할 경우 로컬 디렉터리에 데이터가 없기 때문에 무비렌즈 사이트에서 내려받을 것인지를 물어보는데, 상자 내에 'Y'를 입력하면 된다.
내려 받기가 완료되면 데이터가 저장된 디렉터리가 표시되며, 한 번 내려받아 로컬 디렉터리에 데이터가 저장된 후에는 Dataset.load_builtin('ml-100k')를 호출하면 무비렌즈 사이트에 접속하지 않고 저장된 데이터 세트를 로딩한다.
Surprise에서 사용하는 'ml-100k' 데이터 세트는 무비렌즈 사이트에서 직접 내려 받은 movies.csv, ratings.csv 파일과는 차이가 있는데, 해당 파일들은 최근 영화에 대한 평점 정보를 가지고 있으나, Surprise의 데이터 세트는 과거 버전의 데이터 세트이다.
또한, ratings.csv 파일은 칼럼 분리 문자가 콤마(,)인 csv 파일이지만, 과거 버전의 데이터 파일은 분리 문자가 탭(\t) 문자 이다.
Surprise는 자체적으로 로우 레벨의 데이터를 칼럼 레벨의 데이터로 변경하므로 원본인 로우 레벨의 사용자-아이템 평점 데이터를 데이터 세트로 적용해야 한다.
[SVD로 잠재 요인 협업 필터링 수행]
적용하는 데이터 세트는 앞에서 train_test_split()으로 분리된 학습 데이터 세트이다.
먼저 algo = SVD()와 같이 알고리즘 객체를 생성하고, 이 알고리즘 객체에 fit(학습 데이터 세트)을 수행해 학습 데이터 세트 기반으로 추천 알고리즘을 학습한다.
algo = SVD(random_state=0)
algo.fit(trainset)
[학습된 추천 알고리즘을 기반으로 테스트 데이터 세트에 대해 추천 수행]
Surprise에서 추천을 예측하는 메서드는 test()와 predict() 두 개이다.
- test(): 사용자-아이템 평점 데이터 세트 전체에 대해 추천을 예측하는 메서드 (입력된 데이터 세트에 대해 추천 데이터 세트 생성)
- predict(): 개별 사용자와 영화에 대한 추천 평점 반환
1. test() 메서드 수행
테스트 데이터 세트 전체에 대해 추천 영화 평점 데이터를 생성한 뒤 최초 5개만 추출
predictions = algo.test( testset )
print('prediction type :', type(predictions), 'size: ', len(predictions))
print('prediction 결과의 최초 5개 추출')
predictions[:5]
▶
- SVD 알고리즘 객체의 test(테스트 세트) 메서드의 호출 결과는 파이썬 리스트.
- 크기는 입력 인자 데이터 세트의 크기와 같은 25,000개
- 호출 결과로 반환된 리스트 객체는 25,000 개의 Prediction 객체를 내부에 가지고 있음.
- Prediction 객체는 Surprise 패키지에서 제공하는 데이터 타입이며, 개별 사용자 아이디(uid), 영화(또는 아이템) 아이디(iid), 실제 평점(r_ui) 정보에 기반해 Surprise의 추천 예측 평점(est) 데이터를 튜플 형태로 가지고 있음.
- Prediction 객체의 details 속성은 내부 처리 시 추천 예측을 할 수 없는 경우에 로그용으로 데이터를 남기는 데 사용.
- 'was_impossible'이 True 이면 예측값을 생성할 수 없는 데이터라는 의미.
리스트 객체 내에 내포된 Prediction 객체의 uid, iid, r_ui, est 등의 속성에 접근하려면 객체명.uid와 같은 형식으로 가능하다.
# 3개의 Prediction 객체에서 uid, iid, est 속성 추출
[ (pred.uid, pred.iid, pred.est) for pred in predictions[:3] ]
test() 메서드는 입력 데이터 세트의 모든 사용자와 아이템 아이디에 대해서 predict()를 반복적으로 수행한 결과라고 볼 수 있다.
2. predict() 메서드 수행
인자로 개별 사용자 아이디, 아이템 아이디를 입력하면 추천 예측 평점을 포함한 정보 반환
(기존 평점 정보(r_ui)는 선택 사항이며 사용자 아이디, 아이템 아이디는 문자열로 입력해야 한다)
# 사용자 아이디, 아이템 아이디는 문자열로 입력
uid = str(196)
iid = str(302)
pred = algo.predict(uid, iid)
print(pred)
predict() 는 개별 사용자와 아이템 정보를 입력하면 추천 예측 평점을 est로 반환한다.
[테스트 데이터 세트를 이용하여 추천 예측 평점과 실제 평점과의 차이 평가]
Surprise의 accuracy 모듈은 RMSE, MSE 등의 방법으로 추천 시스템의 성능 평가 정보를 제공한다.
accuracy.rmse(predictions)
Surprise 주요 모듈 소개
1. Dataset
Surprise는 user_id(사용자 아이디), item_id(아이템 아이디), rating(평점) 데이터가 로우 레벨로 된 데이터 세트에만 적용할 수 있어 네 번째 칼럼부터는 아예 로딩을 수행하지 않는다.
예를 들어 user_id, item_id, rating, time_stamp 필드로 구분된 데이터라면 앞 3개 필터만 로딩하고 이후 time_stamp 필드는 로딩에서 제외된다.
무비렌즈 아카이브 서버에서 자동으로 내려받는 데이터 파일뿐만 아니라 일반 데이터 파일이나 판다스 DataFrame에서도 로딩할 수 있다. 단, 데이터 세트의 칼럼 순서가 사용자 아이디, 아이템 아이디, 평점 순으로 반드시 돼 있어야 한다.
API 명 | 내용 |
Dataset.load_builtin (name='ml-100k') |
무비렌즈 아카이브 FTP 서버에서 무비렌즈 데이터를 내려받는다. ml-100k, ml-1M을 내려받을 수 있다. 내려받은 데이터는 .surprise_data 디렉터리 밑에 저장되고, 해당 디렉터리에 데이터가 있으면 FTP에서 내려받지 않고 해당 데이터를 이용한다. 입력 파라미터인 name으로 대상 데이터가 ml-100k인지 ml-1M 인지를 입력한다. (디폴트는 ml-100k) |
Dataset.load_from_file (file_path, reader) |
OS 파일에서 데이터를 로딩할 때 사용. 콤마, 탭 등으로 칼럼이 분리된 포맷의 OS 파일에서 데이터 로딩. 입력 파라미터로 OS 파일명, Reader로 파일의 포맷 지정. |
Dataset.load_from_df (df, reader) |
판다스의 DataFrame 에서 데이터 로딩. 파라미터로 DataFrame 을 입력받으며 DataFrame 역시 반드시 3개의 칼럼인 사용자 아이디, 아이템 아이디, 평점 순으로 칼럼 순서가 정해져 있어야 한다. 입력 파라미터로 DataFrame 객체, Reader 로 파일의 포맷 지정. |
2. OS 파일 데이터를 Surprise 데이터 세트로 로딩
Surprise에 OS 파일을 로딩할 때는 로딩되는 데이터 파일에 칼럼명을 가지는 헤더 문자열이 있어서는 안 된다. ratings.csv 파일은 맨 처음 위치에 칼럼명을 헤더로 가지고 있어 판다스 DataFrame의 to_csv() 함수를 이용해 간단하게 이 칼럼 헤더를 삭제하고 새로운 파일은 ratings_noh.csv로 저장한다.
import pandas as pd
ratings = pd.read_csv("C:/Users/niceq/Documents/DataScience/Python ML Guide/Data/09. ml-latest-small/ratings.csv")
# ratings_noh.csv 파일로 언로드 시 인덱스와 헤더를 모두 제거한 새로운 파일 생성
ratings.to_csv("C:/Users/niceq/Documents/DataScience/Python ML Guide/Data/09. ml-latest-small/ratings_noh.csv", index=False, header=False)
[새로운 파일을 DataSet로 로드]
새롭게 생성한 ratings_noh.csv 파일을 DataSet 모듈의 load_from_file()을 이용해 DataSet로 로드한다.
로드하기 전에 Redader 클래스를 이용해 데이터 파일의 파싱 포맷을 정의해야 한다.
※ Redader 클래스는 로딩될 파일의 파싱 정보를 알려주기 위해 사용된다.
현재 로딩하려는 rating_noh.csv 는 칼럼 헤더가 없고, 4개의 칼럼이 콤마로만 분리돼 있다. 이 4개의 칼럼이 사용자 아이디, 아이템 아이디, 평점, 타임스탬프 임을 로딩할 때 알려줘야 한다.
Reader 클래스의 생성자에 각 필드의 칼럼명과 칼럼 분리문자, 최소~최대 평점을 입력해 객체를 생성하고, load_from_file()로 생성된 Reader 객체를 참조해 데이터 파일을 파싱하면서 로딩한다.
from surprise import Dataset
from surprise import Reader
reader = Reader(line_format='user item rating timestamp', sep=',', rating_scale=(0.5, 5))
data = Dataset.load_from_file("C:/Users/niceq/Documents/DataScience/Python ML Guide/Data/09. ml-latest-small/ratings_noh.csv", reader=reader)
◆ Reader 클래스의 주요 생성 파라미터
- line_format(string): 칼럼을 순서대로 나열. 입력된 문자열을 공백으로 분리해 칼럼으로 인식
- sep(char): 칼럼을 분리하는 분리자이며, 디폴트는 '\t'. 판다스 DataFrame에서 입력받을 경우에는 기재 불필요
- rating_scale(tuple, optional): 평점 값의 최소~최대 평점 설정. 디폴트는 (1, 5). ratings.csv 파일의 경우 (0.5, 5)
[SVD 행렬 분해 기법을 이용한 추천 예측]
잠재 요인 크기 K 값을 나타내는 파라미터인 n_factors를 50으로 설정해 데이터를 학습한 뒤에 테스트 데이터 세트를 적용해 예측 평점을 구하고, 예측 평점과 실제 평점 데이터를 RMSE로 평가.
from surprise import SVD
from surprise import accuracy
from surprise.model_selection import train_test_split
trainset, testset = train_test_split(data, test_size=.25, random_state=0)
# 수행 시마다 동일한 결과를 도출하기 위해 random_state 설정
algo = SVD(n_factors=50, random_state=0)
# 학습 데이터 세트로 학습하고 나서 테스트 데이터 세트로 평점 예측 후 RMSE 평가
algo.fit(trainset)
predictions = algo.test( testset )
accuracy.rmse(predictions)
3. 판다스 DataFrame 에서 Surprise 데이터 세트로 로딩
Dataset.load_from_df()를 이용하면 판다스의 DataFrame 에서도 Surprise 데이터 세트로 로딩할 수 있다.
주의할 점은 DataFrame 역시 사용자 아이디, 아이템 아이디, 평점 칼럼 순서를 지켜야 한다는 것이다.
ratings.csv 파일을 DataFrame으로 로딩한 ratings 에서 Surprise 데이터 세트로 로딩하려면
Dataset.load_from_df(ratings[['userId', 'movieId', 'rating']], reader)
상단과 같이파라미터를 입력하면 된다.
[Dataset.load_from_df() 를 이용한 SVD 추천 예측 코드]
import pandas as pd
from surprise import Reader, Dataset
ratings = pd.read_csv("C:/Users/niceq/Documents/DataScience/Python ML Guide/Data/09. ml-latest-small/ratings.csv")
reader = Reader(rating_scale=(0.5, 5))
# ratings DataFrame에서 칼럼은 사용자 아이디, 아이템 아이디, 평점 순서를 지켜야 함
data = Dataset.load_from_df(ratings[['userId', 'movieId', 'rating']], reader)
trainset, testset = train_test_split(data, test_size=.25, random_state=0)
algo = SVD(n_factors=50, random_state=0)
algo.fit(trainset)
predictions = algo.test( testset )
accuracy.rmse(predictions)
Surprise 추천 알고리즘 클래스
[추천 알고리즘 클래스]
클래스명 | 설명 |
SVD | 행렬 분해를 통한 잠재 요인 협업 필터링을 위한 SVD 알고리즘 |
KNNBasic | 최근접 이웃 협업 필터링을 위한 KNN 알고리즘 |
BaselineOnly | 사용자 Bias 와 아이템 Bias를 감안한 SGD 베이스라인 알고리즘 |
이 밖에도 SVD++, NMF 등 다양한 유형의 알고리즘이 있으며, 보다 자세한 내용은 (글 상단 링크) Surprise 사이트를 참고하면 된다.
[SVD 클래스 입력 파라미터]
파라미터명 | 내용 |
n_factors | 잠재 요인 K 개수. 디폴트는 100. 커질수록 정확도가 높아질 수 있으나 과적합 문제 발생 가능성 있음. |
n_epochs | SGD(Stochastic Gradient Descent) 수행 시 반복 횟수. 디폴트는 20. |
biased (bool) | 베이스라인 사용자 편향 적용 여부. 디폴트는 True |
주로 n_factor와 n_epochs 의 값을 변경해 튜닝할 수 있으나 튜닝 효과는 크지 않다.
[추천 알고리즘 예측 성능 벤치마크 결과]
하단 사이트에서 보다 자세히 확인할 수 있다.
다음은 5-폴드 교차 검증 절차에서 다양한 알고리즘(기본 매개변수 포함)의 평균 RMSE, MAE 및 총 실행 시간이이다. 데이터 세트는 Movielens 100k 및 1M 데이터 세트이며, 폴드는 모든 알고리즘에서 동일하하다. 모든 실험은 인텔 i5 11세대 2.60GHz가 장착된 노트북에서 실행된다. 이러한 표를 생성하는 코드는 벤치마크 예제 에서 찾을 수 있다 .
SVD | 0.934 | 0.737 | 0:00:06 |
SVD++ (캐시 평가=거짓) | 0.919 | 0.721 | 0:01:39 |
SVD++ (캐시 등급=참) | 0.919 | 0.721 | 0:01:22 |
국립환경과학연구센터(NIMF) | 0.963 | 0.758 | 0:00:06 |
Slope One | 0.946 | 0.743 | 0:00:09 |
k-NN | 0.98 | 0.774 | 0:00:08 |
Centered k-NN | 0.951 | 0.749 | 0:00:09 |
k-NN Baseline | 0.931 | 0.733 | 0:00:13 |
Co-Clustering | 0.963 | 0.753 | 0:00:06 |
Baseline | 0.944 | 0.748 | 0:00:02 |
Radom | 1.518 | 1.219 | 0:00:01 |
[베이스라인 평점]
◆ 베이스라인 평점:
개인의 성향을 반영해 아이템 평가해 편향성(Bias) 요소를 반영하여 펑점을 부과하는 것.
보통 베이스라인 평점은 전체 평균 평점 + 사용자 편향 점수 + 아이템 편향 점수 공식으로 계산된다.
- 전체 평균 평점 = 모든 사용자의 아이템에 대한 평점을 평균한 값
- 사용자 편향 점수 = 사용자별 아이템 평점 평균 값 - 전체 평균 평점
- 아이템 편향 점수 = 아이템별 평점 평균 값 - 전체 평균 평점
[교차 검증과 하이퍼 파라미터 튜닝]
Surprise는 교차 검증과 하이퍼 파라미터 튜닝을 위해 사이킷런과 유사한 cross_validate()와 GridSearchCV 제공.
◆ cross_validate() 사용법
해당 함수는 surprise.model_selection 모듈 내에 존재하며, 폴드된 데이터 세트의 개수와 성능 측정 방법을 명시해 교차 검증 수행. ratings.csv를 DataFrame으로 로딩한 데이터를 5개의 학습/검증 폴드 데이터 세트로 분리해 교차 검증을 수행하고 RMSE, MAE로 성능 평가를 수행한다. cross_validate() 인자로 알고리즘 객체, 데이터, 성능 평가 방법(measures), 폴드 데이터 세트 개수(cv)를 입력한다.
from surprise.model_selection import cross_validate
# 판다스 DataFrame 에서 Surprise 데이터 세트로 데이터 로딩
ratings = pd.read_csv("C:/Users/niceq/Documents/DataScience/Python ML Guide/Data/09. ml-latest-small/ratings.csv")
reader = Reader(rating_scale=(0.5, 5))
data = Dataset.load_from_df(ratings[['userId', 'movieId', 'rating']], reader)
algo = SVD(random_state=0)
cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=5, verbose=True)
▶ cross_validate() 는 폴드별 성능 수치와 전체 폴드의 평균 성능 평가 수치를 함께 보여준다.
◆ GridSearchCV 사용법
Surprise의 GridSearchCV 도 사이킷런의 GridSearchCV 와 유사하게 교차 검증을 통한 하이퍼 파라미터 최적화를 수행한다.
하이퍼 파라미터 최적화는 알고리즘 유형에 따라 다를 수 있으나, SVD의 경우 주로 점진적 하강 방식(Stochastic Gradient Descent)의 반복 횟수를 지정하는 n_epochs와 SVD의 잠재 요인 K의 크기를 지정하는 n_factors를 튜닝한다.
ratings.csv 를 DataFrame으로 로딩한 데이터를 사용하여,
n_epochs: [20, 40, 60]
n_factors: [50, 100, 200]
으로 변경하면서 CV가 3일 대의 최적 하이퍼 파라미터 도출
from surprise.model_selection import GridSearchCV
# 최적화할 파라미터를 딕셔너리 형태로 지정
param_grid = {'n_epochs': [20, 40, 60], 'n_factors': [50, 100, 200]}
# CV를 3개 폴드 세트로 지정, 성능 평가는 rmse, mse로 수행하도록 GridSearchCV 구성
gs = GridSearchCV(SVD, param_grid, measures=['rmse', 'mse'], cv=3)
gs.fit(data)
# 최고 RMSE Evluation 점수와 그때의 하이퍼 파라미터
print(gs.best_score['rmse'])
print(gs.best_params['rmse'])
'n_epochs': 20, 'n_factors': 50일 때 3개 폴드의 검증 데이터 세트에서 최적 RMSE가 약 0.8777로 도출되었다.
다음 내용
[출처]
파이썬 머신러닝 완벽 가이드
Surprise Package 사이트
https://surpriselib.com/
'[파이썬 Projects] > <파이썬 머신러닝>' 카테고리의 다른 글
[머신러닝] 머신러닝의 주요 도전 과제 (2) | 2024.11.07 |
---|---|
[머신러닝] 추천 시스템: Surprise를 이용한 영화 시스템 구축 (8) | 2024.11.06 |
[머신러닝] 추천 시스템: 잠재 요인 협업 필터링 실습 (3) | 2024.11.03 |
[머신러닝] 추천 시스템: 아이템 기반 최근접 이웃 협업 필터링 실습 (1) | 2024.11.03 |
[머신러닝] 추천 시스템: 협업 필터링 (0) | 2024.11.03 |