본문 바로가기

프로젝트/Techfork

추천 품질을 4단계 실험으로 개선한 과정 — 임베딩 가중치부터 MMR Lambda까지

들어가며

TechFork는 기술 블로그 글을 수집해서 검색과 추천을 제공하는 서비스입니다. 추천은 사용자 프로필 벡터 조회 → kNN + BM25 병렬 검색 → RRF 후보 결합 → MMR 최종 랭킹이라는 파이프라인으로 동작하는데, 이 구조에서 "제목/요약/본문 임베딩에 각각 얼마만큼의 가중치를 줄 것인가", "kNN 탐색 범위는 어느 정도가 적당한가", "MMR의 관련성-다양성 균형은 어디에 잡아야 하는가" 같은 질문에는 이론만으로 답하기 어렵습니다.

 

그래서 holdout 기반 Ground Truth를 구축하고, Recall@K, nDCG@K, ILD를 측정하는 평가 프레임워크를 만든 뒤, 총 4단계에 걸쳐 설정을 실험적으로 좁혀갔습니다. 이 글은 각 단계에서 무엇을 바꿨고, 수치가 어떻게 나왔고, 왜 그 결론을 내렸는지를 정리한 실험 이력입니다.

평가 지표 정의와 Ground Truth 구축 과정, fixture 기반 평가 환경 구성은 추후 추천 평가 프레임워크 글을 참고해주세요.

추천 파이프라인의 전체 구현 구조는 별도의 추천 구현 구조 정리 글에서 다룹니다.

 


실험 흐름 개요

 


실험 환경

  • Ground Truth: 사용자 15명, 사용자별 읽은 글 80개(프로필 구성용) + holdout 30개(정답 세트)
  • 평가 지표: nDCG@4, nDCG@8, Recall@4, Recall@8, ILD(Intra-List Diversity), Composite Score, 평균 응답 시간(ms)
    • K=4는 UI에서 첫 번째 줄에 표시되는 추천 수, K=8은 첫 화면에 보이는 추천 수입니다. 임의의 숫자가 아니라 실제 사용자가 보는 단위에 맞춰 설정했습니다.
    • Composite Score = Recall@8 × 0.4 + nDCG@8 × 0.4 + ILD × 0.2 로, 정확성과 다양성을 균형 있게 반영합니다.
  • 기반 스택: Elasticsearch 8.x, Spring Boot 3.x
  • 인프라: Oracle Cloud ARM 인스턴스 (4코어, 24GB RAM)

 


Phase 1. 임베딩 가중치 방향 탐색

목적

추천 품질에 가장 큰 영향을 주는 임베딩 필드가 무엇인지 방향을 잡는 단계입니다. 사용자 프로필 벡터와 게시글을 매칭할 때, 제목/요약/본문 임베딩에 각각 얼마만큼의 가중치를 줘야 하는지를 탐색합니다. 제목 중심, 요약 중심, 컨텐츠 중심, 쌍 조합, 균등 가중치, 본문 제외까지 총 7개 시나리오를 비교했습니다.

 

시나리오 구성

# 시나리오 제목 요약 본문
1 균등 가중치 0.33 0.33 0.34
2 제목 중심 0.60 0.20 0.20
3 요약 중심 0.20 0.60 0.20
4 컨텐츠 중심 0.20 0.20 0.60
5 기존 기본값 0.40 0.40 0.20
6 제목+요약 중심 0.45 0.45 0.10
7 제목+요약만 0.50 0.50 0.00

 

결과

시나리오 nDCG@8 Recall@8
1. 균등 가중치 0.2777 0.0911
2. 제목 중심 0.2934 0.1000
3. 요약 중심 0.2319 0.0689
4. 컨텐츠 중심 0.2685 0.0822
5. 기존 기본값 0.2892 0.0911
6. 제목+요약 중심 0.2888 0.0911
7. 제목+요약만 0.2810 0.0867

 

판단

제목 중심(시나리오 2)이 nDCG@8, Recall@8 모두에서 가장 높은 값을 보였습니다. 기존 기본값(0.40/0.40/0.20) 대비 nDCG 약 1.4% 향상입니다.

 

흥미로운 점은 검색에서는 Summary 중심이 최적이었던 반면, 추천에서는 제목 중심이 최적이라는 것입니다. 검색은 사용자가 입력한 쿼리와 문서를 매칭하는 작업이라 요약의 압축된 정보가 유리하지만, 추천은 사용자 프로필 벡터와 게시글 벡터 간의 의미적 유사도를 비교하는 작업입니다. 제목은 글의 핵심 주제를 한 문장으로 압축하기 때문에, 프로필 벡터와의 코사인 유사도 계산에서 더 강한 매칭 신호로 작용한 것으로 보입니다.

 

요약 중심 설정이 가장 낮은 성능을 보인 점도 이를 뒷받침합니다. LLM이 생성한 요약은 원문의 다양한 측면을 포함하기 때문에, 벡터 공간에서 특정 주제로의 방향성이 제목보다 분산됩니다.

 

이 단계의 결론은 탐색 방향을 제목 중심으로 좁히는 것이었습니다.

 


Phase 2. kNN 탐색 공간 크기 최적화

목적

Phase 1에서 찾은 임베딩 가중치를 고정한 상태에서, kNN 검색의 탐색 범위를 조정합니다. knnSearchSize는 최종적으로 가져올 후보 수, numCandidates는 HNSW 그래프에서 탐색할 노드 수를 의미합니다. 탐색 범위를 넓히면 이론적으로 더 좋은 후보를 찾을 수 있지만, latency 비용이 따릅니다. 이 trade-off의 실제 양상을 확인하는 단계입니다.

 

결과

시나리오 knnSearchSize / numCandidates nDCG@8 Recall@8 Latency (ms)
1. 소형 30 / 90 0.2318 0.0800 476.87
2. 중간-하 40 / 120 0.2618 0.0867 549.73
3. 현재 50 / 150 0.2655 0.0867 618.47
4. 중간 60 / 180 0.2634 0.0844 767.20
5. 중간-상 70 / 210 0.2825 0.0978 928.87

 

판단

수치만 놓고 보면 70/210이 품질은 가장 높습니다. 그러나 응답 속도가 928ms로 1초에 육박합니다. 50/150 대비 latency가 50% 증가하면서 nDCG@8은 0.017 차이입니다.

 

현재 TechFork가 단일 ARM 인스턴스(4코어, 24GB)에서 Elasticsearch를 포함한 전체 스택을 운영하고 있다는 점을 감안하면, 이 latency 증가는 부하 상황에서 더 크게 작용합니다. 실제로 k6 부하 테스트에서 ES가 이미 약 3.4/4 코어를 사용하고 있어 탐색 범위를 늘리면 thread starvation 위험이 커집니다.

 

결론적으로 50/150을 유지했습니다. 품질과 속도의 균형점이면서, 인프라 제약을 반영한 실용적인 선택입니다.

 


Phase 3. MMR 후보군 크기 최적화

목적

Phase 2까지는 kNN + BM25의 1차 후보군을 다뤘습니다. Phase 3부터는 MMR(Maximal Marginal Relevance)이 본격적으로 개입합니다. MMR은 관련성과 다양성을 동시에 고려해서 최종 추천 목록을 구성하는 재랭킹 알고리즘입니다.

 

MMR은 후보군 내 모든 아이템 쌍의 유사도를 비교하기 때문에 O(n²) 성격을 갖습니다. 후보군이 클수록 다양성은 좋아질 수 있지만 비용도 증가합니다. 이 단계에서는 후보군 크기를 40~100 범위에서 조절하면서 정확도, 다양성, 속도의 균형점을 찾습니다.

 

결과

시나리오 후보 수 nDCG@8 Recall@8 ILD Latency (ms)
1. 소형 40 0.2721 0.0889 0.6376 866.27
2. 최종 선택 60 0.2790 0.0933 0.6530 853.80
3. 현재 80 0.2692 0.0889 0.6633 932.07
4. 대형 100 0.2692 0.0889 0.6657 944.60

 

판단

흥미로운 패턴이 있습니다. 후보 수를 80개, 100개로 늘리면 ILD(다양성)는 소폭 올라가지만(0.6633 → 0.6657), 정확도(nDCG, Recall)는 오히려 60개일 때가 가장 높습니다. 응답 속도도 60개가 853ms로 가장 빠릅니다.

 

이는 후보군이 너무 커지면 MMR이 관련성 낮은 아이템까지 다양성을 위해 끌어오면서, 오히려 정확도가 떨어지는 현상으로 해석할 수 있습니다. 검색 평가의 Phase 4에서 kNN 탐색 범위를 넓혔을 때 노이즈 유입으로 품질이 하락한 것과 같은 맥락입니다.

 

결론적으로 후보 60개를 채택했습니다. 정확성과 다양성 모두에서 균형점을 보여줬고, 기존 80개 대비 latency도 약 8% 개선됩니다.

 


Phase 4. MMR Lambda 최적화

목적

마지막 단계에서는 MMR의 lambda 파라미터를 튜닝합니다. Lambda는 관련성과 다양성 사이의 비중을 직접 조절하는 값입니다. 1.0에 가까울수록 관련성 중심, 낮을수록 다양성 중심으로 동작합니다. 앞선 3단계에서 찾은 최적값들을 고정해놓고, lambda만 0.80에서 1.00까지 변화시키며 비교합니다.

 

결과

시나리오 Lambda nDCG@8 Recall@8 ILD Latency (ms)
1 0.80 0.1714 0.0533 0.6936 972.0
2 0.85 0.1887 0.0622 0.6897 848.87
3 0.90 0.2212 0.0711 0.6822 836.93
4 0.93 0.2618 0.0867 0.6631 813.2
5 0.95 0.2816 0.0933 0.6506 808.6
6 0.97 0.2733 0.0911 0.6375 799.8
7 1.00 0.2582 0.0889 0.6136 828.6

 

판단

Lambda 0.80~0.90 구간에서는 다양성(ILD)은 0.68~0.69로 높지만, 정확도가 크게 떨어집니다. nDCG@8이 0.17~0.22 수준으로, 추천 목록의 상위 아이템이 정답과 동떨어져 있다는 의미입니다.

 

반대로 lambda 1.00에서는 다양성이 0.6136으로 가장 낮으면서도 정확도(nDCG 0.2582)조차 0.95보다 못합니다. 이것이 이 실험에서 가장 흥미로운 발견입니다. MMR의 다양성 보정을 완전히 제거(λ=1.0)하면 오히려 정확도도 떨어진다는 것은, 약간의 다양성 보정이 유사한 아이템끼리의 순위 경쟁을 완화하면서 실질적으로 더 넓은 관심사 영역을 커버하는 효과를 내기 때문으로 해석할 수 있습니다.

 

Lambda 0.95가 nDCG@8(0.2816), Recall@8(0.0933) 모두 최고 성능을 기록하면서, ILD(0.6506)도 적정 수준을 유지했습니다. 응답 속도 역시 808ms로 안정적입니다.

 


최종 설정 요약

4단계 실험을 거쳐 정해진 현재 추천 설정은 다음과 같습니다.

항목 결정 단계
titleEmbeddingWeight 0.60 Phase 1
summaryEmbeddingWeight 0.20 Phase 1
contentEmbeddingWeight 0.20 Phase 1
knnSearchSize 50 Phase 2
numCandidates 150 Phase 2
mmrCandidateSize 60 Phase 3
mmrLambda 0.95 Phase 4

 


돌아보며

실험을 진행하면서 몇 가지 교훈이 남았습니다.

 

첫째, 검색과 추천은 최적 설정이 다릅니다. 같은 필드 가중치 탐색이라도 검색에서는 Summary 중심(0.70)이, 추천에서는 제목 중심(0.60)이 최적이었습니다. 쿼리-문서 매칭과 프로필-문서 유사도 비교는 근본적으로 다른 작업이기 때문에, 검색에서 검증된 설정을 추천에 그대로 옮기는 것은 적절하지 않습니다.

 

둘째, "더 많이 탐색하면 더 좋다"는 직관은 틀릴 수 있습니다. Phase 2에서 kNN 탐색 범위를 넓히면 당연히 더 좋은 후보를 찾을 것이라 예상할 수 있지만, 실제로는 현재 인덱스 규모(약 5,800건)에서는 노이즈 유입 효과가 더 컸습니다. Phase 3의 MMR 후보군에서도 같은 패턴이 나타났습니다. 이런 현상은 실측 없이는 발견할 수 없습니다.

 

셋째, 다양성은 정확도의 적이 아닙니다. Phase 4에서 lambda 1.0(다양성 보정 없음)이 오히려 0.95보다 정확도가 낮았다는 결과는, 적절한 수준의 다양성 보정이 추천 품질 자체를 개선할 수 있음을 보여줍니다.

현재 설정은 약 5,800건 규모의 인덱스에서 검증된 값입니다. 인덱스 규모가 크게 달라지거나 사용자 프로필 특성이 변하면 재실험이 필요하겠지만, 실험 프레임워크가 구축되어 있으므로 동일한 절차를 반복할 수 있습니다.