티스토리 뷰

썸네일이 썩 맘에 들지 않는다

 

질의처리기 글에서 질의를 슬롯과 키워드로 나눴고, 하이브리드 검색 글에서 키워드(BM25)와 벡터를 합쳐 1차 검색을 완성하는 데까지 왔다. 그런데 그 1차 결과를 들여다보다 자꾸 같은 장면에 걸렸다.

 

답 문서가 분명히 후보 안에는 있는데, 1등이 아니다. 넓게 건지는 건 잘 되는데 맨 위에 정답을 올려놓는 건 약했다. 그 마지막 한 끗을 손보는 게 이번 글의 주제, 리랭커(re-ranker)다.

 

이 글은 RAG 구축 고려사항 중 여섯 번째, 리랭커 매듭이다.

1. 왜 리랭커인가 — 넓게 건지기와 1등 맞히기는 다른 일

1차 검색의 일은 "넓게, 빠르게 건지기"다. 후보 안에 정답이 들어 있게만 하면 절반은 성공이다(이걸 recall이라 부른다). 그런데 그 후보들 중 무엇이 진짜 1등인지 가리는 정밀함은 또 다른 능력이다. 1차 검색은 전자엔 강하고 후자엔 약했다.

 

문제는 뒤 단계가 순위에 민감하다는 점이다. 검색 결과를 생성 모델(LLM)에 넘길 땐 보통 상위 몇 개만 넘긴다. 그러면 2~10위에 묻힌 정답은 후보 안에 있어도 사실상 버려진다.

질의:  "지난달 회의 자료"
1차 검색 결과(상위 10):
  1. 다른달 회의록
  2. 회의 안건 메모
  3. 작년 워크숍 자료
  4. ★ 지난달 회의 자료   ← 정답인데 4위
  ...
생성엔 top 3만 넘긴다면 → 정답이 안 들어간다

정답이 후보엔 있는데 순위가 안 맞는 것. 이걸 푸는 게 리랭커다. 1차가 건져온 소수의 후보를 다시 한 번 정밀하게 채점해 순서를 바꾸는 2차 단계. 위 예에서 4위를 1위로 끌어올리면 정답이 살아난다.

2. 크로스인코더가 뭔가 — 바이인코더와 뭐가 다른가

리랭커를 만드는 길은 크게 둘이다.

 

하나는 LLM한테 직접 맡기는 것 — "이 문서들을 질의에 맞게 순위 매겨줘"라고 프롬프트로 시키는 방식이다. 솔직히 이쪽이 더 세다. 순위 정확도 자체가 크로스인코더보다 나을 수 있고, 단순히 순서만 뱉는 게 아니라 "왜 이게 1등인지", "이 문서가 질의에 어떻게 답이 되는지" 같은 부가적인 판단까지 함께 받을 수 있다. 검색 결과를 그냥 정렬하는 걸 넘어, 결과에 대한 설명이나 다른 형태의 응답으로 확장할 여지가 열린다.

 

문제는 딱 하나, 느리다는 것이다. 그런데 이 하나가 치명적이었다. 매 검색마다 LLM을 한 번씩 부르는 셈이라, 밀리초로 끝나야 할 검색에 초 단위가 얹힌다. 서버에 큰 GPU가 있으면 감수해볼 만하지만, 온디바이스에선 감당이 안 됐다.  그래서 성능을 조금 내주더라도, 순위 매기기만 전문으로 하는 작고 빠른 모델 — 크로스인코더(cross-encoder) — 를 어쩔 수 없이 골랐다.

 

이걸 이해하려면 지금까지의 벡터 검색이 쓰던 바이인코더(bi-encoder)와 비교하는 게 빠르다. 바이인코더는 질의는 질의대로, 문서는 문서대로 따로 임베딩해서 각각 벡터로 만든 뒤, 둘의 코사인 유사도로 가까움을 잰다. SBERT 문서 표현으로는 "질의와 문단의 임베딩을 각각 독립적으로 만든다"(SBERT Retrieve & Re-Rank). 핵심은 문서 임베딩을 미리 계산해 인덱싱해둘 수 있다는 것 — 그래서 빠르다.

바이인코더:  질의 → [벡터]
             문서 → [벡터]   (미리 계산해 둠)
             두 벡터의 거리로 유사도

크로스인코더: (질의 + 문서) → 트랜스포머 한 번에 → 관련도 점수 1개

비유하자면 바이인코더는 두 사람한테 각자 자기소개서를 받아 얼마나 겹치는지 비교하는 것이다. 빠르지만, 둘을 직접 만나게 한 적은 없다.

 

크로스인코더는 다르다. 질의와 문서를 한 번에 같이 트랜스포머에 넣어, 두 텍스트의 토큰이 서로 어텐션을 주고받게 한 뒤 "이 문서가 이 질의에 얼마나 맞나"를 점수 하나로 뱉는다. SBERT 표현으로 "질의와 후보 문서를 동시에 트랜스포머에 통과시켜, 관련도를 나타내는 0~1 사이 점수 하나를 출력한다." bge 리랭커 모델 카드도 같은 말을 한다 — "입력 쌍에 대해 full-attention을 수행하므로 임베딩 모델(바이인코더)보다 정확하지만 더 느리다"(BAAI/bge-reranker).

 

비유를 이으면, 크로스인코더는 둘을 한 방에 앉혀놓고 실제로 대화를 시켜 보는 것이다. 훨씬 정확하다. 대신 그 만남을 매번 새로 주선해야 한다. 미리 계산해둘 수가 없다 — 질의가 들어와야 비로소 (질의, 문서)를 같이 통과시키니까.

 

그래서 크로스인코더는 전체 문서를 다 채점하는 데는 못 쓴다. 1차에서 바이인코더(또는 BM25)가 추려준 top-k 후보 소수만 다시 정밀 채점하는 2차 재정렬에 쓴다. SBERT가 정리한 그림 그대로다 — 1차에서 100개쯤 빠르게 회수하고, 2차에서 크로스인코더가 그 후보들의 관련도를 다시 매긴다. 1차는 넓게 회수, 2차는 좁게 정밀, 이 분업이 핵심이다.

3. 얼마나 오르나 — 일반적 기대값과 내 경우

문헌과 벤더 벤치마크는 방향이 한결같다. 리랭커는 임베딩 검색보다 더 정확하다. Pinecone은 "리랭커가 임베딩 모델보다 훨씬 정확하다"고 못 박는데, 이유가 와닿는다 — 바이인코더는 문서의 온갖 의미를 벡터 하나에 압축해 욱여넣지만, 크로스인코더는 원문을 그대로 트랜스포머에 넣어 정보 손실이 적다(Pinecone Rerankers).

 

다만 "몇 점 오른다"는 숫자는 조심해야 한다. 같은 리랭커라도 비교 기준(베이스라인)이 "리랭커 없음"이냐 "다른 리랭커"냐에 따라 향상폭이 천차만별이라, 특정 퍼센트를 일반값처럼 박는 건 과장이 되기 쉽다. 그래서 여기선 단정 수치 대신 방향만 — "유의미하게 오른다"까지만 일반화로 두는 게 정직하다.

 

대신 내 경우를 가볍게 적어둔다. 1차 검색이 약했던 구간 — 특히 오타가 섞이거나 표현이 모호한 질의 — 에서 top1 정답률이 대략 10%포인트 안팎 올랐다. 반대로 1차가 이미 잘 맞히던 강한 구간에선 거의 움직이지 않았다. 이 "약한 데서 효과, 강한 데선 미미"라는 패턴이 — 미리 말하자면 — 이 글 마지막의 결론으로 그대로 이어진다.

4. 공짜가 아니다 — 온디바이스에서 더 따져야 하는 것

여기까지면 "그럼 항상 켜면 되잖아"인데, 그렇지 않았다. 특히 온디바이스에선.

 

메모리. 서버라면 리랭커 모델 하나쯤 더 띄우는 게 큰일이 아니다. 그런데 온디바이스에선 1차 검색기·임베딩 모델·앱이 같은 RAM을 나눠 쓴다. 리랭커 모델을 통째로 올리는 것 자체가 다른 몫을 깎아먹는다. 그래서 모델을 양자화로 작게 줄여 겨우 예산에 맞추는 식의 타협이 처음부터 깔린다.

 

지연. 크로스인코더는 후보 쌍마다 매번 트랜스포머 추론을 돌린다. 이 비용을 GPU 서버로 떠넘길 수 없으니 기기가 그대로 떠안는다. 1차 검색이 밀리초로 끝나는데 리랭커가 거기에 초 단위를 얹으면 체감이 확 나빠진다. 이게 왜 전수에 못 쓰는지를 극적으로 보여주는 예가 있다 — Pinecone는 "4천만 건을 작은 리랭킹 모델로 V100 GPU에서 전부 돌리면 쿼리 하나에 50시간 넘게 기다려야 한다"고 적는다. 서버 GPU로도 그런데 기기에선 말할 것도 없다. 그래서 재정렬 대상 top-k를 작게 잡는 건 선택이 아니라 필수다.

그래서 설계가 죄다 '아껴 쓰는' 쪽으로 갔다. 재정렬은 소수의 후보로만 제한하고, 항상 켜지 않고 1차가 약한 구간에만 선택적으로 켜고, 순위를 바꿀 때도 확실할 때만 바꾼다.

 

정리하면 질문 자체가 바뀐다. 서버에선 "정확도가 좀 오르면 켤까?"였다면, 온디바이스에선 "이 메모리와 이 지연을 내주고도 그만큼 값어치가 있나?"가 된다. 리랭커를 쓸지 말지가 정확도만의 문제가 아니라, 한정된 자원 예산을 어디에 쓸지의 문제가 되는 것이다.

5. 개악을 막는 안전장치 — 확실할 때만 재정렬

리랭커를 켜고 처음 본 건, 좋아지기만 하는 게 아니라는 거였다. 멀쩡하게 1등이던 정답을 리랭커가 끌어내리는 "개악"도 같이 생겼다. 평균 점수는 올라도 어떤 질의는 오히려 더 나빠지는 것. 그래서 둔 게 일종의 문턱이었다. 리랭커가 매긴 점수 차가 충분히 확실할 때만 재정렬을 반영하고, 애매하면 1차 결과의 순서를 그대로 존중한다. 어설프게 끼어들어 평균을 갉아먹느니, 확신이 있을 때만 손대는 쪽을 택한 것이다.

 

이 "확실하지 않으면 손대지 않는다"는 태도는 질의처리기 글에서 "확신 없으면 하드 필터로 안 걸고 검색어로 남긴다"던 그 보수성과 정확히 같다. 자동화가 전체 평균을 올리되 누군가를 망치지 않게 하는, 같은 철학의 다른 적용이었다.

마치며

리랭커는 검색에 무조건 꽂으면 좋아지는 만능 부스터가 아니었다. 1차 검색이 약한 자리를 메우는 보정 장치에 가까웠다. 1차(키워드+벡터)가 이미 강한 구간에선 지연과 메모리를 내준 만큼의 이득이 안 났고, 약한 구간에선 확실히 제 값을 했다.

 

그래서 리랭커의 진짜 질문은 "성능이 오르나?"가 아니라 "어디에, 어떤 비용으로 쓰이나?"였다. 특히 온디바이스처럼 자원이 빠듯한 자리에선 더 그랬다. 화려한 한 방이 아니라 쓸 곳을 고르는 판단 — 돌아보면 이 시리즈 내내 반복된 결론이, 리랭커에서도 똑같이 나왔다. 

 

RAG 개선기의 마지막 글이다. 다음 글에는 총 정리를 해보면서 느낀점들을 정리해 볼 것 같다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/07   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함