추천시스템의 개요와 배경
- 추천엔진은 사용자가 무엇을 원하는지 빠르게 찾아내어 사용자의 온라인 쇼핑 이용 즐거움을 배가 시킴
- 데이터 기반의 추천시스템
- 사용자가 어떤 상품을 구매했는가?
- 사용자가 어떤 상품을 둘러보거나 장바구니에 넣었는가?
- 사용자가 평가한 영화 평점은? 제품 평가는?
- 사용자가 스스로 작성한 자신의 취향은?
- 사용자가 무엇을 클릭했는가?
- 이러한 데이터를 기반으로 사용자에게 친숙한 문구(ex.’이 상품을 선택한 다른 사람들이 좋아하는 상품들’)로 구매를 유도
<추천 시스템의 유형>
- 콘텐츠 기반 필터링 (Content based filtering)
- 협업 필터링 (Collaborative Filtering)
- 최근접 이웃 협업 필터링 (Nearst Neighbor)
- 잠재요인 협업 필터링 (Latent Factor)
콘텐츠 기반 필터링 추천 시스템
- 사용자가 특정한 아이템을 매우 선호하는 경우, 그 아이템과 비슷한 콘텐츠를 가진 다른 아이템을 추천하는 방식
- ex) 특정 영화에 높은 평점을 주었다면 그 영화의 장르, 출연 배우, 감독, 영화 키워드 등의 콘텐츠와 유사한 다른 영화를 추천해 줌
최근접 이웃 협업 필터링
- 사용자가 아이템에 매긴 평점 정보나 상품 구매 이력과 같은 사용자 행동 양식(User behavior)만을 기반으로 추천을 수행하는 것
- 협업 필터링은 사용자-아이템 평점 매트릭스를 사용하여 사용자가 평가하지 않은 아이템을 평가한 아이템에 기반하여 예측하는 알고리즘
- 사용자-아이템 평점 매트릭스에서 행(Row)는 개별 사용자, 열(Column)은 개별 아이템으로 구성되며, 각 (행,열) 위치에 해당되는 값이 평점을 나타내는 형태
- 다차원 행렬, 희소 행렬의 특성을 가지고 있음
- 최근접 이웃 협업 필터링 = 메모리(Memory) 협업 필터링
- 사용자 기반(User-User)
- 아이템 기반(Item-Item)
- 사용자 기반 최근접 이웃 방식: 특정 사용자와 타 사용자 간의 유사도를 측정한 뒤 가장 유사도가 높은 TOP-N 사용자를 추출해 그들이 선호하는 아이템을 추천하는 것
- 비슷한 상품을 좋아한다고 해서 사람들의 취향이 비슷하다고 판단하기 어렵고 사용자들이 평점을 매긴 상품의 개수가 많지 않은 것이 일반적이기 때문에 사용자 간의 유사도를 측정하기 어려운 단점이 있음
- 아이템 기반 최근접 이웃 방식: 아이템의 속성과는 상관없이 사용자들이 그 아이템을 좋아하는지, 싫어하는지의 평가 척도가 유사한 아이템을 추천하는 기준이 되는 알고리즘
- 최근접 이웃 협업 필터링은 대부분 아이템 기반의 알고리즘을 적용
- 유사도 측정 방법으로는 코사인 유사도를 이용
잠재요인 협업 필터링
- 사용자-아이템 평점 행렬 속에 숨어 있는 잠재요인을 추출해 추천 예측을 할 수 있게 하는 기법
- 다차원 희소 행렬인 사용자-아이템 평점 행렬을 저차원 밀집행렬의 사용자-잠재적 요인 행렬, 잠재적 요인-아이템 행렬로 분해하여 두 행렬의 내적을 통해 새로운 예측 사용자-아이템 평점 행렬을 만듦
- 사용자가 아직 평점을 부여하지 않은 데이터에 대한 예측 평점을 생성할 수 있게 됨
- 가령 영화 평점 기반의 사용자-아이템 행렬에서는 잠재요인을 영화 장르로 생각할 수 있음
- 사용자 - 잠재요인 행렬은 사용자의 영화 장르에 대한 선호도, 잠재요인-아이템 행렬은 영화의 장르별 특성 값으로 해석할 수 있음
- ex) User 1이 평점을 매기지 못한 item 2에 대해 예측 평점을 수행하기 위해 R(1,2)를 구하면 행렬분해된 P 행렬의 User1벡터와 전치된 Q 행렬의 Item2의 벡터 내적 결괏값인 2.56으로 예측할 수 있음
<행렬 분해의 이해>
- 행렬 분해 기법으로는 SVD(Singular Vector Decomposition), NMF(Non-Negative Matrix Factorization) 등이 있음
- R = P*Q.T
- M 은 총 사용자 수
- N 은 총 아이템 수
- K는 잠재 요인의 차원 수
- R은 M x N 차원의 사용자-아이템 평점 행렬
- P는 M x K 차원의 사용자-잠재 요인 행렬
- Q는 N x K 차원의 아이템-잠재 요인 행렬
- Q.T는 Q의 전치 행렬(K x N 차원)
R 행렬의 u행 사용자와 i열 위치에 있는 평점 데이터를 r(u, i) 라고 하면
- 사용자-아이템 평점 행렬의 미정 값을 포함한 모든 평점 값은 행렬 분해로 얻어진 P 행렬과 Q.T 행렬의 내적을 통해 예측 평점으로 다시 계산할 수 있음
- 행렬 분해는 주로 SVD 방식을 사용하지만 원본행렬에 널(NaN) 값이 있을 경우 확률적경사하강법(SGD) 이나 ALS 를 이용하여 SVD 수행
<확률적 경사 하강법을 이용한 행렬 분해>
- P 와 Q 행렬로 계산된 예측 R 행렬 값이 실제 R 행렬 값과 가장 최소의 오류를 가질 수 있도록 반복적인 비용 함수 최적화를 통해 P와 Q 를 유추해내는 것
- P와 Q 를 임의의 값을 가진 행렬로 설정한다.
- P와 Q.T값을 곱해 예측 R 행렬을 계산하고 예측 R 행렬과 실제 R 행렬에 해당하는 오류 값을 계산한다.
- 이 오류 값을 최소화 할 수 있도록 P 와 Q 행렬을 적절한 값으로 각각 업데이트한다.
- 만족할 만한 오류 값을 가질 때까지 2, 3번 작업을 반복하면서 P와 Q 값을 업데이트해 근사화한다.
- 사용자-아이템 평점 행렬의 경우 행렬 분해를 위해서 오류 최소화와 L2규제를 반영한 비용 함수를 사용
L2 규제를 반영해 실제 R 행렬값과 예측 R 행렬값의 차이를 최소화 하는 방향성을 가지고 P 행렬과 Q 행렬에 업데이트 값을 반복적으로 수행하면서 최적의 예측 R 행렬을 구하는 것이 SGD 기반의 행렬분해
<파이썬으로 SGD를 이용한 행렬분해 구현>
원본 행렬 R을 널 값을 포함해 생성하고 분해 행렬 P, Q는 정규분포를 가진 랜덤 값으로 초기화, 잠재요인 차원 K는 3
import numpy as np
#원본 행렬 R 생성, 분해 행렬 P와 Q 초기화, 잠재 요인 차원 K는 3으로 설정
R = np.array([[4, np.NaN,np.NaN, 2, np.NaN],
[np.NaN, 5, np.NaN, 3, 1],
[np.NaN, np.NaN, 3, 4, 4],
[5, 2, 1, 2, np.NaN]])
num_users, num_items = R.shape
K=3
#P와 Q행렬의 크기를 지정하고 정규 분포를 가진 임의의 값으로 입력
np.random.seed(1)
P = np.random.normal(scale=1./K, size=(num_users, K))
Q = np.random.normal(scale=1./K, size=(num_items, K))
get_rmse()함수로 실제 R행렬과, 예측 행렬의 널이 아닌 값에 대한 오차 계산
# 실제 R 행렬과 예측 행렬의 오차를 구하는 get_rmse()함수
from sklearn.metrics import mean_squared_error
def get_rmse(R, P, Q, non_zeros):
error=0
#두 개의 분해된 행렬 P와 Q.T의 내적으로 예측 R 행렬 생성
full_pred_matrix = np.dot(P, Q.T)
#실제 R 행렬에서 널이 아닌 값의 위치 인덱스 추출해 실제 R 행렬과 예측 행렬의 RMSE 추출
x_non_zero_ind=[non_zero[0] for non_zero in non_zeros]
y_non_zero_ind=[non_zero[1] for non_zero in non_zeros]
R_non_zeros = R[x_non_zero_ind, y_non_zero_ind]
full_pred_matrix_non_zeros = full_pred_matrix[x_non_zero_ind, y_non_zero_ind]
mse = mean_squared_error(R_non_zeros, full_pred_matrix_non_zeros)
rmse = np.sqrt(mse)
return rmse
SGD 기반으로 행렬분해 수행
# R>0인 행 위치, 열 위치, 값을 non_zeros 리스트에 저장
non_zeros = [(i,j,R[i,j]) for i in range(num_users) for j in range(num_items) if R[i,j] > 0]
steps = 1000
learning_rate = 0.01
r_lambda = 0.01
#SGD 기법으로 P와 Q 매트릭스를 계속 업데이트
for step in range(steps):
for i, j, r in non_zeros:
# 실제 값과 예측 값의 차이인 오류 값 구함
eij = r - np.dot(P[i,:], Q[j,:].T)
#Regularization을 반영한 SGD 업데이트 공식 적용
P[i,:] = P[i, :]+ learning_rate*(eij*Q[j,:]-r_lambda*P[i,:])
Q[j,:] = Q[j, :]+ learning_rate*(eij*P[i,:]-r_lambda*Q[j,:])
rmse = get_rmse(R, P, Q, non_zeros)
#50회 반복할 때마다 오류 값을 출력
if(step % 50) == 0:
print('### iteration steps :' , step, "rmse :", rmse)
[output]
### iteration steps : 0 rmse : 0.016410959588961445
### iteration steps : 50 rmse : 0.01637308966218986
### iteration steps : 100 rmse : 0.016333882336881947
### iteration steps : 150 rmse : 0.01629366127459915
### iteration steps : 200 rmse : 0.01625272572302349
### iteration steps : 250 rmse : 0.01621133898468145
### iteration steps : 300 rmse : 0.016169725523756746
### iteration steps : 350 rmse : 0.016128072491623933
### iteration steps : 400 rmse : 0.016086533303745525
### iteration steps : 450 rmse : 0.016045231961723215
### iteration steps : 500 rmse : 0.016004267423347117
### iteration steps : 550 rmse : 0.015963717671935763
### iteration steps : 600 rmse : 0.01592364333325907
### iteration steps : 650 rmse : 0.015884090797210332
### iteration steps : 700 rmse : 0.015845094858961524
### iteration steps : 750 rmse : 0.015806680922219093
### iteration steps : 800 rmse : 0.015768866818295302
### iteration steps : 850 rmse : 0.015731664296432708
### iteration steps : 900 rmse : 0.01569508023771203
### iteration steps : 950 rmse : 0.015659117639502302
업데이트가 끝난 후 분해된 P와 Q 함수를 P*Q.T로 예측행렬을 만들어 결과를 확인해보면 널이 아닌 값은 원본행렬과 큰 차이를 보이지 않으며 널 값은 새로운 예측값으로 채워짐
#분해된 P와 Q 행렬을 P*Q.T로 예측행렬을 만들어 출력
pred_matrix = np.dot(P,Q.T)
print('예측 행렬:\n', np.round(pred_matrix,3))
[output]
예측 행렬:
[[3.991 1.171 1.266 2. 1.649]
[6.288 4.978 0.896 2.983 1.003]
[6.401 0.866 2.987 3.978 3.986]
[4.969 2.005 1.006 2.013 1.258]]
'데이터 > 머신러닝' 카테고리의 다른 글
[NLP] Chapter 8 | 텍스트 분석 (텍스트 분류) (1) | 2023.02.03 |
---|---|
[NLP] Chapter 8 | 텍스트 분석 (소개 및 기반지식) (1) | 2023.02.02 |
[Clustering] Chapter 7 | 군집화 실습 - 고객 세그멘테이션 (1) | 2023.01.28 |
[Clustering] Chapter 7 | 군집화 (02. 군집 평가) (0) | 2023.01.23 |
[Clustering] Chapter 7 | 군집화 (01. K-평균 알고리즘의 이해) (0) | 2023.01.22 |