RecBole 사용방법
Written by seoo2001
Local에 Git Clone 하기
설치 방법은 pip, git clone 등 다양함 일단 git clone으로 해봤음
https://github.com/RUCAIBox/RecBole
apt-get install git
git clone https://github.com/RUCAIBox/RecBole.git
cd RecBole
rm -r .git # 깃 설정 삭제 (Optional)
requirements 다운
# 방법 1
python setup.py install
# 방법 2
pip install -r requirements.txt
# 일단 1번으로 진행했음. 에러 발생하면, 2번 진행
# 추가 설치
pip install xgboost # 없으면 error 발생
pip install lightgbm # 없으면 error 발생
데이터셋 설정
interaction 데이터셋, 유저, 아이템 정보 등으로 구성.
파일 형식은 tab으로 구분된 csv 파일.
# 데이터셋 생성 예시
import pandas as pd
train_df = pd.read_csv('../data/train/train_ratings.csv')
genres_df = pd.read_csv('../data/train/genres.tsv', sep='\t')
directors_df = pd.read_csv('../data/train/directors.tsv', sep='\t')
writers_df = pd.read_csv('../data/train/writers.tsv', sep='\t')
years_df = pd.read_csv('../data/train/years.tsv', sep='\t')
titles_df = pd.read_csv('../data/train/titles.tsv', sep='\t')
# merge dataframes
unique_movie = train_df['item'].unique()
genres_df = genres_df.groupby('item')['genre'].apply(lambda x: ' '.join(x)).reset_index()
directors_df = directors_df.groupby('item')['director'].apply(lambda x: ' '.join(x)).reset_index()
writers_df = writers_df.groupby('item')['writer'].apply(lambda x: ' '.join(x)).reset_index()
unique_movie = pd.DataFrame(unique_movie, columns=['item'])
unique_movie = pd.merge(unique_movie, genres_df, on='item', how='left')
unique_movie = pd.merge(unique_movie, directors_df, on='item', how='left')
unique_movie = pd.merge(unique_movie, writers_df, on='item', how='left')
unique_movie = pd.merge(unique_movie, titles_df, on='item', how='left')
unique_movie = pd.merge(unique_movie, years_df, on='item', how='left')
# column name 바꾸기
unique_movie.columns = ['item_id:token', 'genre:token_seq', 'director:token_seq', 'writer:token', 'title:token_seq', 'year:token']
unique_movie.to_csv('../data/train/movie_info.tsv', sep='\t', index=False)
# inter
train_df = pd.read_csv('../data/train/train_ratings.csv')
train_df.columns = ['user_id:token', 'item_id:token', 'timestamp:float']
train_df['label:float'] = 1 # label 1로 추가 (Optional)
train_df.to_csv('../data/train/interactions.tsv', sep='\t', index=False)
interactions.tsv -> interaction 파일, 파일명 데이터셋이름.inter 로 수정 movie_info.tsv -> item 파일, 파일명 데이터셋이름.item 으로 수정
데이터셋 관련 추가 자료는 링크 참고
데이터셋 저장 저장 위치 Recbole/dataset/데이터셋이름/데이터셋이름.inter Recbole/dataset/데이터셋이름/데이터셋이름.item
- 데이터 설정 파일 수정 파일 위치: RecBole/recbole/properties/dataset/데이터셋이름.yaml (데이터셋이름은 위에서 저장한 이름과 동일하게)
field_separator: "\t" # 데이터 구분자
seq_separator: " " # (str) Separator inside the sequence features.
# 필드 정의
load_col:
inter: [user_id, item_id, timestamp] # interaction 데이터의 컬럼
item: [item_id, genre, director, writer, title, year] # 선택적: 아이템 부가 정보
# 필드 타입 명시
USER_ID_FIELD: user_id
ITEM_ID_FIELD: item_id
TIME_FIELD: timestamp # 시간 정보가 있는 경우
# LABEL_FIELD: label # (str) Expected field name of the generated labels for point-wise dataLoaders.
# 평가 설정
metrics: ["Recall"] # 평가 메트릭
topk: [10] # Top-K 추천 설정
valid_metric: Recall@10
NEG_PREFIX: neg_
# training settings
train_batch_size: 2048
eval_batch_size: 4096
# negative sampling settings
train_neg_sample_args:
distribution: uniform # 균등 분포로 샘플링
sample_num: 5 # positive sample 1개당 5개의 negative samples
dynamic: True # 매 epoch마다 새로운 negative samples 생성
# validation, test 네거티브 샘플링 되는건지 모르겠음. 고쳐줘
valid_neg_sample_args:
distribution: uniform
sample_num: 5
test_neg_sample_args:
distribution: uniform
sample_num: 5
학습
Recbole/run_model.py 파일 생성
# 실행: python3 run_model.py
# 실행 방법은 다른 방법도 많음.
from recbole.config import Config
from recbole.quick_start import run_recbole
config_dict = {
'model': 'RaCT',
'dataset': 'ml',
'loss_type': 'BCEWithLogitsLoss',
'train_neg_sample_args': {'distribution': 'uniform', 'sample_num': 5},
'eval_args': {
'split': {'RS': [0.8, 0.1, 0.1]},
'group_by': 'user',
'order': 'RO',
'mode': {'valid': 'full', 'test': 'full'},
'metrics': ['Recall'],
'topk': [10],
'valid_metric': 'Recall@10'
},
# 여기도 negative sampling 되는건지 모르겠음.
'eval_neg_sample_args': {'distribution': 'uniform', 'sample_num': 100},
}
run_recbole(config_dict=config_dict)
학습이 되면 모델이 Recbole/saved/ 경로에 .pth 형식으로 저장됨.
모델 테스트
Recbole/run_inference.py 파일 생성
# run_inference.py 구현 예시
import argparse
import torch
import numpy as np
import pandas as pd
from recbole.quick_start import load_data_and_model
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--model_path', '-m', type=str, default='saved/RaCT-ml-150.pth', help='name of models')
# python run_inference.py --model_path=/opt/ml/input/RecBole/saved/SASRecF-Apr-07-2022_03-17-16.pth 로 실행
args, _ = parser.parse_known_args()
# model, dataset 불러오기
config, model, dataset, train_data, valid_data, test_data = load_data_and_model(args.model_path)
# device 설정
device = config.final_config_dict['device']
# user, item id -> token 변환 array
user_id2token = dataset.field2id_token['user_id']
item_id2token = dataset.field2id_token['item_id']
# user-item sparse matrix
matrix = dataset.inter_matrix(form='csr')
# user id, predict item id 저장 변수
pred_list = None
user_list = None
model.eval()
for data in test_data:
interaction = data[0].to(device)
score = model.full_sort_predict(interaction)
# rating_pred = score.cpu().data.numpy().copy()
batch_user_index = interaction['user_id'].cpu().numpy()
rating_pred = score.cpu().data.numpy().reshape(len(batch_user_index), -1)
rating_pred[matrix[batch_user_index].toarray() > 0] = 0
ind = np.argpartition(rating_pred, -10)[:, -10:]
arr_ind = rating_pred[np.arange(len(rating_pred))[:, None], ind]
arr_ind_argsort = np.argsort(arr_ind)[np.arange(len(rating_pred)), ::-1]
batch_pred_list = ind[
np.arange(len(rating_pred))[:, None], arr_ind_argsort
]
# 예측값 저장
if pred_list is None:
pred_list = batch_pred_list
user_list = batch_user_index
else:
pred_list = np.append(pred_list, batch_pred_list, axis=0)
user_list = np.append(user_list, batch_user_index, axis=0)
result = []
for user, pred in zip(user_list, pred_list):
for item in pred:
user_token = user_id2token[user]
item_token = item_id2token[item]
if user_token != '[PAD]' and item_token != '[PAD]':
result.append((int(user_token), int(item_token)))
else:
result.append((int(user_token), 2571)) # [PAD] 토큰이 들어있는 경우 예외처리. 2571이 중복으로 들어가는 경우도 있어서, 수정 필요함.
# 데이터 저장
dataframe = pd.DataFrame(result, columns=["user", "item"])
dataframe.sort_values(by='user', inplace=True)
dataframe.to_csv(
"./submission.csv", index=False
) # 파일 저장 경로는 적절하게 설정!
print('inference done!')
추가 팁
설정 사항은 다음의 우선순위를 따름. The priority of the configuration methods is: Command Line > Parameter Dicts > Config Files > Default Settings
BPR, NCF, PureSVD, RaCT, MultiVAE, MultiDAE, EASE 등 써보면 될듯.
모델마다 이상한 오류 많았음. 부스팅 모델 쓰려면 negative sampling을 직접해서 데이터셋에 0, 1로 라벨링된 상태로 넣거나, negative sampling 되도록 고쳐야됨.
참고 블로그 https://mingchin.tistory.com/420 https://hoon-bari.github.io/RS/Recbole
Docs(https 보안연결 안됨, 고급 -> 안전하지 않음으로 이동 클릭) https://recbole.io/docs/
- 추가 오류나, 팁, 개선내용 댓글로 추가하기