콘텐츠로 이동

추천 시스템 (Recommender Systems)

난이도: 중급
선수 지식: k-NN, 차원 축소 — 유사도와 행렬 분해 개념
관련 문서: 랭킹 지표 | ML 시스템 설계 패턴 | 인과 추론 기초

핵심 요약: 추천의 두 축은 “너와 비슷한 사람이 좋아한 것”(협업 필터링)과 “네가 좋아한 것과 비슷한 것”(콘텐츠 기반)이다. 협업 필터링이 대체로 더 강력하지만 신규 사용자·신규 아이템에는 무력하다(콜드 스타트). 그리고 정확도만 좇으면 이미 좋아할 것만 보여주는 필터 버블에 빠지므로, 다양성과 신규성을 함께 봐야 한다.

  • 협업 필터링(Collaborative Filtering): 다른 사용자들의 행동 패턴을 이용. 아이템 내용을 몰라도 추천할 수 있다.
  • 콘텐츠 기반(Content-based): 아이템의 속성(장르·태그·설명)이 비슷한 것을 추천.
  • 행렬 분해(Matrix Factorization): 거대한 사용자×아이템 평점 행렬을 작은 두 행렬의 곱으로 근사하는 기법.
  • 잠재 요인(Latent Factor): 행렬 분해가 찾아낸 이름 없는 취향 축. “액션성”, “잔잔함” 같은 것이지만 사람이 이름을 붙여준 것은 아니다.
  • 콜드 스타트(Cold Start): 기록이 없는 신규 사용자·신규 아이템을 추천할 수 없는 문제.

추천 시스템은 “사용자가 아직 보지 않은 것 중 무엇을 좋아할까”를 맞히는 문제다. 넷플릭스 시청의 대부분, 유튜브 시청 시간의 상당 부분이 추천에서 나온다.

기술적으로는 랭킹 문제다. 정답을 맞히는 것이 아니라 순서를 잘 매기는 것이 목표이므로, 평가도 랭킹 지표를 쓴다.

접근원리강점약점
협업 필터링비슷한 사용자/아이템의외의 발견, 내용 분석 불필요콜드 스타트, 희소성
콘텐츠 기반아이템 속성 유사도신규 아이템 OK, 설명 가능비슷한 것만 추천
하이브리드둘을 결합실무 표준복잡도

비유: 서점 직원 두 명이다. 협업 필터링 직원은 “이 책 사신 분들이 저 책도 많이 사셨어요”라고 한다. 책 내용은 몰라도 되고, 뜻밖의 좋은 책을 만날 수 있다. 다만 오늘 처음 온 손님에게는 할 말이 없다. 콘텐츠 기반 직원은 “SF를 좋아하시니 이 SF도 좋아하실 겁니다”라고 한다. 처음 온 손님에게도 말할 수 있지만, 계속 SF만 권한다.


추천 시스템을 학문적·산업적으로 폭발시킨 사건은 Netflix Prize(2006~2009)였다.

넷플릭스는 자사 추천 알고리즘 Cinematch의 RMSE를 10% 개선하면 100만 달러를 주겠다고 공개했다. 1억 개의 평점 데이터가 함께 공개되었고, 전 세계 수만 팀이 3년간 매달렸다.

이 대회가 남긴 것은 상금보다 컸다.

  • 행렬 분해가 협업 필터링의 표준 기법으로 자리 잡았다
  • 앙상블의 위력이 입증되었다(우승 솔루션은 100개 이상의 모델 조합이었다)
  • 그리고 역설적으로, 우승 알고리즘은 실제로 배포되지 않았다 — 너무 복잡해 운영 비용이 이득을 넘어섰기 때문이다

마지막 사실이 실무에 주는 교훈이 가장 크다. 오프라인 지표 0.1% 개선이 서비스에서 아무 의미가 없을 수 있다.

이후 딥러닝 기반 추천(신경망 협업 필터링, 시퀀스 모델, 투 타워 구조)이 등장했지만, 행렬 분해는 여전히 강력한 기준선이다.


방식특징
사용자 기반”나와 비슷한 사람이 좋아한 것”사용자 수가 많으면 계산 부담
아이템 기반”내가 좋아한 것과 비슷한 것”아이템이 사용자보다 적고 안정적이라 실무에서 선호

아이템 간 유사도는 “같은 사용자들이 함께 좋아했는가”로 잰다. 장르가 달라도 함께 소비되면 유사하다고 판단하는 것이 협업 필터링의 힘이다.

사용자 mm명 × 아이템 nn개의 평점 행렬 RR을 두 개의 작은 행렬로 근사한다.

Rm×nUm×kVk×nTR_{m \times n} \approx U_{m \times k} \cdot V^T_{k \times n}

  • kk잠재 요인의 수 (보통 10~200)
  • UU의 각 행은 사용자의 취향 벡터, VV의 각 행은 아이템의 특성 벡터
  • 예측 평점은 두 벡터의 내적: r^ui=uuvi\hat{r}_{ui} = u_u \cdot v_i

왜 잘 작동하는가: 평점 행렬은 99% 이상이 비어 있다(대부분의 사용자가 대부분의 아이템을 평가하지 않는다). 행렬 분해는 이 희소한 데이터를 저차원 구조로 압축해 빈칸을 메운다. 차원 축소와 같은 발상이다.

유형문제대응
신규 사용자행동 기록이 없다인기 아이템, 가입 시 취향 설문, 인구통계
신규 아이템아무도 평가하지 않았다콘텐츠 기반(속성 활용)
신규 서비스데이터가 아예 없다규칙 기반으로 시작해 데이터 축적

순수 협업 필터링은 콜드 스타트에 완전히 무력하다. 실무에서 하이브리드가 표준인 가장 큰 이유다.

4. 정확도만 좇으면 생기는 문제

섹션 제목: “4. 정확도만 좇으면 생기는 문제”

추천 시스템은 자기 예측이 다음 데이터를 만드는 특이한 구조를 갖는다.

추천사용자가 그것을 봄그 로그로 학습같은 것을 더 추천\text{추천} \rightarrow \text{사용자가 그것을 봄} \rightarrow \text{그 로그로 학습} \rightarrow \text{같은 것을 더 추천}

피드백 루프 때문에 생기는 것들:

  • 필터 버블: 이미 좋아하는 것만 계속 보게 된다
  • 인기 편향: 인기 있는 것이 더 노출되어 더 인기 있어진다
  • 다양성 붕괴: 롱테일 아이템이 영원히 발견되지 않는다

그래서 정확도 외에 다양성(diversity), 신규성(novelty), 커버리지(coverage), 의외성(serendipity)을 함께 본다.

사용자 6명 × 영화 6편의 평점 행렬이다(0은 미평가).

액션A액션B액션C로맨스A로맨스B로맨스C
민수554112
지영455211
현우544121
서연121554
예린211455
도윤11254?

도윤이 로맨스C를 좋아할지 예측해 보자. 아이템 기반 협업 필터링은 로맨스C와 다른 영화들의 유사도를 구한 뒤, 도윤의 평점을 그 유사도로 가중평균한다.

아이템로맨스C와의 유사도도윤 평점기여
액션A0.57110.57
액션B0.55410.55
액션C0.49420.99
로맨스A0.95754.79
로맨스B0.97543.90

r^=10.803.55=3.04\hat{r} = \frac{10.80}{3.55} = 3.04

도윤이 액션에 준 점수는 1~2점인데 로맨스C 예측은 3.04점 — 확실히 높다.

그런데 한 가지 이상한 점: 액션 영화들도 유사도가 0.5 근처로 나온다. 장르가 정반대인데도 말이다. 모든 평점이 양수라 코사인 유사도가 전반적으로 부풀려지기 때문이다. 실무에서는 사용자 평균을 뺀 조정 코사인(adjusted cosine)을 써서 이 문제를 완화한다.


추천 시스템은 오프라인 평가와 온라인 성과가 자주 어긋난다.

측정하는 것한계
오프라인과거 로그에서 RMSE, NDCG, Recall@k추천하지 않았던 것의 반응을 알 수 없다
온라인(A/B)실제 클릭·구매·체류 시간느리고 비싸다

핵심 한계는 편향된 로그다. 우리가 가진 데이터는 과거 추천 시스템이 보여준 것에 대한 반응뿐이다. 보여주지 않은 아이템을 사용자가 좋아했을지는 알 수 없다 — 이것이 인과 추론 문제와 직결된다.


상황접근
사용자 행동 데이터가 충분협업 필터링(행렬 분해부터)
신규 아이템이 계속 들어옴(뉴스·상품)콘텐츠 기반 비중 확대
서비스 초기, 데이터 부족인기순 + 규칙 기반으로 시작
세션 내 행동이 중요(이커머스)시퀀스 모델
설명이 필요(“왜 이걸 추천했나”)콘텐츠 기반 또는 아이템 기반 CF

인기순 추천은 놀랍도록 강력한 기준선이다. 복잡한 모델이 이것을 못 이기는 경우가 흔하므로 반드시 먼저 재봐야 한다.


정확도를 올렸더니 매출이 떨어진 팀

섹션 제목: “정확도를 올렸더니 매출이 떨어진 팀”

한 음악 스트리밍 서비스가 추천 모델을 개선해 오프라인 NDCG를 12% 향상시켰다. 사용자가 실제로 들을 곡을 더 정확히 맞히게 된 것이다.

A/B 테스트 결과는 예상 밖이었다.

지표변화
추천 곡 클릭률+18%
세션당 재생 시간−3%
월 구독 유지율−2.1%

원인은 다양성 붕괴였다. 모델이 정확해질수록 사용자가 이미 아는 곡을 더 잘 골랐고, 추천 목록의 장르 다양성이 80% 감소했다. 사용자는 클릭은 더 했지만 새로운 음악을 발견하지 못해 서비스에 머물 이유가 줄었다.

팀은 추천 목록의 일정 비율을 의도적으로 탐색용 곡으로 채우고, 목표 지표를 클릭률에서 “신규 아티스트 저장 수”로 바꿨다. 이후 유지율이 회복되었다.

교훈: 추천 시스템에서 정확도는 목표가 아니라 수단이다. 진짜 목표(유지율·만족도)와 정확도가 어긋나는 일이 흔하며, 그 차이를 만드는 것이 다양성이다.


import numpy as np
# 사용자-아이템 평점 행렬 (0 = 미평가)
R = np.array([
[5, 5, 4, 1, 1, 2],
[4, 5, 5, 2, 1, 1],
[5, 4, 4, 1, 2, 1],
[1, 2, 1, 5, 5, 4],
[2, 1, 1, 4, 5, 5],
[1, 1, 2, 5, 4, 0], # 도윤 - 마지막 항목 미평가
], dtype=float)
users = ["민수","지영","현우","서연","예린","도윤"]
items = ["액션A","액션B","액션C","로맨스A","로맨스B","로맨스C"]
def cos(a, b):
m = (a > 0) & (b > 0)
if m.sum() < 2: return 0.0
return float(a[m] @ b[m] / (np.linalg.norm(a[m]) * np.linalg.norm(b[m])))
u, target = 5, 5 # 도윤 / 로맨스C
print(f"{users[u]}의 평가: " + ", ".join(
f"{items[j]}={int(R[u,j])}" for j in range(6) if R[u,j] > 0))
print(f"\n'{items[target]}'을 좋아할까? 다른 아이템과의 유사도로 추정한다\n")
print(f"{'아이템':>8} {'유사도':>8} {'도윤 평점':>10} {'기여':>8}")
num = den = 0
for j in range(6):
if R[u, j] == 0: continue
s = cos(R[:, target], R[:, j])
num += s * R[u, j]; den += abs(s)
print(f"{items[j]:>8} {s:>8.3f} {int(R[u,j]):>10} {s*R[u,j]:>8.2f}")
print(f"\n예상 평점 = {num:.2f} / {den:.2f} = {num/den:.2f}")
# 실행 결과:
# 도윤의 평가: 액션A=1, 액션B=1, 액션C=2, 로맨스A=5, 로맨스B=4
#
# '로맨스C'을 좋아할까? 다른 아이템과의 유사도로 추정한다
#
# 아이템 유사도 도윤 평점 기여
# 액션A 0.571 1 0.57
# 액션B 0.554 1 0.55
# 액션C 0.494 2 0.99
# 로맨스A 0.957 5 4.79
# 로맨스B 0.975 4 3.90
#
# 예상 평점 = 10.80 / 3.55 = 3.04

로맨스A·B의 유사도가 0.960.98로 압도적이고, 도윤이 그 둘에 5점·4점을 줬기 때문에 로맨스C 예측이 높게 나온다. 액션에 12점을 준 사람에게 3.04점은 확실히 높은 값이다.

주목할 점은 알고리즘이 “로맨스”라는 장르를 전혀 모른다는 것이다. 오직 “같은 사람들이 함께 높게 평가했다”는 사실만으로 유사도를 계산했다. 이것이 협업 필터링의 힘이며, 장르 태그가 없는 아이템에도 쓸 수 있는 이유다.

한편 액션 영화들도 유사도 0.5 근처인 것은 문제다. 모든 평점이 양수라 코사인이 과대평가되기 때문이며, 실무에서는 사용자 평균을 뺀 조정 코사인으로 보정한다.


  1. “정확도가 높은 추천이 좋은 추천이다” — 정확도만 좇으면 이미 아는 것만 추천하게 되어 발견의 가치가 사라진다. 다양성·신규성을 함께 봐야 한다.

  2. “오프라인 지표가 좋으면 서비스도 좋아진다” — Netflix Prize 우승 알고리즘은 배포되지 않았다. 오프라인 개선이 온라인 성과로 이어지지 않는 일이 흔하다.

  3. “복잡한 모델일수록 좋다”인기순 추천이라는 단순 기준선을 못 이기는 경우가 매우 흔하다. 반드시 먼저 재라.

  4. “클릭 로그는 사용자 선호를 반영한다” — 로그는 과거 시스템이 보여준 것에 대한 반응일 뿐이다. 보여주지 않은 아이템의 선호는 알 수 없다(선택 편향).

  5. “협업 필터링이면 콜드 스타트도 해결된다” — 정반대다. 협업 필터링은 기록이 없으면 아무것도 못 한다. 콘텐츠 기반과 섞어야 한다.


1. 오늘 가입한 사용자에게 협업 필터링으로 추천할 수 있는가?

정답 보기

없다. 협업 필터링은 “비슷한 사용자/아이템”을 찾는데, 행동 기록이 없으면 비교할 대상이 없다(콜드 스타트). 대응은 인기 아이템 노출, 가입 시 취향 선택, 인구통계 기반 추천, 또는 콘텐츠 기반으로 시작해 데이터를 모으는 것이다.

2. 추천 정확도를 12% 올렸는데 구독 유지율이 떨어졌다. 무엇을 확인해야 하는가?

정답 보기

다양성과 신규성이다. 정확도가 오를수록 사용자가 이미 아는 것을 잘 맞히게 되어, 새로운 발견이 사라지고 서비스에 머물 이유가 줄어든다. 추천 목록의 장르 커버리지, 신규 아이템 비율, 롱테일 노출을 함께 재야 한다.

3. 협업 필터링이 장르 태그 없이도 “로맨스끼리 비슷하다”를 알아내는 원리는?

정답 보기

같은 사용자들이 함께 높게 평가했기 때문이다. 아이템의 내용은 전혀 보지 않고 소비 패턴의 공통성만 본다. 그래서 장르가 달라도 함께 소비되는 아이템은 유사하다고 판단하며, 이것이 뜻밖의 좋은 추천(serendipity)을 만드는 원천이기도 하다.