buffalo
는 kakao Tech에서 공개한 오픈 소스 추천 시스템 라이브러리이다.
buffalo 라이브러리 내에는 4가지 추천 알고리즘이 구현되어 있다.
1. ALS(Alternating Least Squares)
2. Bayesian Personalized Ranking Matrix Factorization
3. Word2Vec
4. CoFactors
라이브러리를 설치하려고 할 때 많은 시행착오가 있었는데, 먼저 buffalo requirements의 n2 라이브러리가 windows에서는 설치가 안 돼서 linux
로 설치하는 것을 권장한다.
$ git clone https://github.com/kakao/n2.git
$ cd n2
$ git submodule update --init
$ python setup.py install
$ git clone -b master https://github.com/kakao/buffalo
$ cd buffalo
$ git submodule update --init
$ pip install -r requirements.txt
$ python setup.py install
서버에서 conda 가상환경을 여러 개 사용하고 있고, gcc 버전이 사용하는 cuda, cudnn 등의 버전과 맞지 않는다면, gcc 관련 에러가 발생한다.
해당 에러 발생 시에는 conda 가상환경에서 gcc version을 따로 설치해서 사용하면 된다.
나는 gcc 6.4.0
을 설치했다. 다른 버전 설치방법은 검색해보면 나올 것이다.
$ conda install -c rgrout gcc_linux-64
$ conda install -c rgrout gxx_linux-64
설치를 한 후에는 해당 가상환경에서 conda gcc를 사용하도록 경로를 잡아줘야 하는데, 심볼릭 링크
를 생성하여 가상환경 gcc 경로를 잡도록 만들면 된다.
$ cd /home/root/anaconda3/envs/ENV_NAME/bin # 가상환경 bin 폴더로 이동
$ ln -s x86_64-conda-cos6-linux-gnu-gcc cc # 심볼릭 링크 생성(cc,gcc,g++,c++)
$ ln -s x86_64-conda-cos6-linux-gnu-gcc gcc
$ ln -s x86_64-conda-cos6-linux-gnu-g++ g++
$ ln -s x86_64-conda-cos6-linux-gnu-g++ c++
실행 후 buffalo를 재설치 하면 된다.
** 추가로 compute 30 관련 에러가 뜬다면 buffalo의 cuda_setup.py 파일에서,
post_args = ['-gencode=arch=compute_30,code=sm_30',
'-gencode=arch=compute_50,code=sm_50',
'-gencode=arch=compute_60,code=sm_60',
'-gencode=arch=compute_60,code=compute_60',
'--ptxas-options=-v', '-O2']
'-gencode=arch=compute_30,code=sm_30'
부분을 지운 후 설치를 다시 해보자.
추천시스템 구현에 사용할 데이터는, 추천 시스템 성능 테스트로 많이 사용되는 movielens
이다.
다음 다운로드 링크에서 ml-25.zip
을 다운로드 받으면 된다. [링크]
해당 데이터의 압축을 해제하고, ratings.csv
를 data directory에 넣는다.
쥬피터 노트북에 소스코드를 차례로 입력하면서 실행해 보면 된다.
import pandas as pd
data = pd.read_csv('./data/ratings.csv')
data.head()
------------------------------------------------------
# dtype transform
data = data[['userId', 'movieId', 'rating']].astype(str)
------------------------------------------------------
# buffalo library import
from buffalo.algo.als import ALS, inited_CUALS
from buffalo.algo.options import ALSOption
import buffalo.data
from buffalo.misc import aux
from buffalo.data.mm import MatrixMarketOptions
import numpy as np
from scipy.io import mmwrite
from scipy.io import mmread
from scipy.sparse import csr_matrix
import scipy.sparse as sp
------------------------------------------------------
print(inited_CUALS) # True이면 gpu 학습 가능
------------------------------------------------------
# 유저 * 아이템 매트릭스 생성
def get_df_matrix_mappings(df, row_name, col_name):
rid_to_idx = {}
idx_to_rid = {}
for (idx, rid) in enumerate(df[row_name].unique().tolist()):
rid_to_idx[rid] = idx
idx_to_rid[idx] = rid
cid_to_idx = {}
idx_to_cid = {}
for (idx, cid) in enumerate(df[col_name].unique().tolist()):
cid_to_idx[cid] = idx
idx_to_cid[idx] = cid
return rid_to_idx, idx_to_rid, cid_to_idx, idx_to_cid
def df_to_matrix(df, row_name, col_name):
rid_to_idx, idx_to_rid, cid_to_idx, idx_to_cid = get_df_matrix_mappings(df, row_name, col_name)
def map_ids(row, mapper):
return mapper[row]
I = df[row_name].apply(map_ids, args=[rid_to_idx]).to_numpy()
J = df[col_name].apply(map_ids, args=[cid_to_idx]).to_numpy()
V = np.ones(I.shape[0])
interactions = sp.coo_matrix((V, (I, J)), dtype=np.float64)
interactions = interactions.tocsr()
return interactions, rid_to_idx, idx_to_rid, cid_to_idx, idx_to_cid
------------------------------------------------------
# 행렬 매트릭스를 생성하여 파일로 저장
user_items, uid_to_idx, idx_to_uid, mid_to_idx, idx_to_mid = df_to_matrix(data, 'userId', 'movieId')
mmwrite(f'./train/main.mtx', user_items)
------------------------------------------------------
# uid = user ID, iid = item ID
uid = list(idx_to_uid.values())
iid = list(idx_to_mid.values())
------------------------------------------------------
# uid, iid 값을 파일로 저장
with open(f"./train/uid", "w") as f:
for val in uid:
print(val, file=f)
f.close()
with open(f"./train/iid", "w") as f:
for val in iid:
print(val, file=f)
f.close()
------------------------------------------------------
# parameter Optimizer
# -- hyperopt 사용하여 최적 파라미터 서치
opt = ALSOption().get_default_option()
opt.num_workers = 6 # worker 수 조정
opt.num_iters = 20
opt.evaluation_period = 20
opt.evaluation_on_learning = True
opt.save_best = True
opt.accelerator = True # GPU option
# optimizer에 사용할 데이터 옵션(경로) 설정
data_opt = MatrixMarketOptions().get_default_option()
data_opt.input.main = './train/main.mtx'
data_opt.input.iid = './train/iid'
data_opt.input.uid = './train/uid'
data_opt.data.ath = './train/mm.h5py'
data_opt.data.validation.p = 0.1
data_opt.data.validation.max_samples = 5000
# optimizer search 범위 설정
opt.validation = aux.Option({'topk' : 10 })
opt.tensorboard = aux.Option({'root' : './train/als-validation', 'name' : 'als-new'})
opt.optimize = aux.Option({
'loss': 'val_ndcg',
'max_trials':100,
'deployment': True,
'start_with_default_parameters': False,
'space': {
'd': ['randint', ['d', 10, 128]],
'reg_u': ['uniform', ['reg_u', 0.1, 1.0]],
'reg_i': ['uniform', ['reg_i', 0.1, 1.0]],
'alpha': ['randint', ['alpha', 1, 10]]
}
})
------------------------------------------------------
# 설정 옵션을 ALS 모델에 넣고 생성
als = ALS(opt, data_opt = data_opt)
als.initialize()
als.opt.model_path = './train/als-best-model.bin'
als.optimize() # parameter optimizing
als.get_optimization_data()
------------------------------------------------------
# 학습시킬 데이터 설정
data_opt = MatrixMarketOptions().get_default_option()
data_opt.input.main = f'./train/main.mtx'
data_opt.input.iid = f'./train/iid'
data_opt.input.uid = f'./train/uid'
data_opt.data.validation.p = 0.1
data_opt.data.validation.max_samples = 10000
data_opt.data.path = f'./train/mm.h5py'
data = buffalo.data.load(data_opt)
data.create()
------------------------------------------------------
del als
als_opt = ALS()
als_opt.load('./train/als-best-model.bin') # 최적화 opt 불러오기
als_opt.opt
------------------------------------------------------
# model train
model = ALS(als_opt.opt, data= data)
model.initialize()
model.train()
------------------------------------------------------
# Top 5 movie list for 'userId 1'
model.topk_recommendation('1',topk=5)
# Simmilar movie with 'movieId 4973'
model.most_similar('4973',topk=5)
------------------------------------------------------
샘플 실행 코드는 ALS만 있지만, 진행 방식은 유사하기 때문에 라이브러리만 다르게 임포트 해서 다른 알고리즘도 적용해 볼 수 있을 것이다.
날짜로 데이터를 스플릿해서 train set, test set으로 나눈 후 recommendation list를 실제 movie 점수와 비교해보면, 알고리즘 별 성능을 비교해 볼 수 있다.