Ssul's Blog

[추천시스템#3]CF-UBCF(유저기반 협업필터링) 본문

AI & ML

[추천시스템#3]CF-UBCF(유저기반 협업필터링)

Ssul 2023. 12. 21. 23:13

1. 활용 데이터 이해하기

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

[ratings]

 

 

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

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

 

[user 데이터]

 

 

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

 

[item(movie)데이터]

 

 

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

 

 

2. CF(협업필터링)중 UBCF(유저 기반 추천)

import pandas as pd
import numpy as np

# Read rating data
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 데이터를 test, train으로 나누고 train을 full matrix로 변환
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)

- rating 정보 가져오기

- train 75%, test 25%로 데이터 분류

 

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

- IBCF와 달리, user_id가 인덱스에 위치

user1은 영화 1-6을 보고, 3-5점의 점수를 rating

 

 

# 모든 가능한 사용자 pair의 Cosine similarities 계산
from sklearn.metrics.pairwise import cosine_similarity
matrix_dummy = rating_matrix.copy().fillna(0)
user_similarity = cosine_similarity(matrix_dummy, matrix_dummy)
user_similarity = pd.DataFrame(user_similarity, index=rating_matrix.index, columns=rating_matrix.index)

- matrix_dummy = rating_matrix.copy().fillna(0) -> nan값을 0으로 채우기

- user_similarity = cosine_similarity(matrix_dummy, matrix_dummy)
user_similarity = pd.DataFrame(user_similarity, index=rating_matrix.index, columns=rating_matrix.index)

-> user간 유사도 측정, 원래는 0부터 시작하던 index를 1부터 시작하게 만들기

user_similarity: user1과 user1은 같으니, 유사도 1. user1,2는 유사도 0.1584

 

# 모든 user의 user별 rating 평균 계산
rating_mean = rating_matrix.mean(axis=1)

- 각 유저의 rating평균

user1은 3.65, 2는 3.70

 

def ubcf_bias(user_id, movie_id):
    import numpy as np
    # 현 user의 평균 rating 가져오기
    user_mean = rating_mean[user_id]
    if movie_id in rating_matrix:
        # 현 user와 다른 사용자의 유사도 가져오기 : 나와 0, 1, 2, 3...user들과 상관관계
        sim_scores = user_similarity[user_id]
        # 현 movie의 모든 rating 가져오기
        movie_ratings = rating_matrix[movie_id]
        # 모든 사용자의 rating 평균 가져오기
        others_mean = rating_mean.copy()
        # 현 movie에 대한 rating이 없는 user 삭제
        none_rating_idx = movie_ratings[movie_ratings.isnull()].index
        movie_ratings = movie_ratings.drop(none_rating_idx)
        sim_scores = sim_scores.drop(none_rating_idx)
        # 현재 movie에 대한 평가가 없는 user 평균 삭제
        others_mean = others_mean.drop(none_rating_idx)
        # 편차 예측치 계산
        # 현재 movie의 user 평점 - 각 user의 평균
        movie_ratings = movie_ratings - others_mean
        # 현재 유저와 다른유저의 유사도 * 해당영화의 평점(각 user 평균을 뺀)
        prediction = np.dot(sim_scores, movie_ratings) / sim_scores.sum()
        # 예측값에 현 사용자의 평균 더하기
        prediction = prediction + user_mean
    else:
        prediction = user_mean
    return prediction

- user_mean = rating_mean[user_id] > 입력된 현재유저의 평점 평균(보통 몇점을 평점으로 하는지)

- sim_scores = user_similarity[user_id] > 입력된 유저와 다른 모든 사용자간 유사도

- movie_ratings = rating_matrix[movie_id] > 입력된 영화의 모든 평점 리스트

 

- none_rating_idx = movie_ratings[movie_ratings.isnull()].index > 입력된 영화를 평가하지 않은 user 인덱스 
- movie_ratings = movie_ratings.drop(none_rating_idx) > 입력된 영화를 평가한 유저의 평점list
- sim_scores = sim_scores.drop(none_rating_idx) > 입력된 영화를 평가하지 않은 유저는 삭제(해당 영화를 평가한 유저의 유사도만 사용)

 

- movie_ratings = movie_ratings - others_mean > 입력된 영화의 모든 평점리스트 - 입력된 영화를 평가한 각 유저들의 평균값 = 각 유저가 해당 영화를 어떻게 평가하고 있는지 알수 있음 +이면 자신의 평균보다 높게, -이면 자신의 평균보다 낮게

 

(중요)

- prediction = np.dot(sim_scores, movie_ratings) / sim_scores.sum()

(예)

모든유저들의 유사도합을 분모,

입력된 영화를 평가한 user들과의 유사도 * 각 유저가 해당 영화에 대한 평가

입력된 영화에 대해서 user2는 -0.1, user3 +0.5, user4는 +1.5 일때,

입력된 유저와 유사도가 user2는 0.9, user3은 0.01, user4는 0.01이면,

영화에 대한 긍정적인 평가들이 많지만, 나는 user2와 유사도가 높으므로... -0.1이 높은 영향을 주게됨.

이 값을 기반으로 

- prediction = prediction + user_mean > 입력된 유저의 평점 평균에, 예측 평점을 더해주면, 최종 예측값이 나옴

 

3. UBCF정리 및 결론

- 유저간 유사도를 구하고,

- 입력된 영화를 평가한 유저들을 기준으로, 자신의 평균대비 +평가인지 -평가인지 구함

- 최종적으로 입력된 유저와 유저간 유사도(해당영화를 평가한 유저만) x 해당 영화에 대한 각 유저의 평가를 가중치 곱해서,

- 입력된 유저의 평점평균에 더해주면 최종 예측값 도출

- 나와 유사한 유저가 평가한 영화의 평점을 높게 반영, 나와 유사하지 않은 영화의 평점은 적게 반영

 

4. 추천하기

def recommender(user, n_items=10):
    # 현재 사용자의 모든 아이템에 대한 예상 평점 계산
    predictions = []
    rated_index = rating_matrix.loc[user][rating_matrix.loc[user] > 0].index    # 이미 평가한 영화 확인
    items = rating_matrix.loc[user].drop(rated_index)
    for item in items.index:
        predictions.append(ubcf_bias(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

- 앞에 글과 내용이 동일해서 생략

 

 

 

다음 글은 CB(컨텐츠 기반 추천)