Contents
접기
경사하강법을 이용한 행렬 분해
SVD(Singular Value Decomposition)는 NaN 값이 없는 행렬에만 적용할 수 있다.
R 행렬(사용자-영화 평점 행렬)은 평점되지 않은 많은 NaN 값이 있기 때문에 P(사용자-잠재요인 행렬)와 Q(잠재요인-아이템 행렬) 행렬을 일반적인 SVD 방식으로 분해할 수 없다.
행렬 분해 로직 함수
R(m x n) = P(m x K) * Q.T(K x n) [ m: user 수, n: 아이템 수, K: 잠재요인 수 ]
R : 사용자-영화 평점 행렬
P : 사용자-잠재요인 행렬 (초기 값은 랜덤 값으로)
Q : 잠재요인-아이템 행렬 (초기 값은 랜덤 값으로)
행렬 분해 함수 매개변수
R: 사용자 - 아이템 평점 행렬 (실제 평점 값을 갖는 행렬)
K: 잠재요인 차원 수 (임의 지정)
step : 반복 학습 횟수
learning_rate : 학습률
r_lambda : L2 규제 강도
import numpy as np
def matrix_factorization(R, K, steps = 200, learning_rate = 0.01, r_lambda = 0.01):
num_users, num_items = R.shape
# 예측 평가 행렬을 위한 임의의 값 설정
np.random.seed(1)
P = np.random.normal(scale= 1/K, size= (num_users,K)) # randn: 표준정규 분포 내에서 <> noraml : 표준편차 지정 가능
Q = np.random.normal(scale= 1/K, size= (num_items,K))
# R > 0 인 행 위치, 열 위치, 값(평점) 을 리스트에 저장 (평점이 있는 위치 정보 및 평점 점보를 저장)
non_zeros = [ (i, j, R[i,j]) for i in range(num_users) for j in range(num_items) if R[i,j] > 0 ] # 사용자 개수만큼 <아이템 개수 만큼>
# 실제로 평점값이 존재하는 위치와 그 값
for step in range(steps):
for i, j, r in non_zeros:
# 손실함수의 미분식을 가지고 P와 Q를 만듦
# 실제값돠 예측값의 차이인 오차 계산
eij = r - np.dot(P[i,:],Q[j,:].T)
# L2 규제가 적용된 손실함수의 미분식 :: 정해진 식
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,:])
return P, Q
L2 규제가 적용된 손실함수의 미분식 P[i,:] = P[i,:] + learning_rate * ( eij * Q[j,:] - r_lambda * P[i,:]) 과 유사하다.
경사 하강법 이용한 행렬 분해 실습
전처리
np.set_printoptions(precision= 2 , suppress= True)
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]]) # 4x5 아이템-평점 행렬
P, Q = matrix_factorization(R, K = 5, steps= 1000, learning_rate= 0.01, r_lambda= 0.01 )
pred_matrix = np.dot(P,Q.T)
print(R)
print()
print(pred_matrix)
[[ 4. nan nan 2. nan]
[nan 5. nan 3. 1.]
[nan nan 3. 4. 4.]
[ 5. 2. 1. 2. nan]]
[[3.99 1.51 1.15 2. 1.2 ]
[4.85 4.98 1.29 2.99 1. ]
[3.05 1.41 2.99 3.98 3.98]
[4.97 2. 1. 2. 0.82]]
데이터 로딩
import numpy as np
import pandas as pd
movies = pd.read_csv('./dataset/movies.csv')
ratings = pd.read_csv('./dataset/ratings.csv')
ratings.drop('timestamp', axis= 1)
rating_movies = pd.merge(ratings,movies, on='movieId')
ratings_matrix = rating_movies.pivot_table(index= 'userId', columns= 'title', values = 'rating')
ratings_matrix.fillna(0, inplace=True)
예측 평점 행렬 생성
P, Q = matrix_factorization(ratings_matrix.values, K = 50, steps=200, learning_rate= 0.01, r_lambda=0.01)
# ratings_matrix.values : numpy 다차원 배열의 값을 넣어야함
pred_matrix = np.dot(P,Q.T)
ratings_pred_matrix = pd.DataFrame(pred_matrix, index= ratings_matrix.index, columns= ratings_matrix.columns)
ratings_pred_matrix.head(3) #전체 모든 평균값을 예측 , 실제 관람한 것은 그것에 유사한 값
평점을 주지 않은 영화 리스트 반환
def get_unseen_movies(ratings_matrix, userId): # ratings_matrix : 정답을 가진 행렬
user_rating = ratings_matrix.loc[userId] # Series타입. 인덱스: 영화의 제목, 값: 평점
unseen_list = user_rating[user_rating == 0].index.values
return unseen_list
특정 사용자의 관람하지 않은 영화에 대한 예측 평점 기반 추천
def recomm_movie_by_userid(pred_df, userId, unseen_list, top_n = 10):
return pred_df.loc[userId,unseen_list].sort_values(ascending=False)[:top_n]
unseen_list = get_unseen_movies(ratings_matrix, 9)
recomm_movies = recomm_movie_by_userid(ratings_pred_matrix, 9, unseen_list)
#print(recomm_movies)
recomm_movies = pd.DataFrame(recomm_movies.values, index= recomm_movies.index, columns= ['pred_score'])
recomm_movies
영화 평점 기반의 사용자 - 아이템 행렬 데이터라면 영화가 가지는 장르별 특성 선호도를 가정할 수 있다.
"16. (추천) 아이템 기반 최근접 이웃 협업필터링.ipynb" 의 결과와 비교
영화 평점 기반의 사용자 - 아이템 행렬 데이터라면 영화가 가지는 장르별 특성 선호도를 가정할 수 있다.
'국비 교육 > 머신러닝, 딥러닝' 카테고리의 다른 글
[텍스트 마이닝] 텍스트 분석 -2 [실습] 20 뉴스 그룹 분류 (0) | 2024.08.01 |
---|---|
[텍스트 마이닝] 텍스트 분석 -1 (전처리 작업) (0) | 2024.08.01 |
[머신러닝] 추천 시스템 알고리즘 - 아이템 기반 최근접 이웃 필터링 (0) | 2024.08.01 |
[머신러닝] 추천 시스템 알고리즘 - 콘텐츠 기반 필터링 (0) | 2024.08.01 |