Ssul's Blog

[추천시스템#2]CF-IBCF(아이템기반 협업필터링) 본문

AI & ML

[추천시스템#2]CF-IBCF(아이템기반 협업필터링)

Ssul 2023. 12. 21. 01:03

1. 활용 데이터 이해하기

앞으로 추천시스템에 활용할 데이터는 movielens-100k 데이터다. 총 3개의 데이터가 있다.

[ratings]

유저id와 영화id를 기반으로 해당 영화에 유저가 몇점을 주었는지 기록되어 있는 데이터.

평점은 1-5점으로 구성. timestamp는 날짜시간데이터(위 스샷에선 삭제)

 

[user 데이터]

user_id를 기준으로 해당 유저의 나이/성별/직업/우편번호 정보가 있음

 

[item(movie)데이터]

movie_id를 기준으로 해당 영화의 제목, 출시일, 장르 등 영화에 대한 정보가 있음

 

 

2. CF(협업필터링)중 IBCF(아이템 기반 추천)

import numpy as np
import pandas as pd

r_cols = ['user_id', 'movie_id', 'rating', 'timestamp']
ratings = pd.read_csv('./u.data', names=r_cols,  sep='\t',encoding='latin-1')
ratings = ratings.drop('timestamp', axis=1)

- rating정보를 ratings에 가져오기

- 날짜/시간정보는 사용안할것이니, 제외

 

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, random_state=12)

- ratings데이터를 x에 복사 > 75%는 x_train, 25%는 x_test데이터로 분류

- 평점데이터를 y에 복사 > 75%는 y_train, 25%는 y_test데이터로 분류

 

rating_matrix_t = x_train.pivot(values='rating', index='movie_id', columns='user_id')

- 행은 movie_id, 열은 user_id로 설정

- 요소값은 rating점수로 표기

예) user1은 영화1,2,3,4,5를 모두 평가, user5는 영화 1,2를 평가

 

matrix_dummy = rating_matrix_t.copy().fillna(0)

- nan값을 0으로 변경

 

from sklearn.metrics.pairwise import cosine_similarity

item_similarity = cosine_similarity(matrix_dummy, matrix_dummy)
item_similarity = pd.DataFrame(item_similarity, index=rating_matrix_t.index, columns=rating_matrix_t.index)

- index=rating_matrix_t.index, columns=rating_matrix_t.index > 행이 movie_id이니,

- movie_id를 기준으로 코사인 유사도 구하기

- 코사인 유사도가 1이면, 두 영화간 유사도 높음.

- 코사인 유사도가 0이면, 두 영화간 유사도 낮음.

- item_similarity 데이터

가로/세로 모두 movie_id, 무비1과 무비1은 같은 영화니 1, 무비1과 무비6은 0.093으로 별로 안비슷함

 

def ibcf(user_id, movie_id, K=0):
    import numpy as np
    if user_id in rating_matrix_t:          # 사용자가 train set에 있는지 확인
        if movie_id in item_similarity:     # 현재 영화가 train set에 있는지 확인
            # 현재 영화와 다른 영화의 similarity 값 가져오기
            sim_scores = item_similarity[movie_id]
            # 현 사용자의 모든 rating 값 가져오기
            user_rating = rating_matrix_t[user_id]
            # 현 사용자가 평가하지 않은 영화 index 가져오기
            non_rating_idx = user_rating[user_rating.isnull()].index
            # 현 사용자가 평가하지 않은 영화 제거
            user_rating = user_rating.dropna()
            # 현 사용자가 평가하지 않은 영화의 similarity 값 제거
            sim_scores = sim_scores.drop(non_rating_idx)
            # 현 영화에 대한 사용자의 예상 rating 계산, 가중치는 현 영화와 사용자가 평가한 영화의 유사도
            # 현재영화와 userid가 평가한 영화들측의 유사도* user가 평가한 영화rating(가중평균) > 현재영화의 평점 예측
            mean_rating = np.dot(sim_scores, user_rating) / sim_scores.sum()
        else:
            mean_rating = 3.0
    else:
        mean_rating = 3.0
    return mean_rating

- 아이템기반 협업필터링 함수, 특정 user_id, movie_id를 인자로 입력받음

- sim_scores = item_similarity[movie_id] > 현재영화와 다른 모든 영화간의 유사도 가져오기

- user_rating = rating_matrix_t[user_id] > 현재 유저가 모든 영화에 대해서 평가/미평가 데이터 가져오기

- non_rating_idx = user_rating[user_rating.isnull()].index > nan인 영화의 index > 평가하지 않은 영화 index(안본영화)

- user_rating = user_rating.dropna() > 현재 사용자가 보지않은 데이터 날리고, 본 영화 평점만 가져오기

- sim_scores = sim_scores.drop(non_rating_idx) > 현재 영화와 user가 본영화의 유사도 데이터만 남기기

- mean_rating = np.dot(sim_scores, user_rating) / sim_scores.sum() > 현재 영화의 평점 예측(중요 잘 이해해 보자)

(예)

user가 1,2,3번 영화를 각각 5점, 3점, 1점으로 평가.

입력된 영화와 1,2,3번 영화의 코사인 유사도는 0.5, 1.0, 0.1로 구성.

그럼 입력된 영화의 평점은

(0.5/1.6)x5점 > 1번 영화는 전체 1.6 유사도 중 0.5를 차지하고, 유저의 평점 5점 곱해주고,

(1.0/1.6)x3점 > 2번 영화는 전체 1.6 유사도 중 1.0을 차지하고, 유저의 평점은 3점 곱해주고, 

(0.1/1.6)x1점 > 3번 영화는 전체 1.6 유사도 중 0.1을 차지하고, 유저의 평점은 1점 곱해주고,

 

(0.5/1.6)x5점+(1.0/1.6)x3점+(0.1/1.6)x1점 = 입력된 영화의 예측 평점

이렇게 유사도와 user의 평점을 반영해서 영화의 평점을 예측

- else: mean_rating = 3.0 > user나 movie 정보가 없으면 중간값인 3점으로 예측

 

3. IBCF 정리 및 결론

- 유저가 평가한 영화와 입력된 영화간 유사도를 가중치로 사용 > 입력된 영화와 유사도가 낮은 영화의 평점은 적게 반영

- 유저가 높게 평가한 영화는 평점이 높으니, 강하게 반영

- 코드를 보면, 따로 학습이 없이 데이터에서 뽑아오는 개념 > Memory Base Model

 

 

4. 추천하기

def recommender(user, n_items=10):
    # 현재 사용자의 모든 아이템에 대한 예상 평점 계산
    predictions = []
    rated_index = rating_matrix_t[user][rating_matrix_t[user] > 0].index     # 이미 평가한 영화 확인
    items = rating_matrix_t[user].drop(rated_index)
    for item in items.index:
        predictions.append(ibcf(user, item))                                 # 예상평점 계산
    recommendations = pd.Series(data=predictions, index=items.index, dtype=float)
    recommendations = recommendations.sort_values(ascending=False)[:n_items] # 예상평점이 가장 높은 영화 선택
    recommended_items = movies.loc[recommendations.index]['title']
    return recommended_items

- user_id를 입력받는다 > 입력받은 유저에게 영화를 추천하는 함수

- rating_matrix_t는 행은 movie, 열은 user로  > user별로 평가한 영화들을 보여주는 데이터

- rated_index = rating_matrix_t[user][rating_matrix_t[user] > 0].index -> 유저가 평가한 영화의 index

- items = rating_matrix_t[user].drop(rated_index) -> 유저가 평가하지 않은 영화들

- for item in items.index:
        predictions.append(ibcf(user, item))  -> 유저가 평가하지 않은 영화들을 위에서 구성한 ibcf 함수에 넣어서 모두 평점을 예측

- recommendations = recommendations.sort_values(ascending=False)[:n_items] -> 안본영화들의 평점을 구하고, 평점을 기준으로 내림차순 정렬하고, 상위 10개

- recommended_items = movies.loc[recommendations.index]['title'] -> 상위 10개 영화의 title 출력

 

 

 

다음 글은 CF-UBCF(유저기반 협업필터링)