[추천시스템] 01. 기본적인 추천시스템

‍강민석·2022년 7월 7일
2

추천시스템

목록 보기
2/5

이번 1장에서는 가장 기본적인 추천시스템을 MovieLens 데이터를 통해 실습합니다.
이후 장에서도 같은 MovieLens 데이터를 사용합니다.


기본적인 추천시스템

📝 Dataset : MovieLens 100K 데이터 (영화 평가 데이터셋)

  • 사용자 데이터 (u.user), 영화 정보 데이터 (u.item), 영화 평가 데이터 (u.data) - 3가지 파일로 구성

데이터 읽기

import os
import pandas as pd

# u.user 파일 읽기
base_src = 'drive/myDrive/RecoSys/data' #경로 지정
u_user_src = os.path.join(base_src, 'u.user')
u_cols = ['user_id', 'age', 'sex', 'occupation', 'zip_code']
users = pd.read_csv(u_user_src,
                    sep='|', #구분자 기호
                    names=u_cols,
                    encoding='latin-1')
users = users.set_index('user_id')

# u.item 파일 읽기
u_item_src = os.path.join(base_src, 'u.item')
i_cols = ['movie_id', 'title', 'release data', 'video release data',
          'IMDB URL', 'unknown', 'Action', 'Adventure', 'Animation',
          'Children\'s', 'Comedy', 'Crime', 'Documentary', 'Drama', 'Fantasy',
          'Film-Noir', 'Horror', 'Musical', 'Mystery', 'Romance', 'Sci-Fi',
          'Thriller', 'War', 'Western']
movies = pd.read_csv(u_item_src,
                    sep='|', #구분자 기호
                    names=i_cols,
                    encoding='latin-1')
movies = movies.set_index('movie_id')

# u.data 파일 읽기
u_data_src = os.path.join(base_src, 'u.data')
r_cols = ['user_id', 'movie_id', 'rating', 'timestamp']
ratings = pd.read_csv(u_data_src,
                    sep='\t', #구분자 기호
                    names=r_cols,
                    encoding='latin-1')
ratings = ratings.set_index('user_id')

users

movies

ratings

인기제품 방식 추천

개별 사용자 정보가 존재하지 않을 때 사용할 수 있는 방법
모든 사람에게 가장 인기 있는 상품을 추천한다.

# 인기제품 방식 추천 함수
def recom_movie(n_items):
	movie_mean = ratings.groupby(['movie_id'])['rating'].mean() # 각 movie별로 평점 평균
    movie_sort = movie_mean.sort_values(ascending=False)[:n_items] # 평점이 높은 순으로 정렬, n_item만큼 추출
    recom_movies = movies.loc[movie_sort.index] # 영화 제목을 추출하기 위해 index를 이용
    recommendations = recom_movies['title'] # 영화 제목으로 추천
    return recommendations

recom_movie(5) ### 평균 평점이 가장 높은 5개의 영화를 추천 

추천 결과

추천시스템 정확도 측정

영화 평점 실제값과 인기제품 방식으로 구한 예측값으로 RMSE를 계산해 추천시스템의 정확도를 측정할 수 있다.

# RMSE 함수
def RMSE(y_true, y_pred):
    return np.sqrt(np.mean((np.array(y_true) - np.array(y_pred))**2))

# 정확도 계산
rmse = []
movie_mean = ratings.groupby(['movie_id'])['rating'].mean()

for user in set(ratings.index): #중복 제거
    y_true = ratings.loc[user]['rating']
    # 인기제품 방식
    y_pred = movie_mean(ratings.loc[user]['movie_id'])
    accuracy = RMSE(y_true, y_pred)
    rmse.append(accuracy)

# RMSE 계산
print(np.mean(rmse))

약 0.996의 RMSE가 나온다. 실제 평점과 예측 평점이 약 0.996의 차이를 가진다는 것을 의미한다.

사용자 집단별 추천

앞선 인기제품 방식 추천은 모든 대상에게 같은 추천결과를 보여준다. 그러나, 특정 집단에 따라 다른 취향을 가질 수 있으므로 인기제품 방식의 추천은 대부분의 경우에 적합하지 않다. 즉, 특정 집단에 알맞은 추천을 해야 할 필요가 있다.

다음 MovieLens 실습에서는 성별에 따른 인기제품 방식 추천을 수행한다. 따라서, 남성과 여성 각 집단에서 평균 평점이 가장 높은 영화를 추천하게 된다.

# 데이터 읽기
# 앞선 과정에서 3가지 데이터를 set_index하는 부분을 제거합니다. (생략)
# ratings, movies : 필요없는 column 제거
ratings = ratings.drop('timestamp', axis=1)
movies = movies[['movie_id', 'title']]

# 데이터 분할
from sklearn.model_selection import train_test_split
x = ratings.copy()
y = ratings['user_id']

x_train, x_test, y_train, y_test = train_test_split(x, y,
                                             test_size=0.25,
                                             stratify=y)

# 모델별 RMSE를 계산하는 함수
def score(model):
    id_pairs = zip(x_test['user_id'], x_test['movie_id'])
    y_pred = np.array([model(user,movie) for (user,movie) in id_pairs])
    y_true = np.array(x_test['rating'])
    return RMSE(y_true, y_pred)
    
# train 데이터와 유저 정보 데이터 합치기
merged_ratings = pd.merge(x_train, users)

users = users.set_index('user_id')

# 성별 기준으로 평균 groupby
g_mean = merged_ratings[['movie_id', 'sex', 'rating']].groupby(['movie_id', 'sex'])['rating'].mean()

# ratings matrix 생성 : gender기준으로 추천할 때, 사용
rating_matrix = x_train.pivot(index='user_id',
                              columns='movie_id',
                              values='rating') 

g_mean

각 영화의 성별에 따른 평균 평점을 나타낸 결과이다.
성별에 따라 평균 평점이 가장 높은 영화를 추천된다.

# Gender 기준 추천
def cf_gender(user_id, movie_id):
    if movie_id in rating_matrix.columns:
        gender = users.loc[user_id]['sex']
        if gender in g_mean[movie_id].index:
            gender_rating = g_mean[movie_id][gender]
        else:
            gender_rating = 3.0
    else:
        gender_rating = 3.0
        
    return gender_rating

score(cf_gender)

성별에 따른 추천 결과, RMSE는 약 1.037이 나온다. 앞서 성별로 나누어 추천하지 않은 경우에 비해 RMSE가 증가해 성능이 안 좋아진 것으로 볼 수 있으나, 이번에는 데이터셋을 분할하고 test set에 대해서 RMSE를 계산했기 때문에 이러한 결과가 나온 것이다.

profile
데이터분석가

1개의 댓글

comment-user-thumbnail
2022년 12월 2일

안녕하세용, surprise 패키지는 내장 데이터로만 알고리즘이 작동하는건가요?

답글 달기