article · 2026-07-28

이 작업에 LLM까지 쓸 필요가 있었나

ModernBERT, GLiNER, ColBERT, E5·BGE, SetFit, Cross-Encoder. 각자 한 가지만 하는 여섯 개를 다시 정리했다.

리랭커 하나를 새로 넣으려고 후보를 고르다가 또 같은 이름들을 만났다. ColBERT, cross-encoder, E5, BGE. 작년에도 봤고 재작년에도 본 이름인데 매번 처음 보는 것처럼 비교표를 다시 그린다.

이유를 따져보면 간단하다. 검색이든 분류든 개체명 추출이든 작은 NLP 작업 하나가 생기면 손이 먼저 LLM 쪽으로 간다. 프롬프트 한 줄이면 되고 파인튜닝도 필요 없다. 다만 그 반사가 쌓이면 청구서로 돌아온다. 레이블이 100개 넘게 있고 트래픽이 큰 인텐트 분류 작업을 BERT 계열로 파인튜닝하면 슬롯 F1 96~98%에 추론 3.6ms, 요청 100만 건당 0.20달러가 나온다. 같은 일을 LLM 제로샷으로 돌리면 유연하긴 해도 레이턴시와 비용 둘 다 자릿수가 달라진다. 그러니 리랭커를 고르는 순간마다 "이 작업, 정말 LLM까지 가야 하나"를 다시 묻게 된다.

질문은 하나인데 답은 두 갈래다

여섯 개를 한 줄로 세우려니 자꾸 겹쳐 보였는데, 병원 진료과로 나누듯 갈라놓으니 정리가 됐다. 셋은 같은 과 안에서 1차 진료와 정밀 검사를 나눠 맡는 사이고, 나머지 셋은 아예 다른 과다.

이 여섯 개를 다시 훑으면서 정리한 게 그거다. 전부 "LLM 없이 이 일을 판별 모델 하나로 해결할 수 있는가"에 답하는데, 답하는 방식이 두 갈래로 갈린다.

한 갈래는 검색을 어떻게 저울질할 것인가에 대한 답이다. E5·BGE, ColBERT, Cross-Encoder가 여기 속한다. 셋 다 같은 문제, 질의와 문서가 얼마나 관련 있는지를 계산하는데 정확도와 속도를 맞바꾸는 지점이 다르다.

다른 갈래는 각자 하나의 작업만 맡는다. ModernBERT는 그 위에 올라타는 모델들이 공유하는 백본이고, GLiNER는 개체명 인식, SetFit은 적은 예시로 하는 분류를 맡는다.

검색은 결국 저울질이다

채용으로 치면 셋의 차이가 선명해진다. 이력서를 한 줄 요약으로 줄여 서랍에 정리해두고 훑는 쪽, 요약 대신 경력과 기술을 항목별로 남겨두고 대조하는 쪽, 지원자를 앉혀놓고 면접하는 쪽. 면접이 제일 정확한데 지원자 1,000명을 다 면접할 수는 없고, 무엇보다 면접은 미리 해둘 수가 없다.

Bi-encoder (E5·BGE)Late Interaction (ColBERT)Cross-Encoder
채용으로 치면한 줄 요약만 보고 추리기이력서 항목별로 대조앉혀놓고 면접
인코딩질의·문서 각각 벡터 1개로 압축질의·문서 각각 토큰별 벡터 유지질의+문서를 합쳐서 한 번에 인코딩
사전 인덱싱가능가능불가
정확도낮음중간~높음가장 높음
속도가장 빠름중간가장 느림

E5(Microsoft)와 BGE(BAAI)는 질의와 문서를 각각 벡터 하나로 눌러 담는 bi-encoder다. 미리 인덱싱해두고 코사인 유사도로 후보를 넓게 추릴 수 있어서 1차 검색을 맡는다. E5-mistral-7b-instruct는 MTEB 평균 66.63으로 같은 세대 OpenAI text-embedding-3-large(64.6)보다 높고, BGE는 자체 리랭커(BGE-reranker)까지 한 세트로 내놓으면서 벡터 하나로 압축하는 대가로 뉘앙스를 잃는다는 약점을 스스로 보완해뒀다. (arXiv:2212.03533, arXiv:2309.07597)

그 압축 손실을 메우는 반대쪽 끝에 Cross-Encoder가 있다. 질의와 문서를 이어붙여 하나의 트랜스포머에 통과시키면 모든 토큰이 서로 직접 주고받는다. 2019년 Nogueira와 Cho가 이 방식을 재순위화에 처음 붙였을 때 MS MARCO 성능이 기존 대비 27% 개선됐다(arXiv:1901.04085). 문제는 후보 하나마다 모델을 다시 돌려야 해서 미리 인덱싱해둘 수 없다는 것. 그래서 cross-encoder는 거의 항상 상위 수십~수백 개만 추려서 재순위하는 두 번째 단계로 쓰인다.

ColBERT는 그 둘 사이 어딘가를 잡는다. 질의와 문서를 여전히 따로 인코딩하지만 벡터 하나로 뭉개지 않고 토큰별 벡터를 그대로 들고 있다가, 질의 시점에 각 질의 토큰이 문서에서 가장 비슷한 토큰을 찾아 유사도를 더하는 방식으로 점수를 낸다.

MaxSim(q, d) = Σ_i  max_j (q_i · d_j)

문서 쪽 토큰 벡터는 미리 계산해서 인덱싱해둘 수 있으니 bi-encoder의 속도와 cross-encoder의 정밀도 사이 지점을 잡는 셈이다. 2020년 원 논문에서 이미 같은 BERT cross-encoder 대비 재순위 속도가 175배 차이 났다(arXiv:2004.12832). 그 뒤 나온 ColBERTv2는 잔차 압축으로 인덱스 크기를 6~10배 줄이면서 MS MARCO MRR@10 39.7을 냈다(arXiv:2112.01488).

그래서 표준 조합은 이렇게 굳어진다. E5나 BGE로 후보를 수백 개까지 좁히고, ColBERT나 cross-encoder로 상위 몇십 개를 다시 정렬한다. BAAI가 아예 BGE와 BGE-reranker를 한 세트로 배포한 것도 이 2단계 구조를 하나의 생태계 안에 넣어버린 결과다.

나머지 셋은 각자 하나만 한다

이쪽 셋은 서로 경쟁하는 사이가 아니다. 건물로 치면 하나는 골조고 나머지 둘은 그 골조 위에 들어가는 설비에 가깝다. 골조를 새로 올리면 설비 성능도 같이 올라간다.

ModernBERT는 위에서 다룬 모델들이 얹히는 백본이다. BERT와 RoBERTa가 2018~2019년에 멈춘 채로 5년 넘게 버텼는데, 그사이 LLM 쪽에서 나온 기법(RoPE, GeGLU, Flash Attention)을 인코더에 그대로 옮겨 담았다. 컨텍스트는 512토큰에서 8,192토큰으로 늘었고 GLUE 평균 88.4로 DeBERTaV3(88.1)를 처음 앞질렀다(arXiv:2412.13663). 이게 왜 중요하냐면, 위에서 다룬 모델 상당수가 결국 이 백본 위에서 다시 학습되기 때문이다. GLiNER도 이미 ModernBERT 버전이 나왔다.

GLiNER는 개체명 인식만 한다. 다만 엔티티 유형을 미리 정해두고 재학습하는 방식이 아니라 "이런 유형을 찾아줘"라고 텍스트로 지정하면 그 자리에서 제로샷으로 스팬을 뽑는다.

"a generalist model for NER... capable of identifying any entity type"

(GLiNER 원 논문)

논문 벤치마크에서 가장 작은 50M짜리 버전조차 도메인 밖 데이터 F1 52.7로 ChatGPT(47.5)를 넘겼고, 가장 큰 버전은 60.9까지 올라간다.

SetFit은 이름과 달리 새 모델이 아니라 학습법 하나를 가리킨다. Sentence Transformer를 클래스당 예시 8~16개만으로 대조 학습시키고 그 위에 로지스틱 회귀 하나를 얹는다. 프롬프트도 검증기(verbalizer)도 필요 없다. RAFT 벤치마크에서 355M짜리 SetFit이 71.3%를 냈고 175B GPT-3 few-shot은 62.7%에 그쳤다(arXiv:2209.11055). V100 한 대로 학습 시간은 30초, 비용은 2.5센트다.

다음에 리랭커를 다시 고를 때

자전거를 직접 조립해본 사람은 안다. 부품을 좋은 걸로 하나씩 갈아 끼우면 성능은 올라가는데, 어느 순간부터 정비할 게 같이 늘어난다.

여섯 개를 다시 정리하면서 든 생각은 LLM이 틀렸다는 게 아니다. 레이블이 아예 없고 하루 만에 프로토타입을 띄워야 할 때는 여전히 LLM이 맞는 선택이다. 다만 그 작업이 안정되고 트래픽이 쌓이기 시작하면 어느 시점부터는 이 여섯 개 중 하나로 갈아탈 계산이 나온다.

정작 남는 고민은 정확도나 비용이 아니다. 검색 파이프라인 하나에 bi-encoder, 리랭커, 필요하면 ColBERT까지 붙이고 나면 관리할 모델이 서너 개로 늘어난다. LLM 호출 하나로 끝날 일을 조각내는 대가를, 다음번에는 이 비교표를 그리기 전에 먼저 계산해봐야 할 것 같다.

참고