본문 바로가기
TIL - 외/추천시스템

[추천시스템] Memory Based Collaborative Filtering

by chaemj97 2023. 4. 11.
728x90

Collaborative Filtering

사용자와 아이템 간의 상호 상관 관계를 분석하여 새로운 사용자-아이템 관계를 찾아주는 것으로 사용자의 과거 경험과 행동 방식(User Behavior)에 의존하여 추천하는 시스템

 

책 추천 받을 때

1. 내가 좋아하는 장르, 작가, 출판사의 책 추천

-> Content Based Filtering

2. 나랑 비슷한 성향의 친구들이 읽은 책 책춘

-> Collaborative Filtering

 

Collaborative Filtering의 대표적인 한계점

  1. Cold Start
    • 문자 그대로 새로운 사용자나 새로운 아이템 등장 시, 기존의 관련된 경험 또는 행동 방식이 없기 때문에 추천이 곤란해지는 문제
  2. long Tail
    • 전체 추천 아이템으로 보이는 비율이 '사용자들의 관심을 많이 받은 소수의 아이템'으로 구성되어 있는 '비대칭적 쏠림 현상'이 발생하는 문제
  3. 계산 효율 저하
    • 계산량이 많은 알고리즘이기에, 사용자가 증가할수록 계산 시간이 더 길어지게 되며 효율성이 저하되는 문제

 

Memory-based Collaborative Filtering(MBCF)

User Based Collaborative Filtering

  • 동일한 사용자의 이웃 아이템의 점수를 기반으로 해당 아이템에 대한 사용자의 선호도 평가
  • 이미 평가했거나 상호작용한 사용자를 대상으로 하는 아이템과 유사한 아이템을 찾는 방법
  • 방법
    1. 평점을 기반으로 User-Item matrix 만들기
    2. 빈 값을 0으로 채우고, 사용자 사이의 유사도 계산
    3. 유사도를 이용해 가중평균을 구하여 유저의 아이템 선호도 예상

Item Based collaborative Filtering

  • 새로운 아이템을 평가할 때, 유사한 아이템에 대해 비슷한 점수들을 매긴 다른 사용자들을 찾은 후, 해당 사용자가 상호 작용한 적 없는 아이템에 대한 아이템의 사용자 점수를 예측하는 방법
    1. 평점을 기반으로 User-Item matrix 만들기
    2. 빈 값을 0으로 채우고, 아이템 사이의 유사도 계산
    3. 유사도를 이용해 가중평균을 구하여 유저의 아이템 선호도 예상

MBCF의 장점

  • 최적화나 훈련 과정 X
  • 접근 방식이 쉬움

MBCF의 한계점

  • sparse 데이터의 경우 성능 저하
  • 데이터가 많아지면 계산량 증가로 확장성의 한계

실습

캐글 데이터 사용

https://www.kaggle.com/datasets/ruchi798/bookcrossing-dataset

 

Book-Crossing: User review ratings

A collection of book ratings

www.kaggle.com

데이터

import pandas as pd
import numpy as np
from typing import List, Set,Optional

books = pd.read_csv(path+'books.csv') # (232348,6)
# Index(['isbn', 'book_title', 'book_author', 'publisher', 'language', 'category'], dtype='object')
         
users = pd.read_csv(path+'users.csv') # (79516,3)
# Index(['user_id', 'location', 'age'], dtype='object')

ratings = pd.read_csv(path+'ratings.csv') # (56290,3)
# Index(['user_id', 'isbn', 'rating'], dtype='object')

# books, users, ratings 합치기
df = ratings.merge(books, on='isbn')
df = df.merge(users, on='user_id', how='inner') # (56290, 10)

User Based Collaborative Filtering (UBCF)

평점을 기반으로 User-Item matrix를 만들고 코사인 유사도를 이용해 추천 리스트를 만들기

def UBCF(df: pd.DataFrame, user_id: int, topn: Optional[int]=None) -> pd.DataFrame:

    topn=11 if topn is None else topn+1
    # 존재하는 유저인가?
    if user_id in df['user_id'].values:
        user_id = str(user_id)
        # 평점을 기반으로 User-Item matrix를 만들기
        user_item_matrix = df.pivot_table(index=['user_id'], columns=['book_title'], values='rating')
        #유사도 계산을 위해 NaN값을 0으로 채워줍니다.
        user_item_matrix = user_item_matrix.fillna(0)
        #코사인 유사도 계산 (행을 기준으로 유사도)
        user_similarity_df = pd.DataFrame(cosine_similarity(user_item_matrix), index=user_item_matrix.index.astype(str), columns=user_item_matrix.index.astype(str))
        # user_id와 유사한 사람 
        sim_user_df = user_similarity_df[user_id].sort_values(ascending=False).reset_index(drop=False).rename(columns={'index':'user_id', user_id:'similarity'})
        print('입력한 사용자 id: ',user_id)
        display(sim_user_df[1:topn]) #비슷한 사용자를 보여줍니다
    else:
        print('사용자 id를 다시 확인해주세요')
        
# 254번과 유사한 상위 5명 유저
UBCF(df, 254, 5)

RMSE 계산

def UBCF_predict_rating(train_df: pd.DataFrame,
                        test_df: pd.DataFrame) -> list:
    
    # 평점 기준으로 user-item matrix 만들고 유사도 계산
    user_item_matrix = train_df.pivot_table(index=['user_id'], columns=['book_title'], values='rating')
    user_item_matrix = user_item_matrix.fillna(0)
    user_similarity_df = pd.DataFrame(cosine_similarity(user_item_matrix), index=user_item_matrix.index.astype(str), columns=user_item_matrix.index.astype(str))
    user_similarity_df.index = user_similarity_df.index.astype(int)
    
    # 예측 결과
    rating_list=[]
    # 테스트
    for test_id, test_book_title in zip(test_df['user_id'].astype(str), test_df['book_title']):
        pred_rating = (user_similarity_df[test_id].sort_index().values * user_item_matrix[test_book_title].sort_index().values).sum()/(user_similarity_df[test_id].values.sum())
        rating_list.append(pred_rating)
    return rating_list
from sklearn.model_selection import train_test_split
# 훈련, 테스트 데이터 분리
train_df, test_df = train_test_split(df, test_size=0.1, random_state=42)
rating_list = UBCF_predict_rating(train_df, test_df)
# RMSE
mean_squared_error(rating_list, test_df['rating'])**0.5 # 3.8697...

Item Based Collaborative Filtering (IBCF)

def IBCF(df: pd.DataFrame, title: str, topn: Optional[int]=None) -> pd.DataFrame:
    topn=11 if topn is None else topn+1
    if title in df['book_title'].values:
        # rating 기준으로 책 제목 - 유저 행령 만들기 - 코사인 유사도가 행을 기준으로 계산하기 때문에
        user_item_matrix = df.pivot_table(index=['book_title'], columns=['user_id'], values='rating')
        user_item_matrix = user_item_matrix.fillna(0)
        item_similarity_df = pd.DataFrame(cosine_similarity(user_item_matrix), index= user_item_matrix.index, columns=user_item_matrix.index)
        sim_item_df = item_similarity_df[title].sort_values(ascending=False).reset_index().rename(columns={'index':'book_title',title:'similarity'})
        print('입력한 책 이름: ',title)
        display(sim_item_df[1:topn])
    else:
        print('책 제목을 다시 확인해주세요')
        
   # 아래 책과 유사한 상위 5개 책
   IBCF(df,'Harry Potter and the Chamber of Secrets (Book 2)', 5)

RMSE

def IBCF_predict_rating(train_df: pd.DataFrame, test_df: pd.DataFrame) -> list:
    
    # 유사도 구하기
    user_item_matrix = train_df.pivot_table(index=['book_title'], columns=['user_id'], values='rating')
    user_item_matrix = user_item_matrix.fillna(0)
    item_similarity_df = pd.DataFrame(cosine_similarity(user_item_matrix), index=user_item_matrix.index, columns=user_item_matrix.index)

    # 테스트 예측
    rating_list=[]
    for test_id, test_book_title in zip(test_df['user_id'], test_df['book_title']):
        try:
            pred_rating = (item_similarity_df[test_book_title].sort_index().values * user_item_matrix[test_id].sort_index().values).sum() / item_similarity_df[test_book_title].values.sum()
        except:
            pred_rating=0
        rating_list.append(pred_rating)
    # 예측 결과
    return rating_list
    
rating_list = IBCF_predict_rating(train_df, test_df)
# RMSE
mean_squared_error(rating_list, test_df['rating'])**0.5 # 3.85
728x90
반응형

댓글