이전 포스팅에서는 행렬분해(Matrix Factorization)과 확률적 경사하강법(SGD)에 관하여 이야기를 했습니다.
행렬분해(Matrix Factorization)에 대한 내용은 아래의 링크를 확인하시면 됩니다.
[추천시스템] (잠재요인) 모델 기반 협업 필터링(1) - 경사하강법(SGD)
저번에는 추천시스템의 이웃 기반 협업필터링에 관하여 다루었습니다.
my-develop-note.tistory.com
포스팅에 참고한링크는 아래와 같습니다.
- 참고링크1 : https://velog.io/@dlskawns/%EC%B6%94%EC%B2%9C%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%9D%B4%EB%A1%A0-%EC%BB%A8%ED%85%90%EC%B8%A0-%EA%B8%B0%EB%B0%98%EC%B6%94%EC%B2%9CCB-%ED%98%91%EC%97%85%ED%95%84%ED%84%B0%EB%A7%81CF
- 참고링크2 : https://data-science-hi.tistory.com/82
- 참고링크3 : https://angeloyeo.github.io/2019/08/01/SVD.html
- 참고링크4 : https://angeloyeo.github.io/2019/07/17/eigen_vector.html
고유값과 고유벡터
벡터 $\vec{x}$에 임의의 행렬 $n \times n$ A 선형 변환 시킨다면 원래의 것과 다른 벡터가 나오게 되는데 특정한 벡터와 행렬은 선형 변환을 취했을때 크기만 바뀌고 방향은 바뀌지 않을 수 있습니다.
이때 아래와 같이 $A \vec{x}$의 결과가 $\lambda \vec{x}$라면
$$A \vec{x} = \lambda \vec{x}$$
이때 $\lambda$는 고유값, $\vec{x}$ 고유벡터라고 합니다.
즉 선형변환을 하였을 때 방향이 바뀌지 않은 vector를 고유벡터($\vec{x}$), 그 vector($\vec{x}$)의 크기($\lambda$)를 고유값이라고 합니다.
특이값 분해(Singular Value Decomposition) - SVD
특이값 분해는 $m \times n$으로 이루어진 행렬($R$)에 대하여 3개의 행렬로 분해하는 것을 말합니다.
$$R = Q \sum P^{T}$$
이때 $Q$는 $m \times k$, $\sum$은 $k \times k$, $P$는 $n \times k$의 행렬이 됩니다.
행렬 $Q$는 $RR^{T}$행렬의 $k$개의 가장 큰 고유벡터를 가지고있고 Left Singular Matrix라고 하며 행렬 $P$는 $R^{T}R$의 $k$개의 가장 큰 고유벡터를 가지고있고 Right Singular Matrix라고 합니다.
행렬 $\sum$은 각 행렬의 $k$개의 가장 큰 고유값의 제곱근(특이값 - Singular Value)을 가지고 있습니다.
행렬 $Q$, $P$는 차원축소가 되면 각각 $k$차원의 잠재요인을 담고 있습니다.
3개로 분해된 행렬 $Q$, $\sum$, $P$는 아래와 같이 행렬 2개로 정의할 수 있습니다.
$$U = Q \sum$$
$$V = P$$
위의 행렬 $U$, $V$로 $\hat{R}$을 예측한다면 다음과 같이 정의할 수 있습니다.
$$\hat{R} \approx UV^{T}$$
위 모양은 확률적 경사하강법(SGD)에서 사용한 행렬의 모양과 같으며 SGD처럼 행렬분해를 이용하여 빈공간(NULL)을 채워 사용자가 아직 평가하지 않은 아이템의 평점을 예측하여 추천에 활용할 수 있습니다.
특이값분해(SVD)를 활용하여 차원을 축소하고 행렬곱을 이용하여 다시 행렬을 완성시키고
빈공간(NULL)값에 대하여 완성된 행렬에서 채워주고 채워진 행렬을 다시 SVD를 이용하여 값이 최적화 될 때 까지 반복하면서 평점을 채울 수 있습니다.
그리고 bias를 줄이기 위하여 초기에 SVD를 수행하기 전에 빈공간(NULL)을 0으로 채우지 않고, 행의 평균으로 채워 bias를 줄일 수 있습니다.
위의 과정을 정리하면 다음과 같습니다.
1. 초기화 : $R$의 $i$번째 행에서 누락된 항목을 해당 행의 평균 $\mu_{i}$으로 초기화하여 $R_{f}$를 만듭니다.
2. 반복 1 : $Q \sum P^{T}$ 형식으로 $R_{f}$의 rank-$k$ SVD를 수행합니다.
3. 반복 2 : $R_{f}$의 빈공간(NULL)만 $Q \sum P$의 해당 값으로 재조정하고 최적화가 될 때까지 반복 1로 이동합니다.
실습 예제
import numpy as np
import pandas as pd
R = pd.DataFrame([[1, -1, 1, -1, 1, -1],
[1, 1, None, -1, -1, -1],
[None, 1, 1, -1, -1, None],
[-1, -1, -1, 1, 1, 1],
[-1, None, -1, 1, 1, 1]])
#각 행의 평균
m = R.mean(axis=1)
#원본 행렬 R에 평균 채우기
R_f = R.T.fillna(m).T
예제에 사용할 행렬 $R$을 데이터프레임의 형태로 만들어주었습니다.
각 행의 평균($m$)을 구하고 행렬 $R$에 평균 $m$을 채워 $R_{f}$를 만들어주었습니다.
null_index = []
for i in R.index:
for j in R.columns:
if R.isnull().loc[i,j]:
null_index.append((i, j))
사용자가 아직 평가하지 않은 데이터(NULL)값을 찾아 NULL값의 위치정보를 null_index 리스트에 저장했습니다.
epochs = 5
SVD = TruncatedSVD(n_components=2)
for epoch in range(epochs):
U = SVD.fit_transform(R_f)
V = SVD.components_
SVD_R = pd.DataFrame(U.dot(V))
for i, j in null_index:
R_f.loc[i, j] = SVD_R.loc[i, j]
display(R_f)
전체 반복 횟수(epochs)는 5회로 지정하고 sklearn의 TruncatedSVD를 이용하여 $U$($Q \sum$), $V$($P$)를 만들수 있습니다.
5회동안 $U$와 $V$를 행렬곱하여 SVD_R이라고 하는 완성된 행렬을 생성하고 행렬 R_f의 NULL값을 SVD_R로 채워주는 동작을 반복하였습니다.
빨간색 테두리 처럼 사용자가 아직 평가하지 않은 아이템의 값의 변화를 확인할 수 있습니다.
확률적 경사하강법(SVD)와 다르게 5~10회 정도의 적은 반복으로도 값을 최적화할 수 있습니다.
또한 MSE, RMSE, MAE와 같은 평가지표를 활용한다면 모델의 성능을 측정할 수 있습니다.
1.초기화 과정에서는 여기서 행의 평균을 이용하였지만 행렬의 많은 부분이 NULL값이 었다면 이웃 모델과 같은 방법을 이용한 초기화를 진행한다면 수렴속도도 빨라지고 더욱 정확한 결과를 얻을 수 있습니다.
'추천시스템' 카테고리의 다른 글
[추천시스템] (잠재요인)모델 기반 협업 필터링(4) - NMF (1) | 2023.01.17 |
---|---|
[추천시스템] (잠재요인) 모델 기반 협업 필터링(2) - 경사하강법(SGD) (2) | 2023.01.11 |
[추천시스템] (잠재요인) 모델 기반 협업 필터링(1) - 경사하강법(SGD) (0) | 2023.01.10 |
[추천시스템] 이웃 기반 협업 필터링(2) - 아이템 기반 (0) | 2023.01.03 |
[추천시스템] 카카오 Mini Reco 기출문제 회고 (2) | 2023.01.01 |