본문 바로가기
Learning-driven Methodology/ML (Machine Learning)

[Machine Learning] 사용자기반 협업 필터링 (User-based Filtering)

by goatlab 2022. 12. 7.
728x90
반응형
SMALL

사용자기반 협업 필터링 (User-based Filtering)

 

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pylab as plt
from sklearn.metrics import mean_squared_error
from datetime import datetime
%matplotlib inline

movies = pd.read_csv('movies.csv')    # 영화 정보
ratings = pd.read_csv('ratings.csv')  # 사용자가 영화에 대해 남긴 평점 데이터

print(movies.shape)
print(ratings.shape)
movies.head()

# 제목으로 영화 찾기
movies[movies.title.str.contains("Die", case = False)]

# 영화 장르 리스트
{j for i in movies.genres.str.split('|') for j in i}
ratings.head()

# 사용자 수와 사용자 당 평점 횟수
ratings.userId.nunique(), ratings.shape[0] / ratings.userId.nunique()
# 평점 데이터가 일부 위치에만 존재하는 sparse 행렬
ratings.pivot_table('rating', index='userId', columns='movieId').iloc[212:222, 808:817].fillna("")

plt.figure(figsize=(20,20))

df = ratings.pivot_table('rating', index='userId', columns='movieId', fill_value=0)

sns.heatmap(df, cmap='YlGnBu')

plt.show()

# Density level (%)
density = ratings.shape[0] / (ratings.userId.nunique() * ratings.movieId.nunique())

f'{round(density*100,2)}%'
# 평점 분포
print(ratings.describe().rating)

sns.histplot(ratings.rating, bins=10, kde=True); plt.show()

# 사용자 ID
myId = int(input("사용자 id 입력 : "))
print("제시되는 영화에 대해 10개까지 평점(1~5점)을 입력")
print("잘 모르는 영화일 경우 평점을 낮게 주거거나 Enter키")

movies_for_rating = pd.read_csv('movies_for_rating.csv')
my_ratings = []
count = 0

df = movies_for_rating.sample(frac=1)
for i in range(df.shape[0]):
    my_rating = input(f"{df.iloc[i].title}: ")
    try:
        score = int(my_rating)
        if score < 1 or score > 5:
            continue
        my_ratings.append((myId, df.iloc[i].movieId, score, int(datetime.now().timestamp())))
        count += 1
    except:
        continue
    if count >= 10:
        break

my_ratings = pd.DataFrame(my_ratings, columns=ratings.columns)
my_ratings.to_csv('my_ratings.csv', index=False)
print("\nMy rating summary:\n", my_ratings.merge(df)[['title', 'genre', 'rating']])

ratings = pd.concat([ratings, my_ratings]).reset_index(drop=True)
# title 컬럼을 얻기 이해 movies와 조인 수행
rating_movies = pd.merge(ratings, movies, on='movieId')

# 행은 userID, 열은 title로 pivoting 수행. rating이 없는 값(NaN)은 모두 0으로 변환
ratings_matrix = rating_movies.pivot_table('rating', index='userId', columns='title', fill_value=0)

print(ratings_matrix.shape)

ratings_matrix.head()
from sklearn.metrics.pairwise import cosine_similarity

# cosine_similarity()는 행을 기준으로 유사도를 계산
user_sim = cosine_similarity(ratings_matrix, ratings_matrix)

# cosine_similarity()로 반환된 numpy 행렬에 영화명을 매핑하기 위해 DataFrame으로 변환
user_sim = pd.DataFrame(user_sim, ratings_matrix.index, ratings_matrix.index)

print(user_sim.shape)

user_sim.head()
# 나와 유사도가 높은 상위 5명 리스트
user_sim.loc[myId].sort_values(ascending=False)[1:6]
# 유사도가 가장 높은 이웃의 수 설정
K = int(input("유사도가 높은 이웃의 수 설정 : "))
%%time

# 위의 평점예측 수식을 아래와 같이 구현함
R, S = ratings_matrix.values, user_sim.values

# 사용자-아이템 평점 행렬 크기만큼 0으로 채운 예측 행렬 초기화
ratings_pred = np.zeros(R.shape)

# 사용자-아이템 평점 행렬의 열 크기만큼 Loop 수행. 
for u in range(R.shape[0]):
    # 유사도 행렬에서 유사도가 큰 순으로 n개 데이터 행렬의 index 반환
    top_k = np.argsort(S[:, u])[::-1][1:K+1]
    
    # 개인화된 예측 평점을 계산
    for i in range(R.shape[1]):
        ratings_pred[u, i] = S[u, :][top_k].dot(R[:,i][top_k].T) 
        ratings_pred[u, i] /= np.sum(np.abs(S[u, :][top_k]))
        
ratings_pred = pd.DataFrame(ratings_pred, ratings_matrix.index, ratings_matrix.columns)
ratings_pred
# 추천 영화의 수 (Top-K) & 추천 대상 설정
N = int(input("추천 영화의 수 설정 : "))
uid = myId
# 내가 좋아하는 영화
like_movies = ratings.query('userId == @uid and rating >= 4').movieId
movies.query('movieId in @like_movies')

# id로 지정된 사용자의 모든 영화정보 추출하여 Series로 반환함
# 반환된 user_rating은 영화명(title)을 index로 가지는 Series 객체임 
user_rating = ratings_matrix.loc[uid,:]
    
# user_rating이 0보다 크면 기존에 관람한 영화임. 대상 index를 추출하여 list로 만듬
already_seen = user_rating[user_rating > 0].index.tolist()
   
# list comprehension으로 already_seen에 해당하는 movie는 movies_list에서 제외함
unseen_list = [movie for movie in ratings_matrix.columns.tolist() if movie not in already_seen]
    
# unseen_list에서 가장 평점이 높은 N개의 영화를 추천함 
recomm_items = ratings_pred.loc[uid, unseen_list].sort_values(ascending=False)[:N]
list(recomm_items.index)
# 사용자가 평점을 부여한 영화에 대해서만 예측 성능 평가 RMSE를 구함
actual, pred = ratings_matrix.values, ratings_pred.values     
pred = pred[actual.nonzero()].flatten()
actual = actual[actual.nonzero()].flatten()
rmse = np.sqrt(mean_squared_error(pred, actual))
print(f'RMSE:', rmse)
728x90
반응형
LIST