2021. 8. 25. 18:54ㆍRecommender System
추천시스템에는 콘텐츠 기반 필터링(content based filtering), 협업 필터링(collaborative filtering) 이 있다.
최근에는 협업 필터링이 많이 사용되고 있다. 또 협업 필터링은 최근접 이웃기반 협업 필터링과 잠재요인 협업 필터링을 나뉜다. 사용자가 아이템에 남긴 평점, 구매이력 등의 사용자 행동양식을 기반해주는 것이다.
최근접 이웃기반 협업 필터링은 유사한 아이템, 또는 사용자에 기반해 추천해주는 시스템이다.
잠재요인 협업 필터링은 행렬분해(matricx factorization)에 기반한다. 대규모 다차원 행렬을 SVD와 같은 차원 감소 기법으로 분해하는 과정에서 잠재요인을 찾아내 뽑아내는 과정이다.
사용자-아이템 행렬을 사용자-잠재요인, 잠재요인-아이템 행렬로 분해한다. 잠재요인이 정확히 무엇인지 알 수는 없다.
행렬 값을 기반으로 latent factor score를 매길 수 있고 사용자가 사용하지 않은 아이템의 평점을 예측할 수 있다. 그리고 이 값이 높으면 추천하는 시스템이다.
산림프로그램 추천시스템 구현하기
산림빅데이터 거래소 산림치유프로그램 설문 데이터를 통해서 user의 프로그램에 대한 평점을 데이터로 잠재요인협업필터링을 구현할 것이다.
콘텐츠를 기반으로 하는 필터링은 사용자의 history를 반영하기 어렵기 때문에 잠재요인협업필터링을 선택했다.
데이터 파일이 엄청 많고 순서도 섞여 있어 전처리가 어려웠다. 전처리가 제일 싫어...
설문조사 중에서 유형이 프로그램만족도에 해당하는 행을 뽑아냈다. 필요한 데이터는 userid, 프로그램명, 프로그램id, 평점이므로 drop으로 필요없는 열 삭제하고 공통 열이름은 id를 기준으로 df를 합쳤다.
그리고 array로 프로그램명을 확인해보니 오타가 너무 많았다. 그래서 오타는 결국 엑셀 필터로 하나하나 찾아내 삭제했다. replace를 사용해 매우만족, 만족, 보통, 불만족, 매우불만족을 5,4,3,2,1 로 바꿔 주었다.
from sklearn.decomposition import TruncatedSVD from scipy.sparse.linalg import svds import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import numpy as np import warnings warnings.filterwarnings("ignore")
rating_data = pd.read_csv('./rating_data2.csv',encoding = 'cp949',index_col =0 ) rating_data
df 형태를 바꿔주어야한다.
# pivot_table로 사용자-프로그램명 평점 데이터 만들어주기 # 평점 매기지 않아 비어있는 NaN를 0으로 채워주기 user_pro_rating = rating_data.pivot_table( '설문 답변 내용', index = '설문 답변자 고유번호', columns='제목').fillna(0)
user_pro_rating.shape
(736, 98)
사용자 736명 프로그램98개의 데이터이다.
1. 특정프로그램과 비슷한 프로그램 추천 해주기
# .T를 통해 transpose 시켜주면 프로그램-사용자 데이터 만들어주기 pro_user_rating = user_pro_rating.values.T pro_user_rating.shape
SVD 분해
SVD = TruncatedSVD(n_components=12) # SVD 특이값 분해 상위 12개 뽑아내기 matrix = SVD.fit_transform(pro_user_rating) # 프로그램명 데이터가 12개의 어떤 요소 값을 가지게 된다. matrix.shape
matrix[0]
피어쓴 상관관계수 구하기
corr = np.corrcoef(matrix) corr.shape
corr2 = corr[:98, :98] corr2.shape
# heatmap 만들기 plt.figure(figsize=(16, 10)) sns.heatmap(corr2)
# 특정 프로그램 선정 pro_title = user_pro_rating.columns pro_title_list = list(pro_title) coffey_hands = pro_title_list.index("아로마 마사지")
# 특정 프로그램과 상관계수가 높은(0.9이상) 프로그램 찾기 corr_coffey_hands = corr[coffey_hands] list(pro_title[(corr_coffey_hands >= 0.9)])[:10]
'아로마 마사지'와 상관계수가 높은 프로그램은
['나이트워크', '다담치유', '물, 열치유', '숲 명상', '숲길 걷기', '아로마 마사지']과 같다. 비슷한 영화를 추천해줄 수 있다.
2. 사용자 맞춤 개인 추천 시스템 구현하기
df_ratings = pd.read_csv('./rating_data_no2.csv',encoding='cp949' , index_col =0 ) df_pro = pd.read_csv('./pro_data2.csv',encoding='cp949' ) #프로그램명, 프로그램id 데이터
pivot 을 통해서 사용자별 프로그램에 대한 평점 보여주는 df 만들기
# pivot은 중복된 행이 없어야한다. df_user_pro_ratings = df_ratings.pivot( index='설문 답변자 고유번호', columns='설문 문제 일련번호', values='설문 답변 내용' ).fillna(0)
N명의 사용자들의 각각의 평균 평점을 구하고 빼준다.
# matrix는 pivot_table 값을 numpy matrix로 만든 것 matrix = df_user_pro_ratings.values # user_ratings_mean은 사용자의 평균 평점 user_ratings_mean = np.mean(matrix, axis = 1) # R_user_mean : 사용자-영화에 대해 사용자 평균 평점을 뺀 것. matrix_user_mean = matrix - user_ratings_mean.reshape(-1, 1)
pd.DataFrame(matrix_user_mean, columns = df_user_pro_ratings.columns).head()
# scipy에서 제공해주는 svd. # U 행렬, sigma 행렬, V 전치 행렬을 반환. U, sigma, Vt = svds(matrix_user_mean, k = 12)
print(U.shape) print(sigma.shape) print(Vt.shape)
(736, 12)
(12,)
(12, 232)
# 1차원 행렬을 0이 포함된 대칭행렬론 만들기 sigma = np.diag(sigma)
sigma.shape
(12, 12)
SVD를 통해 행렬을 분해한 상태이다. 이제 원본 행렬로 복구해야한다.
U, sigma, Vt의 내적을 수행해야한다.
# U, Sigma, Vt의 내적을 수행하면, 다시 원본 행렬로 복원이 된다. # 거기에 + 사용자 평균 rating을 적용한다. svd_user_predicted_ratings = np.dot(np.dot(U, sigma), Vt) + user_ratings_mean.reshape(-1, 1)
df_svd_preds = pd.DataFrame(svd_user_predicted_ratings, columns = df_user_pro_ratings.columns) df_svd_preds.head()
SVD 값을 이용해 행렬 분해를 기반으로 데이터를 변경해주었다.
def recommend_movies(df_svd_preds, user_id, ori_movies_df, ori_ratings_df, num_recommendations=5): #현재는 index로 적용이 되어있으므로 user_id - 1을 해야함. user_row_number = user_id - 1 # 최종적으로 만든 pred_df에서 사용자 index에 따라 영화 데이터 정렬 -> 영화 평점이 높은 순으로 정렬 됌 sorted_user_predictions = df_svd_preds.iloc[user_row_number].sort_values(ascending=False) # 원본 평점 데이터에서 user id에 해당하는 데이터를 뽑아낸다. user_data = ori_ratings_df[ori_ratings_df['설문 답변자 고유번호'] == user_id] # 위에서 뽑은 user_data와 원본 영화 데이터를 합친다. user_history = user_data.merge(ori_movies_df, on = '설문 문제 일련번호').sort_values(['설문 답변 내용'], ascending=False) # 원본 영화 데이터에서 사용자가 본 영화 데이터를 제외한 데이터를 추출 recommendations = ori_movies_df[~ori_movies_df['설문 문제 일련번호'].isin(user_history['설문 문제 일련번호'])] # 사용자의 영화 평점이 높은 순으로 정렬된 데이터와 위 recommendations을 합친다. recommendations = recommendations.merge( pd.DataFrame(sorted_user_predictions).reset_index(), on = '설문 문제 일련번호') # 컬럼 이름 바꾸고 정렬해서 return recommendations = recommendations.rename(columns = {user_row_number: 'Predictions'}).sort_values('Predictions', ascending = False).iloc[:num_recommendations, :] return user_history, recommendations
already_rated, predictions = recommend_movies(df_svd_preds, 90, df_pro, df_ratings, 10)
90 사용자에게 10개의 프로그램을 추천해준다.
90 사용자가 이번에 체험한 프로그램은 다음과 같다.
already_rated.head(10)
predictions
다음과 같은 프로그램을 추천해줄 수있다.
'Recommender System' 카테고리의 다른 글
잡케어 추천 알고리즘 프로젝트 기록, 데이콘 공모전 후기 (0) | 2022.01.18 |
---|---|
콘텐츠 기반 필터링(Content-based filtering) (0) | 2022.01.14 |
Word2Vec을 활용한 추천시스템 (0) | 2022.01.12 |