추천 시스템
Recommendation System
사용자 선호도 기반 아이템 추천. 협업 필터링, 콘텐츠 기반.
Recommendation System
사용자 선호도 기반 아이템 추천. 협업 필터링, 콘텐츠 기반.
추천 시스템(Recommendation System, RecSys)은 사용자의 과거 행동, 선호도, 또는 아이템의 특성을 분석하여 개인에게 적합한 콘텐츠, 상품, 서비스를 제안하는 AI 시스템입니다. Netflix의 영화 추천, YouTube의 영상 추천, Amazon의 상품 추천이 대표적인 예입니다.
추천 시스템의 역사는 1990년대 중반 GroupLens 프로젝트에서 시작되었습니다. 2006년 Netflix Prize 대회가 협업 필터링 연구를 촉발시켰고, 2016년 Google의 Wide & Deep 모델이 딥러닝 기반 추천의 시대를 열었습니다. 2024-2025년에는 LLM을 활용한 대화형 추천, Graph Neural Network 기반 추천, Two-Tower 모델 등이 주류가 되었습니다.
추천 시스템의 핵심 접근법은 세 가지입니다. 협업 필터링(Collaborative Filtering)은 유사한 사용자나 아이템 간의 패턴을 활용합니다. 콘텐츠 기반 필터링(Content-based Filtering)은 아이템의 특성과 사용자 선호도를 매칭합니다. 하이브리드 방식은 두 접근법을 결합하여 각각의 한계를 보완합니다. 최신 시스템은 시퀀스 모델링(Transformer), 그래프 신경망, 강화학습까지 결합합니다.
실무에서 추천 시스템은 비즈니스 성과에 직접적인 영향을 미칩니다. Netflix는 시청의 80%가 추천에서 발생하고, Amazon 매출의 35%가 추천에서 유래한다고 알려져 있습니다. 그러나 Filter Bubble(정보 편향), Cold Start(신규 사용자/아이템 문제), Scalability(대규모 처리) 등의 도전도 존재합니다. 2025년 현재 실시간 개인화, 설명 가능한 추천, 공정성 있는 추천이 주요 연구 주제입니다.
Python Surprise 라이브러리를 활용한 협업 필터링 추천:
# 협업 필터링 추천 시스템 (Surprise 라이브러리)
from surprise import Dataset, Reader, SVD, KNNBasic
from surprise.model_selection import cross_validate, train_test_split
from surprise import accuracy
import pandas as pd
# 1. 샘플 데이터 준비 (user, item, rating)
data = {
'user_id': [1,1,1,2,2,2,3,3,3,4,4,4,5,5,5],
'item_id': ['A','B','C','A','B','D','B','C','D','A','C','E','C','D','E'],
'rating': [5,4,3,4,5,4,3,4,5,4,3,4,5,4,3]
}
df = pd.DataFrame(data)
# Surprise 데이터 형식으로 변환
reader = Reader(rating_scale=(1, 5))
dataset = Dataset.load_from_df(df[['user_id', 'item_id', 'rating']], reader)
# 2. 모델 선택 및 학습
# SVD (Matrix Factorization 기반)
svd_model = SVD(n_factors=50, n_epochs=20, lr_all=0.005, reg_all=0.02)
# 교차 검증
cv_results = cross_validate(svd_model, dataset, measures=['RMSE', 'MAE'], cv=3, verbose=True)
print(f"\n평균 RMSE: {cv_results['test_rmse'].mean():.4f}")
print(f"평균 MAE: {cv_results['test_mae'].mean():.4f}")
# 3. 전체 데이터로 학습 후 예측
trainset = dataset.build_full_trainset()
svd_model.fit(trainset)
# 특정 사용자에게 아이템 추천
def get_top_n_recommendations(model, user_id, all_items, n=5):
"""사용자에게 상위 N개 아이템 추천"""
predictions = []
for item_id in all_items:
pred = model.predict(user_id, item_id)
predictions.append((item_id, pred.est))
# 예측 점수로 정렬
predictions.sort(key=lambda x: x[1], reverse=True)
return predictions[:n]
all_items = df['item_id'].unique()
user_1_recs = get_top_n_recommendations(svd_model, 1, all_items, n=3)
print(f"\n사용자 1 추천: {user_1_recs}")
PyTorch Two-Tower 모델 기반 추천 시스템:
# Two-Tower 추천 모델 (PyTorch)
import torch
import torch.nn as nn
import torch.nn.functional as F
class TwoTowerModel(nn.Module):
"""
Two-Tower Architecture for Recommendation
- User Tower: 사용자 특성 임베딩
- Item Tower: 아이템 특성 임베딩
- 두 타워의 내적으로 유사도 계산
"""
def __init__(self, num_users, num_items, embedding_dim=64, hidden_dim=128):
super().__init__()
# User Tower
self.user_embedding = nn.Embedding(num_users, embedding_dim)
self.user_tower = nn.Sequential(
nn.Linear(embedding_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, embedding_dim),
nn.LayerNorm(embedding_dim)
)
# Item Tower
self.item_embedding = nn.Embedding(num_items, embedding_dim)
self.item_tower = nn.Sequential(
nn.Linear(embedding_dim, hidden_dim),
nn.ReLU(),
nn.Linear(embedding_dim, embedding_dim),
nn.LayerNorm(embedding_dim)
)
def forward(self, user_ids, item_ids):
# User representation
user_emb = self.user_embedding(user_ids)
user_repr = self.user_tower(user_emb)
# Item representation
item_emb = self.item_embedding(item_ids)
item_repr = self.item_tower(item_emb)
# 코사인 유사도 계산
user_repr = F.normalize(user_repr, dim=1)
item_repr = F.normalize(item_repr, dim=1)
similarity = (user_repr * item_repr).sum(dim=1)
return similarity
def get_user_embedding(self, user_ids):
"""실시간 추천을 위한 사용자 임베딩 추출"""
user_emb = self.user_embedding(user_ids)
return F.normalize(self.user_tower(user_emb), dim=1)
def get_item_embedding(self, item_ids):
"""인덱싱을 위한 아이템 임베딩 추출"""
item_emb = self.item_embedding(item_ids)
return F.normalize(self.item_tower(item_emb), dim=1)
# 모델 초기화 및 학습
model = TwoTowerModel(num_users=10000, num_items=50000, embedding_dim=128)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# Contrastive Loss (In-batch Negatives)
def contrastive_loss(user_repr, item_repr, temperature=0.1):
logits = torch.matmul(user_repr, item_repr.T) / temperature
labels = torch.arange(len(user_repr), device=logits.device)
return F.cross_entropy(logits, labels)
print(f"모델 파라미터: {sum(p.numel() for p in model.parameters()):,}")
주요 추천 알고리즘 특성 비교:
| 알고리즘 | 장점 | 단점 | 적합 상황 |
|---|---|---|---|
| 협업 필터링 (User-based) | 구현 간단, 직관적 | 확장성 낮음, Cold Start | 소규모 사용자 |
| 협업 필터링 (Item-based) | 안정적, 설명 가능 | 다양성 부족 | 전자상거래 |
| Matrix Factorization (SVD) | 정확도 높음 | 실시간 업데이트 어려움 | 배치 추천 |
| Two-Tower (Neural) | 실시간, 확장성 | 학습 복잡 | 대규모 서비스 |
| Transformer 기반 | 시퀀스 모델링 강점 | 계산 비용 높음 | 세션 기반 추천 |
RMSE, Precision@K 같은 오프라인 지표가 높아도 실제 비즈니스 KPI(CTR, 매출)가 오르지 않을 수 있습니다. 반드시 A/B 테스트로 온라인 효과를 검증하세요.
학습 데이터에 인기 아이템이 과대 표현되어 있으면 롱테일 아이템이 추천되지 않습니다. 인기도 편향 제거(Debiasing) 기법을 적용하지 않으면 다양성이 저하됩니다.
탐색(Exploration)과 활용(Exploitation)의 균형을 맞추세요. 랜덤 탐색, UCB, Thompson Sampling 등으로 신규 아이템을 테스트하고, 다양성 지표를 모니터링하세요.