사용자 행동 아이콘이 인기 순위 장치와 개인화 행렬 장치를 통과해 추천 목록으로 나뉘는 모형

추천 시스템 implicit feedback 평가: popularity baseline과 matrix factorization을 Recall@K로 비교하기

추천 시스템 implicit feedback 평가에서 먼저 정할 것은 복잡한 모델이 아니라 채택 기준이다. 같은 과거 데이터와 같은 추천 후보를 썼을 때 개인화 모델이 popularity baseline보다 다음 상호작용을 더 자주 회수하는가, 그리고 그 개선을 소수 인기 아이템의 반복 노출로 만들지는 않는가를 함께 물어야 한다.

2026년 8월 8일 MovieLens 100K를 이용해 이 질문을 통제 비교했다. 평점 100,000건을 관측 상호작용으로 바꾸고 사용자 943명의 마지막 사건을 하나씩 남긴 뒤, popularity와 12차원 BPR matrix factorization을 전체 1,682개 아이템 후보에서 Recall@10과 catalog coverage@10으로 평가했다. 이는 공개 영화 평점 데이터의 목적 제작 실험이며 클릭 로그나 온라인 성과를 검증한 결과는 아니다.

20초 핵심 요약

  • 무엇: 사용자별 다음 관측 아이템을 popularity와 BPR-MF가 top-10에서 얼마나 회수하는지 비교했다.
  • 왜: 무작위 분할은 943명 중 886명의 미래 상호작용을 학습에 섞어 모델을 실제보다 좋게 선택하게 만들 수 있다.
  • 어떻게: 사용자별 마지막 사건을 holdout하고 전체 카탈로그에서 Recall@10과 coverage@10을 같은 조건으로 측정했다.

관측 1과 미관측 0은 좋아요와 싫어요가 아니다

입력 행렬의 행은 사용자, 열은 아이템이다. 이번 실험에서는 MovieLens의 별점 크기를 버리고 “평점을 남겼다”를 관측 positive인 1로 바꿨다. 평점이 없는 칸은 0/unknown이다. Google의 collaborative filtering 설명처럼 행동으로 관심을 추론하는 implicit feedback 표현이다.

이 변환은 예측 대상을 좁힌다. 이 글에서 맞히려는 것은 사용자가 좋아할 영화가 아니라 다음에 평점을 남긴 영화다. 1점 평점도 positive에 포함되며, 미관측 칸에는 미노출·무관심·기회 부족이 함께 섞인다. 실제 서비스 로그에 impression이 없다면 보지 못한 아이템과 보고도 클릭하지 않은 아이템을 이 행렬만으로 구분할 수 없다.

따라서 BPR 학습에서 뽑는 negative도 실제 비선호 레이블이 아니다. 한 번의 업데이트에서 관측 positive보다 낮게 순위를 매기도록 선택한 미관측 비교 항목이다. 이 차이를 지우면 모델이 무엇을 학습했는지보다 강한 의미를 결과에 부여하게 된다.

마지막 사건 하나를 남겨 미래 데이터의 문을 닫았다

각 사용자의 timestamp가 가장 늦은 상호작용 한 건을 test 정답으로 두고 나머지만 train에 넣었다. 평가 질문은 “이 사용자의 과거만 알 때 다음 관측 아이템을 추천 10개 안에 넣을 수 있는가”가 된다. train에서 이미 본 아이템은 두 모델의 추천 후보에서 똑같이 제외했다.

MovieLens rating 행에는 사용자, 영화, 평점, timestamp가 있으며 timestamp는 Unix epoch UTC 초다. 이 필드를 이용해 사용자별 시간 경계를 만들었다. 데이터 형식은 GroupLens MovieLens README에서 확인할 수 있다.

무작위로 사용자별 한 건을 뽑는 대조 실험도 실행했다. 점수만 보면 temporal split보다 좋아졌다. popularity Recall@10은 0.0838에서 0.1368로, MF는 0.1039에서 0.1962로 상승했다. 그러나 test보다 나중에 발생한 train 상호작용이 없어야 한다는 assertion은 다음처럼 실패했다.

random_holdout_users_with_future_training=886/943
Traceback (most recent call last):
  File "<stdin>", line 11, in <module>
AssertionError: random row holdout violates the temporal boundary
[exit status 1]

943명 중 886명, 즉 94.0%에서 미래 상호작용이 학습 데이터에 들어갔다. 다만 random 점수와 temporal 점수의 차이 전부를 누수의 인과 효과라고 단정할 수는 없다. holdout 아이템의 난이도와 시기별 인기 변화도 동시에 달라졌기 때문이다. 확인된 실패는 높은 점수가 아니라 시간 경계 위반이다.

popularity와 BPR-MF에 같은 시험지를 줬다

Popularity baseline은 train의 아이템별 상호작용 수를 세고, 사용자가 아직 보지 않은 항목 중 인기 순서대로 10개를 추천한다. 개인화하지 않지만 복잡한 모델이 단순 인기 신호보다 실제로 나은지 드러내는 대조군이다. 동률은 item id 오름차순으로 정했다.

BPR-MF는 사용자와 아이템을 각각 12차원 벡터로 표현하고 두 벡터의 내적으로 순위 점수를 만들었다. 관측 아이템 점수가 sampled unobserved 아이템보다 높아지도록 pairwise SGD를 6 epochs 수행했다. learning rate는 0.04, L2는 0.01, seed는 20260808이다. 사용자·아이템 임베딩과 내적 점수의 구조는 Google의 matrix factorization 설명, BPR 목적과 bootstrap sampling은 BPR 원 논문에 근거한다.

실행 환경은 Linux와 CPython 3.12.3이며 Python 표준 라이브러리만 사용했다. 두 모델은 동일한 temporal train/test와 동일한 전체 카탈로그 후보를 사용했다. 모델만 바꾸고 시험지는 바꾸지 않은 셈이다.

사용자별 마지막 상호작용을 분리하고 동일 후보군에서 두 모델을 평가하는 통제 실험 흐름도

Recall은 정답 회수, coverage는 노출 폭을 묻는다

사용자 u의 test relevant set을 T_u, 추천 상위 K개를 R_u@K라고 두면 지표는 다음과 같다.

Recall@K = mean_u ( |T_u ∩ R_u@K| / |T_u| )
Catalog coverage@K = | union_u R_u@K | / |전체 catalog|

이번에는 사용자마다 정답이 하나라 top-10에 들어오면 사용자 Recall은 1, 아니면 0이다. 따라서 평균 Recall@10은 943명 중 정답을 맞힌 비율이며 Hit Rate@10과 수치가 같다. Microsoft Recommenders의 Recall@KTensorFlow Recommenders의 FactorizedTopK도 top-K 후보와 true candidate를 맞추는 평가 경로를 제공한다.

Catalog coverage@10은 모든 사용자의 추천 목록에 한 번이라도 등장한 고유 아이템 수를 전체 1,682개로 나눈 값이다. 낮으면 추천이 좁은 품목에 몰렸다는 뜻이지만, 높다고 관련성·다양성·신규성·공정성이 자동으로 좋아지는 것은 아니다. Recall과 다른 질문에 답하는 보조 지표로 읽어야 한다.

시간 분할에서는 MF가 19명을 더 맞혔다

split model Recall@10 hits catalog coverage@10
temporal popularity 0.0838 79/943 0.0541
temporal BPR-MF 0.1039 98/943 0.1427
random(실패 대조) popularity 0.1368 129/943 0.0547
random(실패 대조) BPR-MF 0.1962 185/943 0.1486

채택 판단에는 temporal 두 행만 사용한다. BPR-MF는 popularity보다 19명의 다음 아이템을 추가로 top-10에 넣었다. Recall의 절대 차이는 +0.0201, 상대 차이는 약 +24.1%다. Coverage도 0.0541에서 0.1427로 올라 절대 +0.0886, 약 2.64배가 됐다. 이 데이터·분할·seed·파라미터에서는 개인화가 정답 회수를 얻으면서 카탈로그 노출 폭을 줄이지 않았다.

그 차이를 서비스 채택으로 번역하기에는 아직 작고 불확실하다. MF도 943명 중 98명만 맞혀 약 89.6%의 holdout을 놓쳤고, popularity 대비 추가 hit는 19명이다. 단일 seed와 한 번의 분할에서 얻은 평균이므로 사용자 활동량, 아이템 인기, 시간창별로 개선이 유지되는지는 알 수 없다.

Negative sampling은 학습과 평가에서 역할이 다르다

BPR 학습에는 미관측 아이템 sampling을 사용했다. 전체 사용자×아이템 쌍을 매번 비교하지 않고 관측 positive와 sampled unobserved의 순서를 학습하기 위한 절차다. 하지만 이 표본은 실제 dislike가 아니며, uniform sampling만으로 기존 노출 정책이나 popularity 편향이 사라지지도 않는다.

평가에는 negative sampling을 쓰지 않았다. 사용자가 train에서 본 항목을 제외한 전체 카탈로그를 두 모델 모두 순위화했다. 정답에 소수 random negatives만 붙이면 후보 수와 seed에 따라 문제가 쉬워지고, exact metric과 모델의 상대 순위가 달라질 수 있다는 분석이 있다. Rendle의 sampled metric 연구와 달리, Li 등의 후속 연구는 적절한 매핑 아래 sampled hit ratio로 global hit ratio의 승자를 일관되게 예측할 수 있다고 주장한다.

따라서 sampled evaluation을 언제나 무효라고 부를 수는 없다. 이번에는 아이템이 1,682개여서 exact ranking이 가능했고, 논쟁적인 근사를 도입할 이유가 없었다. 카탈로그가 커서 sampling이 불가피하다면 표본 수·분포·seed를 고정하고 exact metric과의 차이를 따로 확인해야 한다.

MF는 온라인 모델이 아니라 다음 검증 후보가 됐다

이번 BPR-MF는 temporal split에서 popularity를 이겼고 coverage도 떨어뜨리지 않았다. 그래서 여러 seed와 기간, 사용자 활동량·아이템 인기별 slice를 확인할 다음 후보로는 남길 수 있다. 여기까지가 이 통제 실험이 지지하는 판단이다.

반대로 random split만 통과하거나, popularity 대비 추가 hit가 반복 실험에서 사라지거나, coverage가 크게 줄어드는 경우에는 모델 복잡도를 늘릴 근거가 없다. 신규 사용자와 신규 아이템도 이번 leave-last-out에 포함되지 않았다. MovieLens 평점 행위를 implicit positive로 바꾼 결과를 클릭률·만족도·매출 개선으로 옮길 수도 없다.

운영 채택 전에는 여러 seed의 변동, 시간창별 재현성, 세그먼트별 누락 비용, 학습·서빙 latency와 갱신 비용을 확인해야 한다. logged feedback은 기존 노출 정책의 결과이므로 마지막 판단은 shadow test와 제한된 온라인 A/B가 맡아야 한다. 미래 누수를 막는 검증 설계를 더 살펴보려면 HuntLab의 walk-forward 검증 글을 확인한다.

참고 링크

비슷한 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다