[ Recommender System ]

( 참고 : Fastcampus 추천시스템 강의 )

8. [code] Neighborhood-based CF

Neighborhood-based CF에는 아래와 같이 크게 2가지가 있다. 이 두 가지 알고리즘을 구현해볼 것이다.

  • 1) Item-based CF
  • 2) User-based CF


데이터 소개

figure2


1. Sparse Matrix 만들기

  • Movie ID x User ID의 sparse matrix를 만든다

    ( Movie ID: 8938개, User ID: 610개 )

  • pandas의 unstack 사용하면 효율적으로 가능!

sparse_matrix = train_df.groupby('movieId').apply(lambda x: pd.Series(x['rating'].values, index=x['userId'])).unstack()
sparse_matrix.index.name = 'movieId'

sparse_matrix


2. Cosine Similarity

from sklearn.metrics.pairwise import cosine_similarity

def cossim_matrix(a, b):
    cossim_values = cosine_similarity(a.values, b.values)
    cossim_df = pd.DataFrame(data=cossim_values, columns = a.index.values, index=a.index)

    return cossim_df


3. Item-based CF

figure2

  • 평가를 하지 않은 항목에 대해서는 0으로 채워넣는다
  • 사람들의 평점 부여 정보를 바탕으로, item(영화)간의 코사인 유사도 행렬을 구한다.
item_sparse_matrix = sparse_matrix.fillna(0)
item_cossim_df = cossim_matrix(item_sparse_matrix, item_sparse_matrix)
  • Train 데이터의 유저 정보를 바탕으로, 예측을 한다
userId_grouped = train_df.groupby('userId')

item_prediction_result_df = pd.DataFrame(index=list(userId_grouped.indices.keys()), columns=item_sparse_matrix.index)
item_prediction_result_df
for userId, group in tqdm(userId_grouped):
    user_sim = item_cossim_df.loc[group['movieId']]
    user_rating = group['rating']
    sim_sum = user_sim.sum(axis=0)
    
    pred_ratings = np.matmul(user_sim.T.to_numpy(), user_rating) / (sim_sum+1)
    item_prediction_result_df.loc[userId] = pred_ratings

figure2


4. User-based CF

figure2

  • 평가를 하지 않은 항목에 대해서는 0으로 채워넣는다
  • 사람들의 평점 부여 정보를 바탕으로, user간의 코사인 유사도 행렬을 구한다.
user_sparse_matrix = sparse_matrix.fillna(0).transpose()
user_cossim_df = cossim_matrix(user_sparse_matrix, user_sparse_matrix)
  • Train 데이터의 유저 정보를 바탕으로, 예측을 한다
movieId_grouped = train_df.groupby('movieId')
user_prediction_result_df = pd.DataFrame(index=list(movieId_grouped.indices.keys()), columns=user_sparse_matrix.index)

for movieId, group in tqdm(movieId_grouped):
    user_sim = user_cossim_df.loc[group['userId']]
    user_rating = group['rating']
    sim_sum = user_sim.sum(axis=0)

    pred_ratings = np.matmul(user_sim.T.to_numpy(), user_rating) / (sim_sum+1)
    user_prediction_result_df.loc[movieId] = pred_ratings

figure2


5. Evaluation

def evaluate(test_df, prediction_result_df):
  groups_with_movie_ids = test_df.groupby(by='movieId')
  groups_with_user_ids = test_df.groupby(by='userId')
  intersection_movie_ids = sorted(list(set(list(prediction_result_df.columns)).intersection(set(list(groups_with_movie_ids.indices.keys())))))
  intersection_user_ids = sorted(list(set(list(prediction_result_df.index)).intersection(set(groups_with_user_ids.indices.keys()))))
  compressed_prediction_df = prediction_result_df.loc[intersection_user_ids][intersection_movie_ids]
  
  grouped = test_df.groupby(by='userId')
  result_df = pd.DataFrame(columns=['rmse'])
  for userId, group in tqdm(grouped):
      if userId in intersection_user_ids:
          pred_ratings = compressed_prediction_df.loc[userId][compressed_prediction_df.loc[userId].index.intersection(list(group['movieId'].values))]
          pred_ratings = pred_ratings.to_frame(name='rating').reset_index().rename(columns={'index':'movieId','rating':'pred_rating'})
          actual_ratings = group[['rating', 'movieId']].rename(columns={'rating':'actual_rating'})

          final_df = pd.merge(actual_ratings, pred_ratings, how='inner', on=['movieId'])
          final_df = final_df.round(3) 

  return final_df
user_CF_result = evaluate(test_df, user_prediction_result_df)
item_CF_result = evaluate(test_df, item_prediction_result_df)

RMSE 비교

  • user-based CF : 1.695
  • item-based CF : 0.815
rmse_user_CF = np.sqrt(mean_squared_error(user_CF_result['actual_rating'].values,
                           user_CF_result['pred_rating'].values))
rmse_item_CF = np.sqrt(mean_squared_error(item_CF_result['actual_rating'].values,
                           item_CF_result['pred_rating'].values))

Categories:

Updated: