리랭커
더 많은 작업
- Reranker; 리랭커
- 1차 검색이 가져온 후보 문서들을 질의와 함께 더 정밀한 모델로 다시 채점해 순서를 바꾸는 모델
리랭커(reranker)는 검색을 두 단계로 나눌 때 두 번째 단계를 맡는 모델이다. 1차 검색(BM25, 밀집 벡터 검색, 하이브리드 검색)은 수백만 문서에서 빠르게 후보 수십~수백 개를 뽑고, 리랭커는 그 후보만 대상으로 질의와 문서를 함께 보며 관련도를 다시 계산해 상위 몇 개를 고른다. 재순위화(reranking) 모델이라고도 한다. RAG에서는 대형 언어 모델에 넣을 문맥의 질을 좌우하는 단계다.
빠르지만 거친 모델로 넓게 거르고, 느리지만 정확한 모델로 좁게 고르는 구조는 고전 검색 엔진의 학습 기반 순위화(learning to rank)부터 이어져 왔다. 이 문서는 신경망 리랭커의 세 가지 구조(크로스 인코더, 후기 상호작용, LLM 리랭커)와 학습, 파이프라인에서의 배치, 코드를 다룬다.
| 구조 | 계산 | 문서 표현 미리 계산 | 질의당 비용 | 정확도 | 주 용도 |
|---|---|---|---|---|---|
| 바이 인코더(bi-encoder) | 질의와 문서를 따로 인코딩해 벡터 하나씩 만든 뒤 내적 | 가능(벡터 하나) | 질의 인코딩 1회 + 근사 최근접 이웃 탐색 | 가장 낮음 | 1차 검색 |
| 후기 상호작용(late interaction, ColBERT) | 따로 인코딩하되 토큰마다 벡터를 남기고, 점수 계산 때 토큰끼리 비교 | 가능(토큰 수만큼 벡터) | 질의 인코딩 1회 + 토큰 단위 유사도 계산 | 중간 | 1차 검색 또는 리랭커 |
| 크로스 인코더(cross-encoder) | 질의와 문서를 한 입력으로 이어 붙여 트랜스포머가 함께 읽음 | 불가 | 후보 수만큼 모델 실행 | 높음 | 리랭커 |
| LLM 리랭커 | 생성형 언어 모델이 관련도를 판정하거나 순서를 출력 | 불가 | 후보 수만큼 또는 창 단위로 LLM 호출 | 높음(모델에 따라) | 리랭커, 리랭커 학습용 교사 |
크로스 인코더가 정확한 이유는 어텐션이 질의의 모든 토큰과 문서의 모든 토큰을 처음 층부터 서로 보게 하기 때문이다. 바이 인코더는 문서를 질의를 모르는 채로 벡터 하나에 압축해야 하므로 "파리 여행 명소"와 "파리의 역사"처럼 주제는 같지만 질문 의도가 다른 문서를 잘 가르지 못한다. 대신 크로스 인코더는 문서 표현을 미리 계산해 둘 수 없어 전체 문서에 쓸 수 없다. BEIR 벤치마크에서도 재순위화와 후기 상호작용 모델이 제로샷 성능은 가장 좋았지만 계산 비용이 높았다.[1]
국내 문서에서는 bi-encoder를 "바이 인코더", cross-encoder를 "크로스 인코더" 또는 "교차 인코더", late interaction을 "후기 상호작용"이나 "지연 상호작용"으로 옮겨 쓴다.
입력은 [CLS] 질의 [SEP] 문서 [SEP]이고, [CLS] 위치(또는 풀링한) 표현에 선형 층을 얹어 관련도 점수 하나를 낸다.
2019년 노게이라(Nogueira)와 조경현은 BERT를 이렇게 문단 재순위화에 쓴 단순한 재구현으로 MS MARCO 문단 검색 리더보드 1위에 올랐고, 이전 최고 기록보다 MRR@10을 상대적으로 27% 높였다.[2] 이후 이런 구조가 신경망 리랭커의 기본형이 되었다.
- 점별(pointwise): (질의, 문서, 관련 여부)로 이진 교차 엔트로피를 쓴다. 가장 단순하다.
- 쌍별(pairwise): 관련 문서 점수가 비관련 문서보다 높게 되도록 순위 손실(RankNet 등)을 쓴다.
- 목록별(listwise): 질의 하나에 대해 양성 1개와 음성 여러 개를 묶어 소프트맥스 교차 엔트로피를 쓴다. 바이 인코더의 대조 학습 손실과 같은 모양이다.
음성 예제로는 1차 검색기가 상위로 올렸지만 정답이 아닌 하드 네거티브를 쓴다. 리랭커가 실제로 마주할 후보 분포가 바로 그것이기 때문이다. 반대로 잘 학습한 크로스 인코더는 바이 인코더나 ColBERT를 학습시키는 교사로 쓰인다(지식 증류). ColBERTv2는 2,200만 매개변수의 MiniLM 크로스 인코더 점수로 증류 학습했다.[3]
sentence-transformers가 공개한 MS MARCO용 크로스 인코더의 표에서, 6층 MiniLM(cross-encoder/ms-marco-MiniLM-L6-v2)은 TREC DL 2019 nDCG@10 74.30, MS MARCO 개발 세트 MRR@10 39.01에 V100 GPU에서 초당 약 1,800개 문서를 처리했고, 12층 판은 정확도가 거의 같으면서 초당 960개였다.[4] 후보 100개를 재순위화하면 질의당 100번의 순전파가 필요하므로, 모델 크기와 후보 수가 곧 지연 시간이다. 다국어용으로는 BGE-M3 기반의 bge-reranker-v2-m3, 젬마 2B 기반의 bge-reranker-v2-gemma 같은 모델이 공개되어 있다.[5]
ColBERT(2020)는 질의와 문서를 BERT로 따로 인코딩하되 토큰마다 128차원 벡터를 남긴다. 점수는 질의 토큰마다 문서 토큰 중 가장 비슷한 것과의 유사도를 구해 더한 값이다(MaxSim).[6]
질의는 [Q] 표지를 붙이고 [mask] 토큰으로 32개 길이까지 채운다(질의 확장 효과). 문서 쪽 벡터를 미리 계산해 둘 수 있으므로 크로스 인코더보다 훨씬 싸다. 논문은 기존 BERT 기반 모델과 비슷한 효과를 내면서 재순위화에서 170배 이상 빠르고 FLOPs는 약 14,000분의 1이라고 보고했다.[6] 문제는 저장 공간이다. 문서의 토큰 수만큼 벡터를 저장해야 한다. ColBERTv2는 벡터를 가장 가까운 중심점 번호와 1~2비트 잔차로 압축해 벡터당 20 또는 36바이트로 줄였고(원래 ColBERT는 16비트 정밀도 256바이트), 공간을 6~10배 줄이면서 품질을 높였다.[3]
ColBERT는 후보 수백 개를 다시 채점하는 리랭커로도, 토큰 벡터 색인에서 직접 찾는 1차 검색기로도 쓸 수 있다. BGE-M3는 다중 벡터(ColBERT식) 점수를 밀집·희소 점수와 함께 내는 모델이다.[7]
- 점별 판정: "이 문서가 질문에 답하는가?"를 묻고 "예" 토큰의 확률을 점수로 쓴다. Qwen3-Reranker(0.6B, 4B, 8B, 32K 토큰 문맥)는 질의, 문서, 지시문을 주고 "yes"와 "no" 두 토큰의 확률로 점수를 매기며, 지시문을 넣으면 대부분의 과제에서 1~5% 좋아진다고 밝혔다.[8]
- 목록 출력: 후보 여러 개에 번호를 붙여 넣고 "관련도 순서대로 번호를 나열하라"고 시킨다. 2023년 RankGPT 연구는 지시를 잘 준 ChatGPT와 GPT-4가 지도 학습 리랭커에 견줄 만하거나 앞선다는 것을 보였다. 한 번에 넣을 수 있는 후보 수가 제한되므로 창 크기 20, 이동 폭 10의 슬라이딩 창으로 뒤에서부터 앞으로 정렬해 올라온다. 또 ChatGPT의 순위를 작은 모델에 증류해, 4억 4천만 매개변수 모델이 30억 매개변수 지도 학습 모델보다 BEIR에서 나았다.[9]
- 쌍별 비교: 두 문서 중 어느 쪽이 나은지 묻는다. 정확하지만 비교 횟수가 많다.
LLM 리랭커는 지시문으로 기준("최신 문서 우선", "공식 문서 우선")을 바꿀 수 있지만, 비용과 지연이 크고 출력 형식이 깨질 수 있다. 그래서 작은 크로스 인코더로 먼저 줄인 뒤 최상위 몇 개만 LLM으로 보거나, LLM의 판정을 작은 리랭커 학습 데이터로 쓰는 방식이 쓰인다.
- 1차 검색: BM25와 밀집 검색(또는 하이브리드)으로 50~200개 후보를 뽑는다. 이 단계의 재현율이 상한이다. 1차에서 놓친 문서는 리랭커가 살릴 수 없다.
- 재순위화: 리랭커로 후보를 채점해 상위 3~10개를 고른다. 점수 임계값을 두어 관련 없는 문서는 아예 넘기지 않을 수도 있다.
- 생성: 고른 문서를 프롬프트에 넣는다. 문맥이 짧고 깨끗할수록 환각과 비용이 준다.
RAG (인공지능) 문서의 예처럼 벡터 유사도가 비슷하게 본 "파리의 역사"와 "에펠탑 관광 정보" 가운데 질문 의도에 맞는 쪽을 위로 올리는 것이 리랭커의 몫이다.
설계할 때 고려할 점은 다음과 같다.
- 후보 수 대 지연: 크로스 인코더 지연은 후보 수에 비례한다. GPU 배치 처리, 문서 길이 자르기(max_length), 작은 모델로 1차 재순위화 후 큰 모델로 2차 재순위화하는 다단 구조로 맞춘다.
- 입력 길이: 크로스 인코더는 질의와 문서를 합친 길이가 모델 최대 길이를 넘으면 잘린다. 청크 크기를 리랭커 최대 길이에 맞춘다.
- 점수 해석: 출력은 보통 로짓이라 범위가 정해져 있지 않다. FlagEmbedding의
compute_score(..., normalize=True)처럼 시그모이드로 0~1로 바꿀 수 있지만,[5] 이 값도 보정된 확률은 아니므로 임계값은 자기 데이터로 정한다. - 언어: 영어 MS MARCO로만 학습한 리랭커는 한국어 문서에서 성능이 낮을 수 있다. 다국어 리랭커를 쓰거나 한국어 질의-문서 쌍으로 미세 조정하고, 반드시 한국어 평가 세트로 효과를 확인한다.
sentence-transformers(6.1.0 기준)의 CrossEncoder는 쌍 목록을 받아 점수 배열을 내는 predict와, 질의와 문서 목록을 받아 corpus_id, score를 관련도 순으로 돌려주는 rank를 제공한다.[10]
from sentence_transformers import CrossEncoder
model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2")
scores = model.predict([
("How many people live in Berlin?", "Berlin had a population of 3,520,031 registered inhabitants."),
("How many people live in Berlin?", "Berlin is well known for its museums."),
])
print(scores) # 모델 카드 예: [ 8.607138 -4.320078 ]
docs = ["Berlin is well known for its museums.", "Berlin had a population of 3,520,031 registered inhabitants."]
for hit in model.rank("How many people live in Berlin?", docs, top_k=2):
print(hit["corpus_id"], hit["score"]) # 관련도 높은 순으로 정렬된 결과
다국어 리랭커 bge-reranker-v2-m3는 모델 카드에서 FlagEmbedding 패키지 사용법을 안내한다.[5]
from FlagEmbedding import FlagReranker
reranker = FlagReranker("BAAI/bge-reranker-v2-m3", use_fp16=True)
query = "연차를 반차로 나눠 쓸 수 있나요?"
candidates = ["연차는 0.5일 단위로 신청할 수 있다.", "연차 수당은 퇴직 시 정산한다.", "회의실 예약 방법"]
scores = reranker.compute_score([(query, c) for c in candidates], normalize=True) # 시그모이드로 0~1
ranked = sorted(zip(scores, candidates), reverse=True)
ColBERT식 MaxSim 점수는 토큰 벡터만 있으면 몇 줄로 계산된다.
import torch
import torch.nn.functional as F
def maxsim(Q, D, d_mask):
"""Q: (Lq, m) 질의 토큰 벡터, D: (B, Ld, m) 후보 문서 토큰 벡터, d_mask: (B, Ld) 패딩 표시"""
Q, D = F.normalize(Q, dim=-1), F.normalize(D, dim=-1)
sim = torch.einsum("qm,bdm->bqd", Q, D) # (B, Lq, Ld)
sim = sim.masked_fill(~d_mask[:, None, :], float("-inf")) # 패딩 토큰 제외
return sim.max(dim=-1).values.sum(dim=-1) # (B,)
리랭커는 nDCG@10, MRR@10처럼 상위 순위의 질을 보는 지표로 평가한다. 1차 검색기의 Recall@100과 함께 봐야 하는데, 리랭커 성능은 1차 검색기가 넘겨준 후보 안에서만 의미가 있기 때문이다. 같은 리랭커라도 1차 후보 수를 20에서 100으로 늘리면 최종 성능과 지연이 함께 바뀌므로, 후보 수를 바꿔 가며 정확도와 지연의 균형점을 찾는다.
- ↑ Nandan Thakur 외, 〈BEIR: A Heterogenous Benchmark for Zero-shot Evaluation of Information Retrieval Models〉, NeurIPS 2021. arXiv:2104.08663
- ↑ Rodrigo Nogueira, Kyunghyun Cho, 〈Passage Re-ranking with BERT〉, 2019. arXiv:1901.04085
- ↑ 3.0 3.1 Keshav Santhanam 외, 〈ColBERTv2: Effective and Efficient Retrieval via Lightweight Late Interaction〉, NAACL 2022. arXiv:2112.01488
- ↑ cross-encoder/ms-marco-MiniLM-L6-v2 모델 카드
- ↑ 5.0 5.1 5.2 BAAI/bge-reranker-v2-m3 모델 카드
- ↑ 6.0 6.1 Omar Khattab, Matei Zaharia, 〈ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT〉, SIGIR 2020. arXiv:2004.12832
- ↑ Jianlv Chen 외, 〈M3-Embedding: Multi-Linguality, Multi-Functionality, Multi-Granularity Text Embeddings Through Self-Knowledge Distillation〉, 2024. arXiv:2402.03216
- ↑ Qwen/Qwen3-Reranker-0.6B 모델 카드
- ↑ Weiwei Sun 외, 〈Is ChatGPT Good at Search? Investigating Large Language Models as Re-Ranking Agents〉, EMNLP 2023. arXiv:2304.09542
- ↑ Sentence Transformers 문서, CrossEncoder