CF는 유사 집단을 파악하는 것이 핵심이므로 사용자 또는 아이템간 유사도를 구하기 위해 유사도 지표를 활용한다.
🔑 유사도를 계산하는 기준에 따라 분류
사용자 기반 CF
: 취향이 비슷한 집단을 알아내고, 집단에 속한 사용자들이 공통적으로 좋게 평가한 아이템을 추천
아이템 기반 CF
: 사용자들의 평가 패턴을 바탕으로 아이템 간의 유사도를 계산해서 사용자의 특정 아이템에 대한 예측 평점을 계산
즉, 간단히 말해서 사용자 기반 CF는 사용자간 유사도를 계산하는 것이고, 아이템 기반 CF는 아이템간 유사도를 계산하는 것이 차이점이라고 이해할 수 있다.
사용자 기반 CF 알고리즘을 통한 추천 프로세스는 다음과 같다.
💡 사용자 기반 CF - MovieLens 실습에서는 사용자간 유사도를 계산하고, 사용자 기반 CF 알고리즘을 통해 추천 대상이 평가한 영화에 대해 예상 평가값을 계산해 실제값과 비교하여 정확도를 계산한다.
# u.user 파일 DataFrame 형태로 읽기
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 파일 DataFrame 형태로 읽기
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 파일 DataFrame형태로 읽기
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')
### 성능 평가
# RMSE 함수
def RMSE(y_true, y_pred):
return np.sqrt(np.mean((np.array(y_true)-np.array(y_pred))**2))
# score(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)
### 데이터셋 만들기
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)
ratings_matrix = x_train.pivot(index='user_id', columns = 'movie_id',values = 'rating')
위 내용은 movielens 데이터를 읽고 필요한 함수를 정의하는 과정으로, 앞선 과정과 중복되는 내용이다.
### 코사인 유사도 계산
from sklearn.metrics.pairwise import cosine_similarity
matrix_dummy = ratings_matrix.copy().fillna(0)
user_similarity = cosine_similarity(matrix_dummy, matrix_dummy)
user_similarity = pd.DataFrame(user_similarity,
index = ratings_matrix.index,
columns = ratings_matrix.index)
user_similarity
### 영화의 가중평균 rating을 계산하는 함수
def CF_simple(user_id, movie_id):
if movie_id in ratings_matrix.columns:
sim_scores = user_similarity[user_id].copy()
movie_ratings = ratings_matrix[movie_id].copy()
none_rating_idx = movie_ratings[movie_ratings.isnull()].index
movie_ratings = movie_ratings.dropna()
sim_scores = sim_scores.drop(none_rating_idx)
mean_rating = np.dot(sim_scores, movie_ratings) / sim_scores.sum()
else:
mean_rating = 3.0
return mean_rating
# 정확도 계산
score(CF_simple)
RMSE는 약 1.025로, 성별에 따른 추천 방식보다 개선되었음을 알 수 있다.
아이템 기반 CF 알고리즘을 통한 추천 프로세스는 다음과 같다.
아이템 기반 CF 알고리즘의 예시
💡 아이템 기반 CF - MovieLens 실습에서는 영화간 유사도를 계산하고 특정 영화와 유사도가 높은 영화를 추천한다.
# 아이템 기준이므로 전치시켜야 함.
rating_matrix_t = np.transpose(rating_matrix)
# 결측치는 0으로
matrix_dummy = rating_matrix_t.copy().fillna(0)
# 각 상품간의 코사인 유사도
item_similarity = cosine_similarity(matrix_dummy, matrix_dummy)
# 데이터프레임으로 변환
item_similarity = pd.DataFrame(item_similarity, index=rating_matrix_t.index,
columns=rating_matrix_t.index)
# 각 상품 (영화) 간의 유사도 출력
item_similarity
위 사진에서는 영화의 id가 아닌 title을 기준으로 영화간의 유사도를 계산한 결과이다. (위 코드는 id를 기준으로 계산)
# 유사도가 높은 영화 10개 추천 함수
def get_recom(title):
return item_similarity[title].sort_values(ascending=False)[:11]
get_recom('Godfather, The (1972)')