복수의 추천 알고리즘을 결합해서 추천하는 것을 하이브리드 추천 알고리즘 혹은 하이브리드 추천 시스템이라고 한다.
추천 알고리즘의 정확도는 개별 아이템에 대한 개별 사용자의 예측 평점을 알고리즘으로 계산한 후에 이 예측 평점과 실제 평점과의 차이가 정확도가 된다.
하이브리드 추천 시스템에서는 복수의 알고리즘이 계산한 개별 사용자의 개별 아이템에 대한 예측치 하나하나에 대해서 일정한 규칙으로 결합하는 것이다.
한 알고리즘이 잡아내지 못하는 평가 패턴을 다른 알고리즘이 잡아내서 이 둘을 결합하면 서로 보완적인 효과가 생기게 되는 장점이 있다.
# 2개의 알고리즘을 결합하는 하이브리드 추천 알고리즘의 예
##### (1)
# Dummy recommender 0
def recommender0(recomm_list):
recommendations = []
for pair in recomm_list:
recommendations.append(random.random() * 4 + 1)
return np.array(recommendations)
# Dummy recommender 1
def recommender1(recomm_list):
recommendations = []
for pair in recomm_list:
recommendations.append(random.random() * 4 + 1)
return np.array(recommendations)
# RMSE check
def RMSE2(y_true, y_pred):
return np.sqrt(np.mean((np.array(y_true) - np.array(y_pred))**2))
# Hybrid 결과 얻기
weight = [0.8, 0.2]
recomm_list = np.array(ratings_test)
predictions0 = recommender0(recomm_list)
predictions1 = recommender1(recomm_list)
predictions = predictions0 * weight[0] + predictions1 * weight[1]
RMSE2(recomm_list[:,2], predictions)
def recommender0(recomm_list, mf):
recommendations = np.array([mf.get_one_prediction(user, movie) for (user, movie) in recomm_list])
return recommendations
def recommender1(recomm_list, neighbor_size=0):
recommendations = np.array([CF_knn_bias(user, movie, neighbor_size) for (user, movie) in recomm_list])
return recommendations
recomm_list = np.array(ratings_test.iloc[:, [0,1]])
predictions0 = recommender0(recomm_list, mf)
RMSE2(ratings_test.iloc[:,2], predictions0)
predictions1 = recommender1(recomm_list, 37)
RMSE2(ratings_test.iloc[:,2], predictions1)
weight = [0.8, 0.2]
predictions = predictions0 * weight[0] + predictions1 * weight[1]
RMSE2(ratings_test.iloc[:,2], predictions)
MF 알고리즘의 RMSE는 약 0.909601, CF알고리즘의 RMSE는 약 0.946719이다.
이 둘을 결합한 하이브리드 추천 시스템은 RMSE가 0.909193으로서 미세하지만 두 알고리즘보다 더 좋은 결과를 보여준다.
# 최적의 가중치
for i in np.arange(0, 1, 0.01):
weight = [i, 1.0 - i]
predictions = predictions0 * weight[0] + predictions1 * weight[1]
print('Weights - %.2f : %.2f ; RMSE = %.7f' % (weight[0], weight[1], RMSE2(ratings_test.iloc[:,2], predictions)))
0.88(MF) : 0.12(CF)의 비중이 RMSE 0.908876으로서 가장 좋은 결과를 보여준다.
추천 시스템에서 사용되는 대부분의 데이터는 full matrix로 변환하면 많은 원소가 비어있는 희박행렬(sparse matrix)이다.
예를 들어, MovieLens 100K 데이터를 full matrix로 변환하면 원소 중 값을 갖는 비율이 약 0.6%, MovieLens 20M 데이터는 비율이 약 0.1%밖에 되지 않는다.
full matrix를 sparse matrix로 처리하면 값이 있는 원소의 인덱스와 값만 저장하면됨. 이 방식은 데이터가 희박할수록 저장공간이 절약되는 정도가 커진다.
sparse matrix의 단점은 데이터를 저장하거나 읽어올 때마다 값이 존재하는지 확인해 그에 맞는 처리를 하기에 데이터 처리의 overhead cost가 크다는 점이다.
데이터가 희박하지 않은 경우에 sparse matrix를 사용하면 공간 절약의 이점보다 속도 저하의 단점이 오히려 크게 된다.
반면 행렬의 데이터가 희박할수록 sparse matrix을 사용하는 편이 더 낫게 된다.
r_cols = ['user_id', 'movie_id', 'rating', 'timestamp']
# 20M data 읽어오기
ratings = pd.read_csv('ratings-20m.csv', names=r_cols, sep=',', encoding='latin-1')
ratings = ratings[['user_id', 'movie_id', 'rating']].astype(int)
# train test 분리
from sklearn.utils import shuffle
TRAIN_SIZE = 0.75
ratings = shuffle(ratings, random_state=1)
cutoff = int(TRAIN_SIZE * len(ratings))
ratings_train = ratings.iloc[:cutoff]
ratings_test = ratings.iloc[cutoff:]
# Sparse matrix 사용
from scipy.sparse import csr_matrix
data = np.array(ratings['rating'])
row_indices = np.array(ratings['user_id'])
col_indices = np.array(ratings['movie_id'])
ratings = csr_matrix((data, (row_indices, col_indices)), dtype=int)
# New MF class for training & testing
class NEW_MF():
def __init__(self, ratings, K, alpha, beta, iterations, verbose=True):
self.R = ratings
self.num_users, self.num_items = np.shape(self.R)
self.K = K
self.alpha = alpha
self.beta = beta
self.iterations = iterations
self.verbose = verbose
# Root Mean Squared Error(RMSE) 계산
def rmse(self):
xs, ys = self.R.nonzero()
self.predictions = []
self.errors = []
for x, y in zip(xs, ys):
prediction = self.get_prediction(x, y)
self.predictions.append(prediction)
self.errors.append(self.R[x, y] - prediction)
self.predictions = np.array(self.predictions)
self.errors = np.array(self.errors)
return np.sqrt(np.mean(self.errors**2))
# Rating prediction for user i and item j
def get_prediction(self, i, j):
prediction = self.b + self.b_u[i] + self.b_d[j] + self.P[i, :].dot(self.Q[j, :].T)
return prediction
# Stochastic gradient descent to get optimized P and Q matrix
def sgd(self):
for i, j, r in self.samples:
prediction = self.get_prediction(i, j)
e = (r - prediction)
self.b_u[i] += self.alpha * (e - self.beta * self.b_u[i])
self.b_d[j] += self.alpha * (e - self.beta * self.b_d[j])
self.P[i,:] += self.alpha * (e * self.Q[j, :] - self.beta * self.P[i, :])
self.Q[j,:] += self.alpha * (e * self.P[i, :] - self.beta * self.Q[j, :])
# Test set을 선정
def set_test(self, ratings_test):
test_set = []
for i in range(len(ratings_test)):
x, y, z = ratings_test.iloc[i]
test_set.append([x,y,z])
self.R[x,y] = 0
self.test_set = test_set
return test_set
# Test set의 RMSE 계산
def test_rmse(self):
error = 0
for one_set in self.test_set:
predicted = self.get_prediction(one_set[0], one_set[1])
error += pow(one_set[2] - predicted, 2)
return np.sqrt(error / len(self.test_set))
# Training 하면서 test set의 정확도를 계산
def test(self):
self.P = np.random.normal(scale=1./self.K, size=(self.num_users, self.K))
self.Q = np.random.normal(scale=1./self.K, size=(self.num_items, self.K))
self.b_u = np.zeros(self.num_users)
self.b_d = np.zeros(self.num_items)
self.b = np.mean(self.R[self.R.nonzero()])
rows, columns = self.R.nonzero()
self.samples = [(i,j,self.R[i,j]) for i,j in zip(rows, columns)]
training_process = []
for i in range(self.iterations):
np.random.shuffle(self.samples)
self.sgd()
rmse1 = self.rmse()
rmse2 = self.test_rmse()
training_process.append((i+1, rmse1, rmse2))
if self.verbose:
if (i+1) % 10 == 0:
print('Iteration : %d ; Train RMSE = %.4f ; Test RMSE = %.4f' % (i+1, rmse1, rmse2))
return training_process
def get_one_prediction(self, user_id, item_id):
return self.get_prediction(user_id, item_id)
def full_prediction(self):
return self.b + self.b_u[:,np.newaxis] + self.b_d[np.newaxis,:] + self.P.dot(self.Q.T)
# Testing MF RMSE
R_temp = ratings.copy()
mf = NEW_MF(R_temp, K=30, alpha=0.001, beta=0.02, iterations=100, verbose=True)
test_set = mf.set_test(ratings_test)
result = mf.test()
데이터의 크기가 커지면서 RMSE가 0.7877로 MovieLens 100K 데이터보다 매우 향상되었음을 알 수 있다.
투빅스 17기 박나윤입니다.
하이브리드 추천시스템
대규모 데이터 처리 Sparse matrix
투빅스 16기 박한나입니다.
[하이브리드 추천 시스템]
[Sparse matrix]
투빅스 16기 이승주
1. 하이브리드 추천 시스템
복수의 알고리즘이 계산한 예측 평점에 대해서 일정한 규칙으로 결합하는 것이다. 한 알고리즘이 잡아내지 못하는 평가 패턴을 다른 알고리즘이 잡아내서 이 둘을 결합하면 서로 보완적인 효과가 생기게 되는 장점이 있다.
2. Sparse matrix
full matrix를 sparse matrix로 처리하면 값이 있는 원소의 인덱스와 값만 저장하면된다. 해당 방식은 데이터가 희박할수록 저장공간이 절약되는 정도가 커지지만 데이터를 저장하거나 읽어올 때마다 값이 존재하는지 확인해 그에 맞는 처리를 하기때문에 데이터 처리의 overhead cost가 크다는 단점이 존재한다.
하이브리드 추천시스템은 여러가지의 알고리즘을 섞어서 사용한 모델을 의미합니다. 하나의 모델이 좋은 성능을 가지는 경우도 있지만 여러가지의 알고리즘을 활용하여 더 나은 모델을 만들 수 있습니다.
또한, sparse matrix는 희소 행렬로 불리며, 대부분의 값이 관측되지 않거나, 없는 값으로 이루어져 있습니다. 값이 이루어져있는 인덱스만 표시하기 때문에 큰 데이터에서 주로 사용이 됩니다. 하지만 단점으로는 매번 해당 인덱스에 값이 있는지 확인 후 불러오기 때문에 overhead cost가 있다는 단점이있습니다.
투빅스 17기 염제윤입니다.
하이브리드 추천시스템은 복수의 알고리즘을 활용한 모델을 뜻하며, 앙상블 모델은 하이브리드 모델안에 포함된다. 하이브리드 추천 시스템은 복수의 알고리즘이 계산한 개별 사용자의 개별 아이템에 대한 예측치 하나하나에 대해서 일정한 규칙으로 결합하는 것이다. 개별 알고리즘이 잡아내지 못하는 평가 패턴을 다른 알고리즘이 잡아내서 이 둘을 결합하면 서로 보완적인 효과가 생기게 되는 장점이 있다.
대부분의 정보를 full matrix로 표현할시 매우 sparse한 데이터가된다. 이것은 연산과 저장공간적인 측면에서 매우 비효율적이다. 따라서 이것을 sparse matrix로 변환하여 처리한다. 이럴 경우 원소의 인덱스와 값만 저장하면 되므로 희박할수록 절약정도가 커진다. 단 데이터를 정정하고 읽어올 때마다 일일이 확인하게 되므로 데이터처리의 overhead cost가 크다는 단점이 있다. 따라서 dense한 데이터일 경우 fullmatrix 희박할수록 sparse matrix를 추천한다.