in DATA on Data, Python, Recommend, Contents-filter, 컨텐츠기반, 필터링, 추천시스템입문
잠재요인 최근접 이웃 이해하기
importnumpyasnp# 원본 행렬 R 생성
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.shapeK=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))
fromsklearn.metricsimportmean_squared_errordefget_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]fornon_zeroinnon_zeros]y_non_zero_ind=[non_zero[1]fornon_zeroinnon_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)returnrmse
non_zeros=[(i,j,R[i,j])foriinrange(num_users)forjinrange(num_items)ifR[i,j]>0]steps=1000learning_rate=0.01r_lambda=0.01# SGD 기법으로 P와 Q 매트릭스를 계속 업데이트.
forstepinrange(steps):fori,j,rinnon_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)if(step%50)==0:print("### iteration step : ",step," rmse : ",rmse)
# name 키에 해당하는 값만 추출
movies_df["genres"]=movies_df["genres"].apply(lambdax:[y["name"]foryinx])movies_df["keywords"]=movies_df["keywords"].apply(lambdax:[y["name"]foryinx])movies_df[["genres","keywords"]][:1]
genres
keywords
0
[Action, Adventure, Fantasy, Science Fiction]
[culture clash, future, space war, space colony, society, space travel, futuristic, romance, spa...
fromsklearn.feature_extraction.textimportCountVectorizer# Count Vectorizer를 적용하기 위해 공백문자로 word 단위로 구분되는 문자열로 반환
movies_df["genres_literal"]=movies_df["genres"].apply(lambdax:(' ').join(x))count_vect=CountVectorizer(min_df=0,ngram_range=(1,2))genre_mat=count_vect.fit_transform(movies_df["genres_literal"])print(genre_mat.shape)
# 장르 유사도에 따라 영화를 추천하는 함수 생성
deffind_sim_movie(df,sorted_ind,title_name,top_n=10):# 인자로 입력된 title_name인 df 추출
title_movie=df[df["title"]==title_name]title_index=title_movie.index.values# top_n개의 index 추출
similar_indexes=sorted_ind[title_index,:(top_n)]print(similar_indexes)similar_indexes=similar_indexes.reshape(-1)returndf.iloc[similar_indexes]
# 장르 유사도에 따라 영화를 추천하는 함수 생성
deffind_sim_movie(df,sorted_ind,title_name,top_n=10):# 인자로 입력된 title_name인 df 추출
title_movie=df[df["title"]==title_name]title_index=title_movie.index.values# top_n X 2개의 index 추출
similar_indexes=sorted_ind[title_index,:(top_n*2)]similar_indexes=similar_indexes.reshape(-1)# 기준영화 인덱스 제외
similar_indexes=similar_indexes[similar_indexes!=title_index]returndf.iloc[similar_indexes].sort_values("weighted_vote",ascending=False)[:top_n]similar_movies=find_sim_movie(movies_df,genre_sim_sorted_ind,"The Godfather",10)similar_movies[["title","vote_average"]]