🤖 AI/ML

추천 시스템

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 기반 시퀀스 모델링 강점 계산 비용 높음 세션 기반 추천

🗣️ 실무에서 이렇게 말하세요

💬 회의에서
"현재 인기도 기반 추천에서 개인화 추천으로 전환하면 CTR이 20-30% 상승할 것으로 예상됩니다. Two-Tower 모델로 시작하고, 벡터 검색(FAISS/Milvus)로 실시간 서빙하는 아키텍처를 제안합니다."
💬 면접에서
"Cold Start 문제는 콘텐츠 기반 폴백, 인기도 기반 추천, 탐색(Exploration) 전략으로 해결합니다. 신규 사용자에게는 온보딩 설문이나 인기 아이템을, 신규 아이템에는 콘텐츠 유사도 기반 추천을 제공합니다."
💬 기술 토론에서
"A/B 테스트에서 정확도뿐만 아니라 다양성(Diversity), 신선도(Novelty), 세렌디피티도 측정해야 합니다. 정확도만 최적화하면 Filter Bubble이 심해지고 장기적으로 사용자 이탈이 증가하거든요."

⚠️ 흔한 실수 & 주의사항

오프라인 지표만 신뢰

RMSE, Precision@K 같은 오프라인 지표가 높아도 실제 비즈니스 KPI(CTR, 매출)가 오르지 않을 수 있습니다. 반드시 A/B 테스트로 온라인 효과를 검증하세요.

인기도 편향 무시

학습 데이터에 인기 아이템이 과대 표현되어 있으면 롱테일 아이템이 추천되지 않습니다. 인기도 편향 제거(Debiasing) 기법을 적용하지 않으면 다양성이 저하됩니다.

올바른 접근법

탐색(Exploration)과 활용(Exploitation)의 균형을 맞추세요. 랜덤 탐색, UCB, Thompson Sampling 등으로 신규 아이템을 테스트하고, 다양성 지표를 모니터링하세요.

🔗 관련 용어

📚 더 배우기