[Recommender System] 추천 시스템의 종류
2025.06.01 - [Data & Research] - [Recommender System] Table of Contents
추천 시스템 정리를 시작해 보려고 합니다. Introduction의 느낌으로 추천 시스템이 어떤 문제를 다루고 있으며, 어떤 카테고리로 분류될 수 있는지 적어보겠습니다.
1. 추천 시스템의 목적
(1) Ranking Problem
목적: 랭킹 문제의 핵심 목표는 사용자에게 가장 관련성이 높거나 선호할 가능성이 높은 아이템 순서대로 목록을 제공하는 것입니다. 단순히 특정 아이템을 추천하는 것을 넘어, 어떤 아이템을 먼저 보여줄지가 중요합니다. 검색 엔진 결과, 뉴스 피드, 상품 목록 등 순서가 중요한 대부분의 추천 시나리오에서 핵심적인 문제입니다. 각 아이템의 절대적인 선호도보다는 다른 아이템과의 상대적인 순위가 중요합니다(Discrete Choice Model 같은 계량 마케팅 모형에서도 utility는 절대적인 값보다는 그 차이가 중요한 것처럼요).
평가지표: Precision@K, Recall@K, NDCG (Normalized Discounted Cumulative Gain), MAP (Mean Average Precision), MRR (Mean Reciprocal Rank) 등
(2) Prediction Problem
목적: 예측 문제의 목표는 특정 사용자-아이템 쌍에 대한 사용자의 선호도 또는 행동을 예측하는 것입니다. 주로 평점 예측 (Rating Prediction)이나 클릭률 예측 (Click-Through Rate Prediction, CTR) 등의 형태로 나타납니다.
평가지표: RMSE (Root Mean Squared Error), MAE (Mean Absolute Error), AUC (Area Under the ROC Curve), Log Loss등
2. 추천 시스템의 종류
일단 대표적인 두 개의 카테고리부터 살펴보시죠.
(1) 컨텐츠 기반 필터링 (Content-Based Filtering)
: 아이템 자체의 속성(features)을 분석하여 (추천대상 사용자가) 과거에 좋아했던 아이템과 유사한 아이템을 추천합니다.
컨텐츠 기반 필터링의 장점
콜드 스타트(Cold Start) 문제 완화
새로운 아이템이라도 콘텐츠 정보만 있다면 추천 가능
사용자 독립성: 다른 사용자의 데이터에 의존하지 않으므로, 사용자 수가 적거나 새로운 사용자에게도 추천 가능
투명성: 추천 이유를 아이템의 속성을 통해 설명
컨텐츠 기반 필터링의 단점
콘텐츠 정보 부족: 아이템의 콘텐츠 정보가 부족하거나 품질이 낮으면 추천 성능 저하
새로운 취향 발견의 어려움: 사용자가 과거에 선호했던 아이템과 유사한 아이템만 추천하므로, 새로운 취향을 발견하거나 다양한 아이템을 경험하기 어려움 (Filter Bubble 문제).
아이템 간의 연관성 무시: 서로 다른 콘텐츠를 가진 아이템이라도 실제로는 함께 소비되는 경우가 많지만, 콘텐츠 기반 필터링은 이러한 연관성을 고려하지 못함
컨텐츠 기반 필터링의 절차
1) 아이템 표현 (Item Representation):
각 아이템의 고유한 속성(features)을 추출합니다. 이 속성은 아이템의 내용을 설명하는 텍스트 데이터(예: 영화 줄거리, 상품 설명), 카테고리 정보, 키워드, 장르, 제작자 등 다양한 형태를 가질 수 있습니다. 추출된 속성들을 컴퓨터가 이해할 수 있는 형태로 변환합니다. 주로 텍스트 데이터는 TF-IDF (Term Frequency-Inverse Document Frequency) 벡터화나 Word Embedding (Word2Vec, GloVe, FastText 등) 방식을 사용하여 수치형 벡터로 표현됩니다. 범주형 데이터는 원-핫 인코딩(One-Hot Encoding) 등을 통해 벡터화할 수 있습니다.
2) 사용자 프로필 생성 (User Profile Generation):
사용자가 과거에 긍정적인 상호작용(예: 시청, 구매, 좋아요)을 했던 아이템들의 표현 벡터를 분석합니다. 이러한 아이템들의 벡터를 평균내거나, 가중 평균하는 등의 방식으로 사용자 선호도 프로필 벡터를 생성합니다. 예를 들어, 사용자가 특정 장르의 영화를 많이 봤다면, 사용자 프로필 벡터는 해당 장르와 관련된 차원을 높은 값으로 가질 수 있습니다.
3) 아이템-사용자 유사도 계산 (Item-User Similarity Calculation):
아직 사용자가 경험하지 않은 각 아이템의 표현 벡터와 사용자 프로필 벡터 간의 유사도를 계산합니다. 유사도 측정에는 코사인 유사도 (Cosine Similarity)가 가장 일반적으로 사용됩니다. 두 벡터 간의 각도의 코사인 값을 계산하여 유사성을 측정하며, 값이 1에 가까울수록 유사도가 높습니다. 그 외에도 유클리드 거리(Euclidean Distance), 피어슨 상관계수(Pearson Correlation) 등을 사용할 수 있습니다.
4) 아이템 랭킹 및 추천 (Item Ranking and Recommendation):
계산된 아이템-사용자 유사도 점수를 기준으로 아이템들을 내림차순으로 정렬합니다. 상위 N개의 아이템을 해당 사용자에게 추천합니다. N은 추천 시스템의 목적이나 화면 구성 등에 따라 결정될 수 있습니다.
컨텐츠 기반 필터링의 프로그래밍 예시
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# 간단한 영화 데이터 (제목, 줄거리)
movies_data = {
'title': ['어벤져스', '아이언맨', '스파이더맨', '겨울왕국', '라푼젤'],
'genre': ['액션 SF', '액션 SF', '액션 SF', '애니메이션 판타지', '애니메이션 판타지'],
'plot': [
'외계 침략에 맞서 싸우는 슈퍼히어로들의 이야기',
'천재적인 재능을 가진 억만장자가 아이언맨으로 활약하는 이야기',
'평범한 고등학생이 슈퍼히어로 스파이더맨이 되는 이야기',
'얼음 마법을 가진 공주와 그녀의 동생의 이야기',
'긴 머리카락을 가진 소녀가 탑에서 벗어나 세상을 탐험하는 이야기'
]
}
movies_df = pd.DataFrame(movies_data)
# 1. 아이템 표현: 줄거리에 대한 TF-IDF 벡터화
tfidf_vectorizer = TfidfVectorizer(stop_words='english')
tfidf_matrix = tfidf_vectorizer.fit_transform(movies_df['plot'])
# 2. 사용자 프로필 생성 (가상의 사용자)
# 사용자가 '어벤져스'와 '아이언맨'을 좋아했다고 가정
liked_movies = movies_df[movies_df['title'].isin(['어벤져스', '아이언맨'])]
liked_indices = liked_movies.index
user_profile = tfidf_matrix[liked_indices].mean(axis=0)
# 3. 아이템-사용자 유사도 계산
cosine_similarities = cosine_similarity(user_profile, tfidf_matrix)
similarity_scores = cosine_similarities[0]
# 4. 아이템 랭킹 및 추천
# 사용자가 이미 본 영화는 제외하고 추천
user_liked_titles = liked_movies['title'].tolist()
recommendations = []
for i, score in enumerate(similarity_scores):
if movies_df['title'][i] not in user_liked_titles:
recommendations.append((movies_df['title'][i], score))
# 유사도 점수를 기준으로 내림차순 정렬
recommendations.sort(key=lambda x: x[1], reverse=True)
print("사용자가 좋아한 영화:", user_liked_titles)
print("\n추천 영화:")
for title, score in recommendations:
print(f"- {title}: {score:.4f}")
TF-IDF 행렬의 형태: (5, 28)
첫 번째 영화 ('어벤져스')의 TF-IDF 벡터 (일부):
[[0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.36464287 0.
0. 0. 0. 0. 0. 0.52196656
0. 0. 0. 0. ]]
고유 단어 목록 (일부):
['alien' 'avengers' 'away' 'becoming' 'beyond' 'brother' 'discover'
'elves' 'face' 'family' 'father' 'find' 'frozen' 'girl' 'having'
'high' 'homecoming' 'hulk' 'ice' 'intelligent']
사용자 프로필 벡터 (일부):
[[0. 0.30873796 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.30873796 0.
0. 0. 0. 0. 0. 0.44185934
0. 0. 0. 0. ]]
사용자 프로필 벡터의 형태: (1, 28)
사용자가 좋아한 영화: ['어벤져스', '아이언맨']
추천 영화:
- 스파이더맨: 0.6877
- 겨울왕국: 0.0000
- 라푼젤: 0.0000
(2) 협업 필터링 (Collaborative Filtering)
: 유사한 취향을 가진 다른 사용자의 행동 패턴을 기반으로 현재 사용자에게 아이템을 추천합니다. "취향이 비슷한 사람들은 비슷한 것을 좋아할 것이다"라는 가정에 기반합니다.
협업 필터링은 세부 갈래로 나누어 볼 수 있습니다.
A. 사용자 기반 협업 필터링 (User-Based CF): 특정 사용자와 유사한 취향의 사용자를 찾아 그들이 선호하는 아이템을 추천
B. 아이템 기반 협업 필터링 (Item-Based CF): 사용자들의 선호도를 바탕으로 아이템 간 유사도를 계산하여 사용자에게 추천하는 방식.
C. 모델 기반 협업 필터링 (Model-Based CF): 사용자-아이템 상호작용 데이터를 기반으로 잠재 요인(latent factors)을 학습하는 모델 (예: Matrix Factorization, Neural Collaborative Filtering)을 구축하여 추천을 생성합니다.
협업 필터링의 장점
새로운 취향 발견 가능성: 사용자의 과거 선호도에 직접적으로 의존하지 않고, 다른 사용자의 행동을 기반으로 추천하므로 예상치 못한 좋은 아이템을 발견할 수 있습니다.
다양한 형태의 아이템 추천 가능: 콘텐츠 정보가 없더라도 사용자들의 상호작용 데이터만 있다면 어떤 종류의 아이템이든 추천할 수 있습니다.
협업 필터링의 단점
콜드 스타트 문제: 새로운 사용자나 새로운 아이템에 대한 정보가 부족하면 작동에 장애
희소성(Sparsity) 문제: 사용자-아이템 상호작용 데이터가 희소하면 (대부분의 사용자가 극히 일부의 아이템에만 반응하는 경우) 모델의 성능 저하
확장성(Scalability) 문제: 사용자나 아이템의 수가 매우 많아지면 대규모 컴퓨팅 리소스 필요
인기 편향(Popularity Bias): 인기 있는 아이템 위주로 추천되는 경향이 있어, 덜 인기 있지만 사용자의 특정 취향에 맞을 수 있는 아이템을 놓칠 위험 존재
협업 필터링의 절차
사용자 기반 협업 필터링 (User-Based Collaborative Filtering)
1) 사용자-아이템 상호작용 데이터 수집: 사용자들이 아이템에 대해 수행한 행동 (평점, 구매, 클릭 등) 데이터를 수집하여 사용자-아이템 행렬 형태로 표현. 예) 행은 사용자, 열은 아이템, 각 셀의 값은 사용자의 아이템에 대한 상호작용 정도를 나타내는 행렬
2) 사용자 간 유사도 계산: 사용자-아이템 행렬을 기반으로 각 사용자 간의 유사도를 측정합니다. 주로 코사인 유사도 (Cosine Similarity), 피어슨 상관계수 (Pearson Correlation) 등이 사용됩니다. 유사도가 높을수록 취향이 비슷한 사용자로 판단합니다.
3) 이웃 사용자 선택: 특정 사용자와 유사도가 높은 상위 N명의 이웃 사용자를 선택합니다.
4) 아이템 예측: 선택된 이웃 사용자들이 선호했던 아이템 중에서 현재 사용자가 아직 경험하지 못한 아이템에 대한 선호도를 예측합니다. 예측 값은 이웃 사용자들의 해당 아이템에 대한 평가를 유사도에 따라 가중 평균하여 계산할 수 있습니다.
5) 아이템 랭킹 및 추천: 예측된 선호도 점수가 높은 아이템들을 현재 사용자에게 추천합니다.
아이템 기반 협업 필터링 (Item-Based Collaborative Filtering)
1) 사용자-아이템 상호작용 데이터 수집: 사용자-아이템 상호작용 데이터를 사용자-아이템 행렬 형태로 수집합니다 (사용자 기반과 동일).
2) 아이템 간 유사도 계산: 사용자-아이템 행렬을 기반으로 각 아이템 간의 유사도를 측정합니다. 특정 아이템을 선호한 사용자들의 패턴이 얼마나 유사한지를 비교합니다.
3) 사용자가 선호한 아이템 선택: 현재 사용자가 과거에 긍정적인 상호작용을 했던 아이템들을 선택합니다.
4) 추천 아이템 예측: 선택된 선호 아이템들과 유사도가 높은 아이템들을 찾습니다. 각 유사도에 사용자의 선호도를 가중하여 최종 추천 아이템을 결정할 수 있습니다.
5) 아이템 랭킹 및 추천: 예측된 관련성이 높은 아이템들을 현재 사용자에게 추천합니다.
한 번 아래 예시를 통해 비교해 볼까요?
관람객 | 액션 어드벤처 A | SF 스릴러 B | 드라마 C | 코미디 D | 판타지 E | 로맨스 F |
김철수 | 3 | 3 | 4 | 3.5 | 1 | 2 |
박영희 | 3.5 | 2 | - | 5 | 5 | - |
최민지 | 4.5 | - | 2 | ? | 2.5 | 3 |
이승현 | 5 | 1 | 2.5 | 4 | 1.5 | - |
정미나 | - | 2 | 5 | 1 | 3 | - |
User-Based Collaborative Filtering
사용자 유사도 (최민지 님 기준)를 다음과 같이 가정해보겠습니다:
김철수 님 유사도 = 0.3
박영희 님 유사도 = 0.6
이승현 님 유사도 = 0.45
정미나 님 유사도 = 0.15
최민지 님의 '코미디 D' 평점을 예측하기 위해, 가장 유사한 관람객 박영희 님의 '코미디 D' 평점 (5점)을 활용하여 예측합니다.
Item-Based Collaborative Filtering
영화의 유사도를 다음과 같이 가정해보겠습니다:
'코미디 D'와 '액션 어드벤처 A' 유사도 = 0.7
'코미디 D'와 '판타지 E' 유사도 = 0.85
(영화 간 유사도를 계산하는 과정에서 아이템 간의 유사도만 보는 것 같지만, 다른 사용자들의 평가 정보가 반드시 활용됩니다. 예를 들어 김철수 님과 이승현 님만 두 영화를 모두 평가했다고 가정하고, 평가 벡터를 각각 (3,3.5)와 (5,4)라고 한다면 이 벡터들 간의 cosine similarity등을 계산하여 유사도를 얻는 것이지요)
예측: 최민지 님의 '코미디 D' 평점을 예측하기 위해, 최민지 님이 이미 평가한 영화 ('액션 어드벤처 A', '판타지 E')와 '코미디 D'의 유사도를 활용합니다.
예측 평점 = (유사도('코미디 D', '액션 어드벤처 A') * 최민지 님의 '액션 어드벤처 A' 평점) + (유사도('코미디 D', '판타지 E') * 최민지 님의 '판타지 E' 평점)
----------------------------------------------------------------------------------------------------------------------------------
유사도('코미디 D', '액션 어드벤처 A') + 유사도('코미디 D', '판타지 E')
= (0.7 * 4.5) + (0.85 * 2.5)
----------------------
0.7 + 0.85
= 3.15 + 2.125
-------
1.55
= 3.403 ≈ 3.40
컨텐츠 기반 필터링의 프로그래밍 예시
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
# 영화 데이터 (제목)
movies_df = pd.DataFrame({
'title': ['어벤져스', '아이언맨', '스파이더맨', '겨울왕국', '라푼젤']
})
# 사용자-평점 데이터 (가상의 데이터)
ratings_data = {
'user_id': [1, 1, 2, 2, 3, 3, 4, 4, 1, 2, 3, 4],
'title': ['어벤져스', '아이언맨', '어벤져스', '스파이더맨', '아이언맨', '겨울왕국', '스파이더맨', '라푼젤', '스파이더맨', '라푼젤', '어벤져스', '아이언맨'],
'rating': [5, 4, 4, 5, 3, 5, 4, 3, 3, 2, 4, 5]
}
ratings_df = pd.DataFrame(ratings_data)
# 사용자-아이템 행렬 생성
user_item_matrix = ratings_df.pivot_table(index='user_id', columns='title', values='rating').fillna(0)
# -------------------- 아이템 기반 협업 필터링 --------------------
# 아이템 간 코사인 유사도 계산
item_similarity = cosine_similarity(user_item_matrix.T)
item_similarity_df = pd.DataFrame(item_similarity, index=user_item_matrix.columns, columns=user_item_matrix.columns)
def get_item_based_recommendations(user_id, user_item_matrix, item_similarity_df, top_n=3):
user_ratings = user_item_matrix.loc[user_id]
similar_scores = pd.DataFrame(index=user_item_matrix.columns)
similar_scores['score'] = 0
for item, rating in user_ratings.items():
if rating > 0:
item_sim_series = item_similarity_df[item] * rating
similar_scores['score'] = similar_scores['score'].add(item_sim_series, fill_value=0)
similar_scores = similar_scores.sort_values(by='score', ascending=False)
user_rated_items = user_ratings[user_ratings > 0].index.tolist()
recommendations = similar_scores[~similar_scores.index.isin(user_rated_items)].head(top_n)
return recommendations
print("\n----- 아이템 기반 협업 필터링 -----")
user_id_to_recommend = 1
item_based_recommendations = get_item_based_recommendations(user_id_to_recommend, user_item_matrix, item_similarity_df)
print(f"사용자 {user_id_to_recommend}에게 추천:")
print(item_based_recommendations)
# -------------------- 사용자 기반 협업 필터링 --------------------
# 사용자 간 코사인 유사도 계산
user_similarity = cosine_similarity(user_item_matrix)
user_similarity_df = pd.DataFrame(user_similarity, index=user_item_matrix.index, columns=user_item_matrix.index)
def get_user_based_recommendations(user_id, user_item_matrix, user_similarity_df, top_n=3):
similar_users = user_similarity_df[user_id].sort_values(ascending=False)[1:] # 자기 자신 제외
user_ratings = user_item_matrix.loc[user_id]
recommendations = pd.DataFrame(columns=['score'])
for i, similarity in similar_users.head(3).items(): # 상위 3명의 유사 사용자만 고려
neighbor_ratings = user_item_matrix.loc[i]
weighted_ratings = neighbor_ratings * similarity
recommendations = recommendations.add(weighted_ratings, fill_value=0)
recommendations = recommendations.div(user_similarity_df[user_id].abs().sum()) # 유사도 합으로 정규화
recommendations = recommendations.sort_values(ascending=False).fillna(0)
user_rated_items = user_ratings[user_ratings > 0].index.tolist()
recommendations = recommendations[~recommendations.index.isin(user_rated_items)].head(top_n)
return recommendations
print("\n----- 사용자 기반 협업 필터링 -----")
user_id_to_recommend = 1
user_based_recommendations = get_user_based_recommendations(user_id_to_recommend, user_item_matrix, user_similarity_df)
print(f"사용자 {user_id_to_recommend}에게 추천:")
print(user_based_recommendations)
----- 아이템 기반 협업 필터링 -----
사용자 1에게 추천:
title
스파이더맨 9.0
겨울왕국 0.0
라푼젤 0.0
Name: score, dtype: float64
----- 사용자 기반 협업 필터링 -----
사용자 1에게 추천:
스파이더맨 3.666667
라푼젤 3.333333
겨울왕국 1.666667
Name: score, dtype: float64
마지막으로 약간 헷갈리는 부분에 대한 비교도 해보겠습니다. Content based filtering과 Item-based collaborative filtering은 어떻게 다를까요?
- 아이템 기반 협업 필터링: 아이템 간의 유사도를 계산합니다. 특정 아이템을 선호한 사용자들의 평가 패턴이 얼마나 유사한지를 측정합니다. 예를 들어, SF 영화를 좋아하는 사용자들이 액션 영화도 비슷하게 좋아했다면, 두 장르의 영화는 유사하다고 판단할 수 있습니다.
- 콘텐츠 기반 필터링: 아이템과 사용자 프로필 간의 유사도를 계산합니다. 먼저 사용자가 과거에 좋아했던 아이템들의 콘텐츠 정보를 분석하여 사용자 프로필을 생성합니다. 그리고 아직 사용자가 경험하지 못한 아이템의 콘텐츠와 사용자 프로필을 비교하여 유사한 아이템을 추천합니다.
예시로,
Item-based filtering : Ceiling fan을 사면 light을 추천 (ceiling fan을 샀던 많은 사람들이 light도 구매하니까)
Content based filtering: Camera를 사면 유사한 다른 종류의 카메라를 추천