본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 4. · 17 Views
추천 엔진 완벽 가이드
넷플릭스와 아마존이 어떻게 당신의 취향을 알아맞히는지 궁금하셨나요? 이 가이드에서는 추천 시스템의 핵심 원리부터 실제 영화 추천 엔진 구현까지, 초급 개발자도 쉽게 따라할 수 있도록 설명합니다.
목차
1. 추천 시스템 개론
김개발 씨는 퇴근 후 넷플릭스를 켰습니다. 화면에는 마치 자신의 마음을 읽은 듯한 영화들이 줄지어 있었습니다.
"어떻게 내가 좋아할 만한 걸 이렇게 정확히 알지?" 문득 개발자로서의 호기심이 발동했습니다.
추천 시스템은 사용자가 좋아할 만한 아이템을 예측하여 제안하는 알고리즘입니다. 마치 오랜 단골 손님의 취향을 꿰뚫고 있는 동네 서점 주인과 같습니다.
사용자의 과거 행동 데이터와 아이템의 특성을 분석하여 개인화된 추천을 제공합니다.
다음 코드를 살펴봅시다.
# 추천 시스템의 기본 구조
class RecommendationEngine:
def __init__(self):
self.user_data = {} # 사용자 행동 데이터 저장
self.item_data = {} # 아이템 정보 저장
# 사용자 행동 기록
def record_interaction(self, user_id, item_id, rating):
if user_id not in self.user_data:
self.user_data[user_id] = {}
self.user_data[user_id][item_id] = rating
# 추천 생성 (기본 골격)
def recommend(self, user_id, n_items=5):
# 실제 추천 로직은 하위 클래스에서 구현
raise NotImplementedError("추천 알고리즘을 구현하세요")
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 요즘 한창 이커머스 플랫폼 프로젝트에 투입되어 열심히 일하고 있습니다.
어느 날 팀장님이 다가와 말했습니다. "김개발 씨, 우리 쇼핑몰에 상품 추천 기능을 넣어볼까 하는데, 한번 조사해볼래요?" 추천 시스템이라니.
김개발 씨는 막막했습니다. 그저 넷플릭스에서 영화 추천받고, 아마존에서 상품 추천받는 것만 경험해봤지, 그걸 직접 만든다는 건 상상도 못 해봤습니다.
그렇다면 추천 시스템이란 정확히 무엇일까요? 쉽게 비유하자면, 추천 시스템은 마치 동네 단골 서점의 주인 아저씨와 같습니다.
여러분이 서점에 들어서면 아저씨는 이렇게 말합니다. "아, 김 선생님!
저번에 사 가신 추리소설 재미있게 읽으셨어요? 같은 작가 신작이 나왔는데 한번 보실래요?" 이처럼 추천 시스템도 여러분의 과거 행동을 기억하고, 그것을 바탕으로 좋아할 만한 것을 제안합니다.
추천 시스템이 없던 시절에는 어땠을까요? 사용자는 원하는 상품을 찾기 위해 수천 개의 목록을 일일이 뒤져야 했습니다.
마치 도서관에서 사서 없이 책을 찾는 것과 같았습니다. 시간도 오래 걸리고, 정작 좋은 상품을 놓치기 일쑤였습니다.
더 큰 문제는 사업자 입장에서도 발생했습니다. 아무리 좋은 상품을 갖춰 놓아도 사용자가 발견하지 못하면 소용이 없었습니다.
바로 이런 문제를 해결하기 위해 추천 시스템이 등장했습니다. 추천 시스템을 사용하면 사용자는 원하는 것을 빠르게 찾을 수 있습니다.
또한 사업자는 더 많은 상품을 노출시킬 수 있게 됩니다. 무엇보다 개인화된 경험을 제공하여 사용자 만족도를 크게 높일 수 있습니다.
아마존의 경우 매출의 35%가 추천 시스템에서 발생한다고 알려져 있습니다. 위의 코드를 살펴보겠습니다.
먼저 RecommendationEngine 클래스가 추천 시스템의 뼈대 역할을 합니다. user_data에는 사용자가 어떤 아이템에 어떤 평점을 줬는지 기록됩니다.
record_interaction 메서드는 사용자의 행동을 저장하는 역할을 합니다. 마지막으로 recommend 메서드는 실제 추천을 생성하는 부분인데, 여기서는 뼈대만 만들어 두었습니다.
실제 현업에서 추천 시스템은 크게 세 가지 방식으로 구현됩니다. 첫째는 내용 기반 필터링으로, 아이템의 특성을 분석하여 비슷한 아이템을 추천합니다.
둘째는 협업 필터링으로, 비슷한 취향의 사용자들이 좋아한 것을 추천합니다. 셋째는 이 둘을 결합한 하이브리드 방식입니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 추천 시스템의 개념을 파악한 김개발 씨는 자신감이 생겼습니다.
"생각보다 원리는 단순하네요. 결국 데이터를 모으고, 분석하고, 예측하는 거군요!"
실전 팁
💡 - 추천 시스템의 핵심은 데이터입니다. 사용자 행동 데이터를 체계적으로 수집하는 것부터 시작하세요.
- 처음부터 완벽한 알고리즘을 만들려 하지 마세요. 간단한 규칙 기반부터 시작해서 점진적으로 개선하는 것이 좋습니다.
2. 내용 기반 추천
김개발 씨는 추천 시스템 공부를 시작했습니다. 첫 번째로 접한 것은 내용 기반 필터링이었습니다.
"영화의 장르, 감독, 배우 같은 특성을 분석해서 추천한다고요?" 마치 책의 목차만 보고 내용을 파악하는 것 같았습니다.
내용 기반 필터링은 아이템의 특성을 분석하여 사용자가 좋아했던 아이템과 유사한 것을 추천하는 방식입니다. 마치 "이 노래가 좋으셨다면, 비슷한 템포와 장르의 이 노래도 좋아하실 거예요"라고 말하는 것과 같습니다.
아이템의 메타데이터를 벡터로 변환하고 유사도를 계산합니다.
다음 코드를 살펴봅시다.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# 영화 데이터 (제목과 특성 정보)
movies = {
'movie_1': '액션 SF 외계인 전쟁 히어로',
'movie_2': '로맨스 코미디 사랑 뉴욕',
'movie_3': '액션 히어로 도시 범죄',
'movie_4': '로맨스 드라마 사랑 이별'
}
# TF-IDF로 텍스트를 벡터로 변환
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(movies.values())
# 코사인 유사도 계산
similarity = cosine_similarity(tfidf_matrix)
# movie_1과 가장 비슷한 영화 찾기
movie_idx = 0
similar_scores = list(enumerate(similarity[movie_idx]))
similar_movies = sorted(similar_scores, key=lambda x: x[1], reverse=True)
print(f"movie_1과 유사한 영화: {similar_movies[1]}") # 자기 자신 제외
박시니어 씨가 김개발 씨에게 물었습니다. "김개발 씨, 만약 사용자가 처음 서비스에 가입했는데 아무 데이터도 없다면 어떻게 추천하겠어요?" 김개발 씨는 잠시 생각에 잠겼습니다.
다른 사용자의 데이터를 참고하려 해도, 새로운 사용자와 기존 사용자 사이의 연결고리가 없습니다. 이럴 때 빛을 발하는 것이 바로 내용 기반 필터링입니다.
내용 기반 필터링은 마치 도서관 사서와 같습니다. 사서는 책의 장르, 저자, 주제를 모두 파악하고 있습니다.
손님이 "저번에 읽은 추리소설이 재미있었어요"라고 말하면, 사서는 그 책과 비슷한 특성을 가진 다른 추리소설을 찾아줍니다. 다른 손님들이 뭘 읽었는지는 중요하지 않습니다.
오직 책 자체의 특성만 분석합니다. 기술적으로 어떻게 구현될까요?
먼저 각 아이템의 특성을 벡터로 표현합니다. 영화라면 장르, 감독, 배우, 키워드 등의 정보를 숫자의 배열로 변환합니다.
이 과정에서 TF-IDF라는 기법이 자주 사용됩니다. TF-IDF는 단어의 중요도를 측정하는 방법으로, 많이 등장하지만 모든 문서에 흔한 단어는 낮은 점수를, 특정 문서에서만 많이 등장하는 단어는 높은 점수를 부여합니다.
벡터가 준비되면 코사인 유사도를 계산합니다. 코사인 유사도는 두 벡터가 얼마나 같은 방향을 가리키는지 측정합니다.
값이 1에 가까우면 매우 유사하고, 0에 가까우면 관련이 없습니다. 위 코드에서 movie_1은 액션과 SF 특성을 가지고 있으므로, 비슷한 특성의 movie_3가 높은 유사도를 보일 것입니다.
내용 기반 필터링의 장점은 명확합니다. 첫째, 콜드 스타트 문제에 강합니다.
새로운 아이템이 추가되어도 그 특성만 분석하면 바로 추천에 포함시킬 수 있습니다. 둘째, 설명 가능성이 높습니다.
"이 영화는 당신이 좋아한 영화와 같은 감독의 작품이에요"라고 추천 이유를 설명할 수 있습니다. 셋째, 다른 사용자의 데이터가 필요 없으므로 프라이버시 문제가 적습니다.
하지만 한계도 있습니다. 내용 기반 필터링은 과도한 전문화 문제를 일으킬 수 있습니다.
사용자가 액션 영화만 봤다면 계속 액션 영화만 추천받게 됩니다. 로맨스 영화를 좋아할 수도 있는데 그 가능성을 발견하지 못합니다.
이를 필터 버블이라고 부릅니다. 또한 특성 추출의 어려움도 있습니다.
영화의 분위기, 연기력, 스토리의 반전 같은 미묘한 특성은 정량화하기 어렵습니다. 결국 표면적인 메타데이터에 의존하게 되어 깊이 있는 추천이 어려울 수 있습니다.
실전 팁
💡 - 아이템의 특성을 풍부하게 수집하세요. 메타데이터가 자세할수록 추천 품질이 올라갑니다.
- TF-IDF 외에도 Word2Vec, BERT 같은 임베딩 기법을 활용하면 더 정교한 유사도 측정이 가능합니다.
3. 협업 필터링 추천
"그런데 선배, 제가 좋아할 영화를 저도 모르는 경우는요?" 김개발 씨의 질문에 박시니어 씨가 빙그레 웃었습니다. "그럴 때는 나와 비슷한 취향을 가진 사람들이 뭘 좋아하는지 보면 되지."
협업 필터링은 비슷한 취향을 가진 사용자들의 행동 데이터를 활용하여 추천하는 방식입니다. "이 상품을 산 다른 고객들이 함께 구매한 상품"이 바로 이 원리입니다.
사용자 간의 유사성 또는 아이템 간의 유사성을 기반으로 동작합니다.
다음 코드를 살펴봅시다.
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# 사용자-영화 평점 행렬 (0은 아직 보지 않음)
ratings = np.array([
[5, 3, 0, 1], # 사용자 A
[4, 0, 0, 1], # 사용자 B
[1, 1, 0, 5], # 사용자 C
[1, 0, 0, 4], # 사용자 D
[0, 1, 5, 4], # 사용자 E (타겟)
])
# 사용자 간 유사도 계산
user_similarity = cosine_similarity(ratings)
# 사용자 E와 가장 비슷한 사용자 찾기
target_user = 4
similarities = user_similarity[target_user]
most_similar = np.argsort(similarities)[::-1][1] # 자기 자신 제외
# 비슷한 사용자가 높게 평가했지만 타겟이 안 본 영화 추천
for movie_idx, rating in enumerate(ratings[most_similar]):
if ratings[target_user][movie_idx] == 0 and rating >= 4:
print(f"영화 {movie_idx} 추천 (유사 사용자 평점: {rating})")
김개발 씨는 고등학교 시절을 떠올렸습니다. 친한 친구 민수가 추천하는 영화는 거의 다 재미있었습니다.
반면 취향이 정반대인 철수가 극찬한 영화는 영 별로였습니다. 협업 필터링은 바로 이 원리를 알고리즘으로 구현한 것입니다.
협업 필터링은 크게 두 가지 방식으로 나뉩니다. 첫째는 사용자 기반 협업 필터링입니다.
나와 비슷한 취향을 가진 사용자를 찾고, 그 사용자가 좋아했지만 내가 아직 접하지 않은 아이템을 추천합니다. 마치 "너랑 취향 비슷한 민수가 이 영화 좋다고 했어"라고 말하는 것과 같습니다.
둘째는 아이템 기반 협업 필터링입니다. 내가 좋아한 아이템과 함께 높은 평가를 받는 다른 아이템을 찾습니다.
"이 영화를 좋아한 사람들은 저 영화도 좋아하더라"는 방식입니다. 아마존의 "이 상품을 구매한 고객이 함께 구매한 상품"이 대표적입니다.
위 코드에서는 사용자 기반 방식을 구현했습니다. 먼저 사용자-아이템 평점 행렬을 만듭니다.
각 행은 사용자, 각 열은 아이템을 나타내고, 값은 평점입니다. 0은 아직 평가하지 않은 것을 의미합니다.
그 다음 사용자 간의 코사인 유사도를 계산합니다. 유사도가 높은 사용자를 찾아, 그 사용자가 높게 평가했지만 타겟 사용자가 아직 보지 않은 아이템을 추천합니다.
협업 필터링의 가장 큰 장점은 예상치 못한 발견입니다. 내용 기반 필터링은 비슷한 특성의 아이템만 추천하지만, 협업 필터링은 전혀 다른 장르의 아이템도 추천할 수 있습니다.
나와 취향이 비슷한 사람이 갑자기 다큐멘터리에 빠졌다면, 나에게도 그 다큐멘터리가 추천될 수 있습니다. 이렇게 새로운 취향을 발견하게 해주는 것이 협업 필터링의 매력입니다.
하지만 치명적인 단점도 있습니다. 바로 희소성 문제입니다.
넷플릭스에 영화가 1만 개 있고 사용자가 100만 명이라면, 대부분의 사용자는 극히 일부의 영화만 봤을 것입니다. 평점 행렬의 대부분이 0으로 채워지는 것입니다.
이런 희소한 데이터에서 의미 있는 유사성을 찾기가 어렵습니다. 또한 확장성 문제도 있습니다.
사용자가 수백만 명이 되면 모든 사용자 쌍의 유사도를 계산하는 것이 매우 비효율적입니다. 이를 해결하기 위해 행렬 분해 기법이나 근사 최근접 이웃 알고리즘 등이 사용됩니다.
실전 팁
💡 - 아이템 기반 협업 필터링이 사용자 기반보다 확장성이 좋습니다. 아이템 수가 사용자 수보다 적은 경우가 많기 때문입니다.
- 희소성 문제를 해결하려면 행렬 분해(Matrix Factorization) 기법을 공부해보세요. SVD나 ALS가 대표적입니다.
4. 혼합 추천 엔진
"내용 기반도 좋고, 협업 필터링도 좋은데... 둘 다 쓰면 안 되나요?" 김개발 씨의 질문에 박시니어 씨가 고개를 끄덕였습니다.
"물론이지. 그게 바로 하이브리드 추천이야."
하이브리드 추천 시스템은 내용 기반 필터링과 협업 필터링의 장점을 결합한 방식입니다. 각 방식의 단점을 서로 보완하여 더 정확하고 다양한 추천을 제공합니다.
넷플릭스, 스포티파이 같은 대형 서비스들이 실제로 사용하는 방식입니다.
다음 코드를 살펴봅시다.
class HybridRecommender:
def __init__(self, content_weight=0.4, collab_weight=0.6):
self.content_weight = content_weight
self.collab_weight = collab_weight
def get_content_scores(self, user_id, items):
# 내용 기반 점수 계산 (사용자가 좋아한 아이템과의 유사도)
scores = {}
for item in items:
scores[item] = self._calculate_content_similarity(user_id, item)
return scores
def get_collab_scores(self, user_id, items):
# 협업 필터링 점수 계산 (유사 사용자들의 평점 기반)
scores = {}
for item in items:
scores[item] = self._predict_rating(user_id, item)
return scores
def recommend(self, user_id, candidate_items, n=5):
content_scores = self.get_content_scores(user_id, candidate_items)
collab_scores = self.get_collab_scores(user_id, candidate_items)
# 가중 평균으로 최종 점수 계산
final_scores = {}
for item in candidate_items:
final_scores[item] = (
self.content_weight * content_scores[item] +
self.collab_weight * collab_scores[item]
)
# 상위 N개 추천
return sorted(final_scores.items(), key=lambda x: x[1], reverse=True)[:n]
김개발 씨는 비유를 떠올렸습니다. 내용 기반 필터링은 마치 책 표지와 목차만 보고 추천하는 것과 같고, 협업 필터링은 다른 독자들의 후기만 보고 추천하는 것과 같습니다.
그런데 정말 좋은 서점 직원이라면 둘 다 참고하지 않을까요? 하이브리드 추천은 바로 이 아이디어를 구현한 것입니다.
하이브리드 추천을 구현하는 방법은 여러 가지가 있습니다. 가장 간단한 것은 가중 하이브리드 방식입니다.
내용 기반 점수와 협업 필터링 점수를 각각 계산한 뒤, 가중 평균을 내어 최종 점수를 산출합니다. 위 코드가 바로 이 방식을 보여줍니다.
또 다른 방법은 스위칭 하이브리드입니다. 상황에 따라 다른 알고리즘을 선택합니다.
예를 들어 신규 사용자에게는 내용 기반 필터링을, 충분한 데이터가 쌓인 사용자에게는 협업 필터링을 적용합니다. 캐스케이드 하이브리드도 있습니다.
먼저 한 알고리즘으로 후보군을 추리고, 다른 알고리즘으로 순위를 매깁니다. 예를 들어 협업 필터링으로 100개의 후보를 뽑은 뒤, 내용 기반 필터링으로 최종 10개를 선정하는 방식입니다.
가중치 설정이 핵심입니다. 위 코드에서 content_weight와 collab_weight가 각각 0.4와 0.6으로 설정되어 있습니다.
이는 협업 필터링에 조금 더 비중을 두겠다는 의미입니다. 하지만 최적의 가중치는 도메인과 데이터에 따라 다릅니다.
A/B 테스트를 통해 실험적으로 찾아야 합니다. 하이브리드 방식의 장점은 강력합니다.
내용 기반 필터링은 콜드 스타트에 강하고, 협업 필터링은 예상치 못한 발견에 강합니다. 둘을 결합하면 신규 사용자에게도 괜찮은 추천을 하면서, 기존 사용자에게는 놀라운 발견을 제공할 수 있습니다.
또한 한 알고리즘이 실패해도 다른 알고리즘이 보완해주므로 안정성도 높아집니다. 물론 복잡성이라는 대가가 있습니다.
두 개의 시스템을 모두 구축하고 유지해야 합니다. 가중치 튜닝도 필요하고, 계산 비용도 늘어납니다.
하지만 대부분의 대형 서비스는 이 정도 복잡성을 감수할 가치가 있다고 판단합니다.
실전 팁
💡 - 가중치는 고정하지 말고 사용자별, 상황별로 동적으로 조절하는 것이 좋습니다.
- 처음에는 단순한 가중 평균으로 시작하고, 데이터가 쌓이면 머신러닝으로 최적의 결합 방식을 학습시키세요.
5. 콜드 스타트 문제
"선배, 그런데 우리 서비스에 새 사용자가 가입하면 어떡하죠? 아무 데이터도 없는데..." 김개발 씨의 걱정스러운 표정에 박시니어 씨가 말했습니다.
"그게 바로 그 유명한 콜드 스타트 문제야."
콜드 스타트 문제는 새로운 사용자나 새로운 아이템에 대한 데이터가 없어서 추천이 어려운 상황을 말합니다. 마치 처음 방문한 식당에서 "평소 뭐 좋아하세요?"라고 물어봐도 손님의 취향을 알 수 없는 것과 같습니다.
추천 시스템의 가장 고전적인 난제 중 하나입니다.
다음 코드를 살펴봅시다.
class ColdStartHandler:
def __init__(self):
self.popular_items = [] # 인기 아이템 캐시
self.item_features = {} # 아이템 특성 정보
def handle_new_user(self, user_profile=None):
if user_profile and user_profile.get('preferences'):
# 온보딩에서 수집한 선호도 활용
return self._content_based_for_preferences(user_profile['preferences'])
elif user_profile and user_profile.get('demographics'):
# 인구통계학적 정보 활용 (나이, 성별 등)
return self._demographic_based_recommendation(user_profile['demographics'])
else:
# 최후의 수단: 인기 아이템 추천
return self.popular_items[:10]
def handle_new_item(self, item_id, item_features):
# 새 아이템: 특성 기반으로 유사 아이템 그룹에 배치
similar_items = self._find_similar_items(item_features)
# 유사 아이템을 좋아한 사용자에게 추천 대상으로 등록
self._register_for_recommendation(item_id, similar_items)
return True
def _content_based_for_preferences(self, preferences):
# 선호 장르/키워드와 매칭되는 아이템 반환
return [item for item, feat in self.item_features.items()
if any(p in feat for p in preferences)]
김개발 씨는 자신이 새로 가입한 음악 스트리밍 서비스를 떠올렸습니다. 가입하자마자 "좋아하는 아티스트 3명을 선택하세요"라는 화면이 나왔습니다.
귀찮았지만 선택하고 나니, 첫 플레이리스트부터 마음에 들었습니다. 그제야 깨달았습니다.
이게 바로 콜드 스타트 문제를 해결하는 방법이었구나! 콜드 스타트 문제는 세 가지 상황에서 발생합니다.
첫째, 새로운 사용자가 가입했을 때입니다. 이 사용자가 무엇을 좋아하는지 전혀 알 수 없습니다.
둘째, 새로운 아이템이 추가되었을 때입니다. 아무도 평가하지 않았으므로 협업 필터링이 작동하지 않습니다.
셋째, 새로운 시스템을 구축했을 때입니다. 사용자도 아이템도 데이터가 없는 완전한 백지 상태입니다.
새로운 사용자 문제는 어떻게 해결할까요? 가장 효과적인 방법은 온보딩 과정에서 선호도를 수집하는 것입니다.
"좋아하는 장르를 선택하세요", "관심 있는 카테고리를 고르세요" 같은 질문으로 초기 데이터를 확보합니다. 스포티파이, 넷플릭스 모두 이 방식을 사용합니다.
또 다른 방법은 인구통계학적 정보 활용입니다. 20대 남성은 평균적으로 어떤 콘텐츠를 좋아하는지 데이터가 있다면, 새로운 20대 남성 사용자에게 그것을 추천할 수 있습니다.
물론 개인차가 크므로 완벽하지는 않습니다. 인기 아이템 추천은 최후의 수단입니다.
아무 정보가 없다면 그냥 많은 사람들이 좋아하는 것을 보여줍니다. 틀릴 확률보다 맞을 확률이 높기 때문입니다.
새로운 아이템 문제는 다르게 접근합니다. 새 영화가 등록되면 아무도 평점을 주지 않았으므로 협업 필터링에서 제외됩니다.
이때 내용 기반 필터링이 빛을 발합니다. 영화의 장르, 감독, 배우 정보를 분석하여 비슷한 영화를 좋아한 사용자에게 추천합니다.
탐색과 활용의 균형도 중요합니다. 때로는 인기 있는 것만 추천하지 않고, 새로운 아이템을 의도적으로 노출시켜 데이터를 수집해야 합니다.
이를 **탐색(Exploration)**이라고 합니다. 반대로 이미 좋다고 알려진 것을 추천하는 것은 **활용(Exploitation)**입니다.
추천 시스템은 이 둘 사이에서 적절한 균형을 찾아야 합니다.
실전 팁
💡 - 온보딩은 길면 이탈률이 높아집니다. 3-5개 선택 정도로 간결하게 유지하세요.
- 새 아이템은 일정 기간 강제로 노출시켜 데이터를 빠르게 수집하는 전략도 고려해보세요.
6. 넷플릭스 아마존 사례
김개발 씨는 궁금해졌습니다. "실제로 대기업들은 어떻게 추천 시스템을 운영하고 있을까?" 박시니어 씨가 노트북을 열며 말했습니다.
"넷플릭스와 아마존 사례를 보면 많은 것을 배울 수 있어."
넷플릭스와 아마존은 추천 시스템의 선구자로 알려져 있습니다. 넷플릭스는 100만 달러 상금의 추천 알고리즘 대회를 열기도 했고, 아마존은 "이 상품을 본 고객이 함께 본 상품"으로 이커머스 추천의 표준을 만들었습니다.
이들의 사례에서 실무적인 인사이트를 얻을 수 있습니다.
다음 코드를 살펴봅시다.
# 넷플릭스 스타일: 행렬 분해 기반 추천
import numpy as np
class MatrixFactorization:
def __init__(self, n_factors=20, learning_rate=0.01, n_epochs=100):
self.n_factors = n_factors # 잠재 요인 수
self.lr = learning_rate
self.n_epochs = n_epochs
def fit(self, ratings_matrix):
n_users, n_items = ratings_matrix.shape
# 사용자와 아이템의 잠재 요인 벡터 초기화
self.user_factors = np.random.normal(0, 0.1, (n_users, self.n_factors))
self.item_factors = np.random.normal(0, 0.1, (n_items, self.n_factors))
# SGD로 학습
for epoch in range(self.n_epochs):
for u in range(n_users):
for i in range(n_items):
if ratings_matrix[u, i] > 0: # 평점이 있는 경우만
error = ratings_matrix[u, i] - self.predict(u, i)
# 그래디언트 업데이트
self.user_factors[u] += self.lr * error * self.item_factors[i]
self.item_factors[i] += self.lr * error * self.user_factors[u]
def predict(self, user_id, item_id):
return np.dot(self.user_factors[user_id], self.item_factors[item_id])
2006년, 넷플릭스는 업계를 발칵 뒤집는 발표를 했습니다. 자사의 추천 알고리즘 성능을 10% 개선하는 팀에게 100만 달러를 주겠다는 것이었습니다.
이 대회는 넷플릭스 프라이즈라 불렸고, 전 세계 수천 개 팀이 참가했습니다. 2009년에 우승팀이 나왔습니다.
그들이 사용한 핵심 기법은 **행렬 분해(Matrix Factorization)**였습니다. 행렬 분해의 아이디어는 간단합니다.
거대한 사용자-아이템 평점 행렬을 두 개의 작은 행렬로 분해합니다. 하나는 사용자의 잠재 요인 행렬이고, 다른 하나는 아이템의 잠재 요인 행렬입니다.
잠재 요인이란 "이 사용자가 얼마나 액션을 좋아하는가", "이 영화가 얼마나 로맨틱한가" 같은 숨겨진 특성을 의미합니다. 위 코드는 이 행렬 분해를 구현한 것입니다.
n_factors는 잠재 요인의 수입니다. 20개의 요인이 있다면 각 사용자와 영화는 20차원 벡터로 표현됩니다.
학습 과정에서 실제 평점과 예측 평점의 차이(error)를 줄이는 방향으로 벡터를 조정합니다. 학습이 완료되면 사용자 벡터와 영화 벡터의 내적으로 평점을 예측할 수 있습니다.
아마존의 접근 방식은 다릅니다. 아마존은 아이템 기반 협업 필터링을 선택했습니다.
사용자 수가 아이템 수보다 훨씬 많고 빠르게 변하기 때문입니다. 아이템 간의 유사도는 상대적으로 안정적이어서 미리 계산해둘 수 있습니다.
사용자가 상품을 볼 때 미리 계산된 유사 상품을 즉시 보여주면 됩니다. 아마존의 "이 상품을 본 고객이 함께 본 상품" 기능이 바로 이것입니다.
두 회사의 공통점도 있습니다. 첫째, 하이브리드 방식을 사용합니다.
넷플릭스도 행렬 분해만 쓰는 게 아니라 여러 알고리즘을 앙상블합니다. 둘째, 지속적인 A/B 테스트를 진행합니다.
새로운 알고리즘은 반드시 실험을 통해 검증됩니다. 셋째, 개인화의 깊이가 다릅니다.
같은 영화 목록도 사용자마다 순서가 다르고, 심지어 썸네일 이미지도 다르게 보여줍니다. 성과는 어떨까요?
넷플릭스는 시청 시간의 80%가 추천에서 발생한다고 밝혔습니다. 아마존은 매출의 35%가 추천을 통해 발생합니다.
추천 시스템은 단순한 편의 기능이 아니라 비즈니스의 핵심 엔진인 것입니다.
실전 팁
💡 - 행렬 분해는 강력하지만 실시간 추천에는 부적합합니다. 미리 학습해두고 예측값을 캐싱하세요.
- A/B 테스트 없이 알고리즘을 배포하지 마세요. 오프라인 지표와 온라인 성과는 다를 수 있습니다.
7. 실용 예제 영화 추천 엔진
"자, 이제 직접 만들어볼까?" 박시니어 씨가 키보드에 손을 올렸습니다. "지금까지 배운 것들을 활용해서 실제로 동작하는 영화 추천 엔진을 만들어보자."
이번 섹션에서는 실제로 동작하는 영화 추천 엔진을 처음부터 끝까지 구현합니다. 내용 기반 필터링과 협업 필터링을 결합한 하이브리드 방식을 사용하며, MovieLens 데이터셋을 활용합니다.
코드를 따라 치면서 추천 시스템의 전체 흐름을 이해할 수 있습니다.
다음 코드를 살펴봅시다.
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
class MovieRecommender:
def __init__(self):
self.movies = None
self.ratings = None
self.content_sim = None
def load_data(self, movies_path, ratings_path):
self.movies = pd.read_csv(movies_path)
self.ratings = pd.read_csv(ratings_path)
self._build_content_similarity()
def _build_content_similarity(self):
# 장르를 TF-IDF로 벡터화
tfidf = TfidfVectorizer(token_pattern=r'[^|]+')
genre_matrix = tfidf.fit_transform(self.movies['genres'].fillna(''))
self.content_sim = cosine_similarity(genre_matrix)
def recommend(self, user_id, n=10):
# 사용자가 높게 평가한 영화 찾기
user_ratings = self.ratings[self.ratings['userId'] == user_id]
liked = user_ratings[user_ratings['rating'] >= 4]['movieId'].tolist()
# 내용 기반 점수 계산
scores = np.zeros(len(self.movies))
for movie_id in liked:
idx = self.movies[self.movies['movieId'] == movie_id].index
if len(idx) > 0:
scores += self.content_sim[idx[0]]
# 이미 본 영화 제외 후 상위 N개 반환
seen = set(user_ratings['movieId'].tolist())
recommendations = [(i, scores[i]) for i in range(len(scores))
if self.movies.iloc[i]['movieId'] not in seen]
recommendations.sort(key=lambda x: x[1], reverse=True)
return [self.movies.iloc[i]['title'] for i, _ in recommendations[:n]]
드디어 김개발 씨의 실습 시간입니다. 지금까지 배운 이론을 코드로 옮길 차례입니다.
박시니어 씨는 MovieLens 데이터셋을 준비해두었습니다. 이 데이터셋은 추천 시스템 연구에서 가장 널리 사용되는 벤치마크입니다.
먼저 데이터 구조를 이해해야 합니다. movies.csv에는 영화 정보가 있습니다.
movieId, title, genres 컬럼으로 구성됩니다. genres는 "Action|Adventure|Sci-Fi" 형태로 파이프(|)로 구분되어 있습니다.
ratings.csv에는 평점 정보가 있습니다. userId, movieId, rating, timestamp 컬럼으로 구성됩니다.
클래스 구조를 살펴봅시다. MovieRecommender 클래스가 추천 엔진의 본체입니다.
load_data 메서드로 데이터를 로드하고, 로드할 때 자동으로 _build_content_similarity를 호출하여 영화 간 유사도 행렬을 미리 계산해둡니다. 내용 기반 유사도는 어떻게 계산할까요?
영화의 장르를 TF-IDF로 벡터화합니다. 각 영화는 장르에 따라 다차원 벡터가 됩니다.
그리고 모든 영화 쌍의 코사인 유사도를 계산하여 행렬로 저장합니다. 이 행렬에서 **content_sim[i][j]**는 i번째 영화와 j번째 영화의 유사도를 나타냅니다.
추천 로직은 다음과 같습니다. 먼저 해당 사용자가 4점 이상 준 영화들을 찾습니다.
이것이 "좋아한 영화" 목록입니다. 그 다음, 각 좋아한 영화와 유사한 영화들의 점수를 합산합니다.
점수가 높을수록 사용자가 좋아한 영화들과 비슷한 영화라는 뜻입니다. 마지막으로 이미 본 영화를 제외하고 점수가 높은 순으로 N개를 반환합니다.
실제로 실행해보면 이렇게 됩니다. 사용자 1번이 "토이 스토리"와 "라이온 킹"을 좋아했다면, 시스템은 "알라딘", "인어공주" 같은 비슷한 장르의 애니메이션을 추천할 것입니다.
물론 이것은 기본 버전이고, 협업 필터링을 추가하면 더 정교한 추천이 가능합니다. 이 코드를 확장하려면 어떻게 해야 할까요?
첫째, 협업 필터링 점수를 추가하세요. 유사 사용자가 좋아한 영화도 점수에 반영합니다.
둘째, 가중치를 조절하세요. 내용 기반과 협업 필터링의 비율을 실험해보세요.
셋째, 더 풍부한 특성을 추가하세요. 감독, 배우, 개봉년도 등을 벡터에 포함시키면 추천 품질이 올라갑니다.
김개발 씨는 코드를 실행하며 뿌듯함을 느꼈습니다. "진짜 동작하네요!" 박시니어 씨가 웃으며 말했습니다.
"이제 시작이야. 여기서부터 계속 개선해나가면 돼."
실전 팁
💡 - MovieLens 데이터셋은 https://grouplens.org/datasets/movielens/ 에서 무료로 다운로드할 수 있습니다.
- 실제 서비스에 적용할 때는 유사도 행렬을 미리 계산해두고 캐싱하여 응답 속도를 높이세요.
- 추천 결과의 다양성도 중요합니다. 너무 비슷한 영화만 추천하지 않도록 다양성 지표를 모니터링하세요.
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.