---
title: "M3-Embedding을 읽고 Dense와 Sparse를 왜 합치는지 끝까지 생각해봤다"
slug: "bge-m3-embedding-review"
category: "RAG"
topic: "rag"
subtopic: "retrieval"
tags: ["BGE-M3","BM25","mDPR","mContriever","E5","Dense Retrieval","Learned Sparse","Multi-vector","Hybrid Search","Long Document","Multilingual"]
status: "published"
created: "2026-07-21"
updated: "2026-07-21"
summary: "M3-Embedding 논문의 세 검색 방식을 먼저 따라간 뒤, Hybrid가 강한 이유와 nDCG·Recall의 의미, 논문 결과의 한계, Google·Naver 검색과의 차이를 질문 중심으로 정리했다."
kind: "내 생각 정리"
evidence: "M3-Embedding v5 표 1~6·부록 표 8·11, baseline model card·config, Google·Naver 공식 문서 확인"
---

[M3-Embedding 논문](https://arxiv.org/abs/2402.03216)을 읽었다. 논문에서 모델은 `M3-Embedding`으로 표기하며, 공개 구현에서는 흔히 BGE-M3로 부른다.

처음에는 “Dense, Sparse, Multi-vector를 하나의 model로 모두 지원한다”는 기능 목록에 관심이 갔다. 읽고 나서는 기능의 개수보다 세 점수의 역할을 어떻게 나눴는지, 그리고 benchmark가 어떤 학습 데이터와 candidate budget에서 나온 결과인지가 더 중요하게 보였다.

특히 두 결과가 같이 남았다.

```text
MIRACL에서는 Dense 69.2 > Sparse 53.9, 결합 70.4, 세 방식 결합 71.5
MLDR에서는 Sparse 62.2 > Dense 52.5, 결합 64.8, 세 방식 결합 65.0
```

같은 모델 안에서도 dataset이 바뀌면 Dense와 Sparse의 순서가 뒤집혔다. 결합은 두 dataset에서 모두 개별 방식보다 높았지만, 개선 폭과 계산 경로는 같지 않았다.

## 이 논문은 무엇을 한 논문인가

### 무엇을 만들었나

BGE-M3는 하나의 encoder에서 세 retrieval 기능을 제공하는 multilingual embedding model이다.

- Dense는 문장 또는 문서 전체를 하나의 vector로 표현한다.
- Sparse는 token별 중요도를 학습하고, query와 document에 같이 나타난 token의 weight로 점수를 계산한다.
- Multi-vector는 token vector를 보존하고 late interaction으로 세밀하게 비교한다.

100개가 넘는 언어와 최대 8,192 token 입력을 목표로 해 multilinguality, multi-functionality, multi-granularity의 세 M을 이름에 담았다.

### 어떻게 만들었나

XLM-RoBERTa 계열 encoder를 여러 단계로 pre-training·fine-tuning했다. dense, sparse, multi-vector 점수를 합친 값을 teacher signal로 사용하는 self-knowledge distillation을 적용했고, 길이가 비슷한 sample을 묶고 긴 batch를 다시 나눠 처리하는 batching으로 긴 입력 학습 비용을 줄였다.

### 왜 이렇게 만들었나

Dense는 semantic matching에 강하지만 exact term signal을 잃을 수 있다. Sparse는 lexical matching과 inverted index를 이용할 수 있지만 표현 차이에 약하다. Multi-vector는 token-level interaction이 세밀하지만 비싸다. 논문은 하나를 선택하기보다 한 encoder가 세 표현을 함께 학습하고 필요에 따라 결합하도록 만들었다.

### 결과는 무엇이었나

MIRACL에서는 Dense가 Sparse보다 높았고, MLDR long-document retrieval에서는 Sparse가 Dense보다 약 9.7점 높았다. 두 경우 모두 Dense+Sparse가 각 단일 방식보다 높았다. Multi-vector까지 사용한 `All`이 가장 높은 평균을 냈다.

하지만 이 결과를 “BGE-M3 Hybrid가 모든 검색의 정답”으로 읽으면 안 된다. MIRACL은 fine-tuning data에 포함됐고, MLDR은 논문이 만든 long-document data의 held-out test다. 결합 방식마다 후보 수와 reranking 비용도 다르다.

이 논문의 의미는 benchmark 1위를 하나 추가했다는 데만 있지 않다. 서로 다른 model 세 개를 운영하지 않고도 **하나의 encoder가 동일한 입력에서 세 종류의 검색 표현을 동시에 만들도록 학습했다**는 데 있다. 같은 backbone에서 Dense, learned sparse, Multi-vector를 비교하고 섞을 수 있으므로 architecture와 training data가 다른 모델 세 개를 비교할 때보다 retrieval function 자체의 차이를 관찰하기 쉬워졌다.

반대로 model artifact가 하나라는 말이 검색 시스템도 하나라는 뜻은 아니다. 실제 서비스에서는 dense vector index, sparse inverted index, token vector reranking 경로가 각각 필요하다. 이 논문은 만능 검색 함수 하나를 만든 논문이라기보다, 세 검색 관점을 한 encoder에서 꺼낼 수 있게 만든 논문에 가깝다.

## 무엇이 하나의 모델 안에 들어갔나

### Dense는 전체를 한 vector로 줄인다

query와 passage를 encoder에 넣은 뒤 `[CLS]` hidden state를 정규화해 각각 하나의 vector로 사용한다. 두 vector의 inner product가 Dense score다.

```text
query    → normalized CLS vector ┐
                                  ├─ inner product → dense score
passage  → normalized CLS vector ┘
```

corpus document는 미리 vector로 만들 수 있어 approximate nearest neighbor index로 후보를 빠르게 찾을 수 있다. 대신 긴 문서의 여러 세부 사실을 하나의 vector에 압축해야 한다.

### Sparse는 BM25가 아니다

논문의 lexical 또는 Sparse 기능은 각 token hidden state를 하나의 weight로 변환한다. 같은 token이 여러 번 나오면 최대 weight를 남기고, query와 passage에 함께 존재하는 token weight의 곱을 더해 점수를 만든다.

```text
sparse score = Σ(query와 passage에 함께 있는 token의 query weight × passage weight)
```

겉으로는 inverted index를 사용할 수 있는 lexical retrieval이지만 BM25와 계산 방식이 다르다.

```text
BM25       → term frequency, document frequency, document length를 수식으로 반영
M3 Sparse  → neural encoder가 문맥에 따라 token weight를 학습
```

따라서 논문의 `Sparse 62.2`를 “BM25 62.2”로 읽으면 안 된다. learned sparse가 Dense보다 높았다는 결과다.

### Multi-vector는 모든 문서를 처음부터 비교하지 않았다

Multi-vector는 query와 passage의 token embedding을 보존한다. 각 query token마다 가장 가까운 passage token을 찾고 그 점수를 합치는 ColBERT 계열 late interaction을 사용한다.

세밀하지만 corpus 전체에 바로 적용하기에는 비용이 크다. MIRACL 실험에서는 Dense가 찾은 상위 200개를 Multi-vector로 다시 정렬했다. 즉 `Multi-vec` 결과는 독립적인 1차 검색 index의 결과가 아니라 Dense candidate에 의존하는 reranking 결과다.

이 구분이 중요했다. 표에 Dense, Sparse, Multi-vec가 같은 열로 있어도 실제 실행 단계는 같지 않다.

## 어떻게 세 기능을 같이 학습했나

### Self-knowledge distillation

dense, sparse, multi-vector는 같은 positive를 높이고 negative를 낮추도록 각각 InfoNCE loss를 가진다. 문제는 한 encoder를 공유하면서 서로 원하는 표현이 충돌할 수 있다는 점이다.

논문은 세 relevance score의 가중합을 teacher score로 만들었다. 이 통합 score의 확률 분포를 각 기능이 따라가도록 학습했다. 외부 Cross-Encoder teacher가 아니라 모델 자신의 서로 다른 retrieval 기능을 합친 신호를 teacher로 썼기 때문에 self-knowledge distillation이라고 부른다.

MIRACL ablation에서 distillation의 영향은 다음과 같았다.

| 방식 | distillation 없음 | distillation 사용 |
|---|---:|---:|
| Dense | 68.7 | 69.2 |
| Sparse | 36.7 | 53.9 |
| Multi-vector | 69.3 | 70.5 |

Sparse의 증가 폭이 가장 컸다. 하나의 모델에 head 세 개를 붙인 것만으로 같은 성능이 생긴 것이 아니라, 서로 다른 score를 teacher로 삼은 학습이 특히 Sparse 품질에 크게 관여했다.

### 긴 입력을 위한 batching

긴 sequence를 그대로 섞으면 padding과 activation memory가 커지고 batch size가 작아진다. 논문은 다음 방법을 조합했다.

- 길이가 비슷한 데이터를 같은 group에서 sampling한다.
- 긴 mini-batch를 sub-batch로 나눠 encoding한다.
- gradient checkpointing으로 중간 activation memory를 줄인다.
- 여러 GPU의 embedding을 공유해 in-batch negative 수를 늘린다.

논문의 설정에서 8,192 token 입력의 device당 최대 batch size는 split-batch 없이 6, 적용 후 130이었다. 이 수치는 해당 A800 환경과 구현의 결과다. 모든 환경에서 같은 처리량을 보장하는 수치는 아니지만, 긴 입력 지원이 positional length 하나를 늘리는 일로 끝나지 않는다는 점은 분명했다.

## 내 생각 정리 — 왜 Dense와 Sparse를 합치면 더 좋아질까

### 먼저 Dense와 Sparse가 밀집 검색과 희소 검색인가

맞다. `dense retrieval`을 밀집 검색, `sparse retrieval`을 희소 검색이라고 부른다. 이름은 vector에서 0이 아닌 값이 얼마나 많이 존재하는지에서 왔다.

```text
Dense vector
[0.13, -0.41, 0.08, 0.72, ...]
대부분의 차원이 값을 가짐

Sparse vector
{"베어링": 2.8, "진동": 1.9, "설비-A17": 4.1}
아주 큰 vocabulary 중 일부 term만 값을 가짐
```

다만 **Sparse가 곧 BM25라는 뜻은 아니다.** BM25도 sparse lexical retrieval이지만, M3 Sparse는 neural encoder가 문맥에 따라 token weight를 학습하는 learned sparse다.

```text
희소 검색
├─ BM25              → 사람이 정한 수식으로 term weight 계산
└─ M3 learned sparse → 학습된 model이 token weight 계산
```

### 둘을 합치면 왜 더 좋아질 수 있나

핵심은 두 방식이 잘하는 문제가 다르고, 따라서 틀리는 query도 완전히 같지 않다는 점이다.

예를 들어 사용자가 `설비 A17 베어링 6205 진동`을 검색한다고 해보자.

- Sparse는 `A17`, `6205`, `베어링`처럼 정확히 일치하는 희귀 term을 강하게 잡는다.
- Dense는 본문에 `회전체 축받이에서 떨림이 발생했다`처럼 다른 표현만 있어도 의미가 가깝다고 판단할 수 있다.
- Sparse만 쓰면 동의어와 문장 표현 차이를 놓칠 수 있다.
- Dense만 쓰면 `A17`과 `A71`, `6205`와 `6206`처럼 의미는 비슷하지만 업무상 다른 식별자를 뭉갤 수 있다.

두 후보 집합을 합치면 한쪽이 놓친 정답을 다른 쪽이 후보에 넣을 가능성이 생긴다. 그다음 두 점수를 함께 사용하면 **정확한 term도 맞고 전체 의미도 맞는 문서**를 위로 올릴 수 있다.

```text
Sparse만 찾은 정답 ─┐
                     ├─ 후보 union → 점수 결합 → 최종 순위
Dense만 찾은 정답  ─┘
```

성능 향상의 원인은 두 개로 나눠야 한다.

1. `candidate recall`이 증가한다. 두 검색기의 후보를 합쳐 정답 후보 자체가 늘어난다.
2. `ranking`이 개선된다. 두 신호를 같이 본 점수가 정답을 더 위에 배치한다.

논문의 Dense+Sparse 결과에는 이 두 효과가 섞여 있다. Dense 상위 1,000개와 Sparse 상위 1,000개의 합집합을 다시 정렬했기 때문이다. 단순히 두 숫자를 더해서 좋아졌다고만 설명하면 후보 풀이 넓어진 효과를 놓친다.

### 합치면 항상 좋아지나

아니다. 다음 조건이 있어야 한다.

- 두 방식이 서로 다른 relevant document를 실제로 찾아야 한다.
- 두 score의 scale을 맞추거나 validation data로 weight를 조정해야 한다.
- 약한 검색기의 잘못된 신호가 강한 검색기의 순위를 망치지 않아야 한다.
- 늘어난 latency, memory, index 운영비보다 품질 개선의 가치가 커야 한다.

후보와 오류가 거의 같으면 두 번 검색해도 얻는 것이 없다. Sparse가 특정 query 유형에서 계속 틀리는데 고정 weight로 섞으면 Dense 단독보다 나빠질 수도 있다. Hybrid는 `1 + 1 = 무조건 2`가 아니라, **서로 다른 오류를 가진 두 판단을 잘 보정했을 때 강해지는 ensemble**이다.

retrieval 단계의 통합 score는 다음 구조다.

```text
final score
= w1 × dense score
+ w2 × sparse score
+ w3 × multi-vector score
```

weight는 downstream scenario에 따라 달라진다. 논문도 모든 dataset에 하나의 값을 사용하지 않았다.

실무에서는 score fusion 말고 rank fusion도 비교할 필요가 있다. Dense cosine score와 sparse score는 범위와 분포가 다르기 때문이다. `RRF(Reciprocal Rank Fusion)`처럼 각 검색 결과의 순위만 합치면 score calibration 부담은 줄지만, score가 가진 세밀한 차이는 버리게 된다. 어느 쪽이 낫다는 것도 validation query로 확인해야 한다.

### MIRACL 실행 경로

```text
Dense       → top 1,000 from Faiss
Sparse      → top 1,000 from Lucene
Dense+Sparse→ 두 후보 집합의 union을 1 대 0.3으로 재정렬
All         → Dense top 200을 Dense, Sparse, Multi-vector 순서로 1 대 0.3 대 1로 재정렬
```

여기서 `Dense+Sparse`는 단일 retriever보다 후보 budget이 커질 수 있다. Dense 1,000개와 Sparse 1,000개의 union이기 때문이다. `All`은 Dense top 200만 Multi-vector로 다시 본다. 결과 차이는 score 결합뿐 아니라 candidate source와 candidate count의 차이도 포함한다.

“세 점수를 합쳤더니 71.5”라고만 적으면 실행 구조를 놓치게 된다.

## 비교 대상의 이름부터 다시 풀어봤다

PDF에서 본 `mDPR³`, `mContriever⁴`, `mE5large`, `E5mistral-7b` 표기가 처음에는 model version처럼 보였다. 원문과 각주를 다시 보니 `3`과 `4`는 각각 Hugging Face checkpoint로 연결하는 각주 번호였다. `Zhang et al., 2023`, `Wang et al., 2022`도 model 이름의 일부가 아니라 인용 논문이다.

논문이 비교한 방식은 크게 세 그룹으로 나뉘다.

```text
lexical baseline       → BM25
single-vector Dense    → mDPR, mContriever, mE5-large, E5-Mistral-7B
M3 retrieval functions → Dense, Sparse, Multi-vector, 그 결합
```

### BM25는 학습하지 않는 lexical baseline이다

BM25는 query term이 document에 얼마나 등장하는지, 전체 corpus에서 얼마나 희귀한지, document 길이가 어떤지를 함께 반영한다. embedding model이 아니며 신경망 inference도 필요 없다.

여기서 사용한 BM25는 보통의 Lucene analyzer 조건과 다르다. 저자들은 M3 Sparse와 vocabulary 및 retrieval latency 조건을 맞추기 위해 BM25에도 XLM-RoBERTa tokenizer를 사용했다. 같은 tokenizer를 썼다고 BM25가 Dense로 바뀌는 것은 아니다. text를 term으로 나누는 단계만 맞춘 것이다.

### mDPR은 mBERT 기반의 초기 multilingual Dense baseline이다

[mDPR checkpoint](https://huggingface.co/castorini/mdpr-tied-pft-msmarco)는 query와 passage를 각각 하나의 768차원 vector로 만드는 bi-encoder다. mBERT 규모의 12-layer encoder와 최대 512 token 입력을 사용한다.

checkpoint 이름도 실험 조건을 담고 있다.

```text
tied         → query encoder와 passage encoder가 weight를 공유
pft-msmarco  → MS MARCO Passage로 pre-fine-tuning
```

BGE-M3 논문이 인용한 Zhang et al., 2023은 mDPR을 처음 만든 논문이 아니라 MIRACL의 baseline 구성을 가리킨다. 해당 checkpoint는 mBERT에서 시작해 MS MARCO Passage로 학습한 뒤 MIRACL에서 zero-shot으로 평가됐다.

### mContriever는 multilingual contrastive pre-training을 먼저 했다

[mContriever](https://github.com/facebookresearch/contriever)는 29개 언어의 CCNet data로 비지도 contrastive pre-training을 한 mBERT 기반 retriever다. 문장의 token hidden state를 average pooling해 768차원 단일 vector를 만든다.

다만 논문에서 사용한 [`facebook/mcontriever-msmarco`](https://huggingface.co/facebook/mcontriever-msmarco)는 비지도 pre-training 체크포인트 그대로가 아니다. 영어 MS MARCO로 fine-tuning한 모델이다. “mContriever는 비지도 model”이라는 요약만 남기면 실제 비교 조건에서 supervised fine-tuning을 놓친다.

### mE5-large는 XLM-R large 기반의 multilingual embedding model이다

논문의 `mE5large`는 [`intfloat/multilingual-e5-large`](https://huggingface.co/intfloat/multilingual-e5-large)를 가리킨다. XLM-RoBERTa-large에서 시작한 24-layer encoder로, 최대 512 token을 입력받고 1,024차원 vector를 만든다. multilingual text pair의 weakly supervised contrastive pre-training 후 labeled retrieval data로 fine-tuning했다.

이 model은 입력 형식도 실험 조건의 일부다.

```text
query: 사용자 질문
passage: 검색 대상 문서
```

공식 model card는 비대칭 retrieval에서 `query:`와 `passage:` prefix를 빼면 성능이 저하된다고 명시한다. BGE-M3 논문의 `Wang et al., 2022`는 최초 E5 논문을 가리키며, multilingual E5의 학습 방법을 정리한 기술 보고서는 2024년에 공개됐다.

### E5-Mistral-7B는 같은 E5 계열이지만 구조가 다르다

[`intfloat/e5-mistral-7b-instruct`](https://huggingface.co/intfloat/e5-mistral-7b-instruct)는 encoder-only XLM-R이 아니라 decoder-only Mistral-7B-v0.1을 embedding model로 fine-tuning한 checkpoint다. 32-layer, 7B parameter model이며 마지막 유효 token의 hidden state를 4,096차원 embedding으로 사용한다.

query에는 task instruction이 필요하고 document에는 붙이지 않는다.

```text
Instruct: Given a web search query, retrieve relevant passages that answer the query
Query: BM25와 Dense retrieval은 어떻게 다른가
```

BGE-M3 논문 표는 E5-Mistral-7B의 최대 길이를 8,192로 기록했다. 현재 공식 model card의 사용 예제와 limitation은 4,096 token 초과 입력을 권장하지 않는다. 또 multilingual data로 fine-tuning했지만 Mistral-7B-v0.1의 영어 중심 pre-training 때문에 다국어 사용에는 multilingual-e5-large를 권장한다. 논문의 표와 현재 checkpoint 사용 조건을 같은 것으로 가정하면 안 된다.

### 표의 model들은 같은 규모가 아니다

| 방식 | backbone | 출력 | 입력 길이 | query 형식 |
|---|---|---:|---:|---|
| BM25 | 없음 | sparse term score | neural 제한 없음 | raw query |
| mDPR | mBERT, 12 layers | 768 | 512 | raw query |
| mContriever | mBERT, 12 layers | 768 | 512 | raw query |
| mE5-large | XLM-R large, 24 layers | 1,024 | 512 | `query:` prefix |
| E5-Mistral-7B | Mistral-7B, 32 layers | 4,096 | 논문 8,192·model card 권장 4,096 | task instruction |
| BGE-M3 Dense | XLM-R large, 24 layers | 1,024 | 8,192 | instruction 불필요 |

다국어 Dense retrieval이라는 공통점은 있지만 parameter, input length, pooling, query format, training data가 다르다. 따라서 이 표는 같은 model size의 공정한 architecture 비교가 아니다. 각 시대의 공개 multilingual baseline과 M3의 최종 품질을 함께 열어 놓은 benchmark에 가깝다.

## 성능은 무엇을 기준으로 책정했나

검색 성능은 model이 스스로 정하는 값이 아니다. 먼저 query마다 어떤 document가 관련 있는지 정답표인 `qrels`를 만들고, model이 그 문서를 몇 등으로 올렸는지를 metric으로 계산한다.

이 논문의 핵심 metric은 `nDCG@10`과 `Recall@100`이다.

### nDCG@10은 상위 10개의 순서를 본다

`DCG`는 관련 문서가 위에 있을수록 큰 점수를 주고, 아래로 갈수록 logarithm으로 할인한다. `nDCG`는 그 값을 가능한 최적 순위의 점수로 나눠 0과 1 사이로 정규화한다.

```text
관련 문서가 1등에 있음       → 큰 점수
관련 문서가 9등에 있음       → 찾기는 했지만 할인된 점수
관련 문서가 10등 밖에 있음   → nDCG@10에는 반영되지 않음
```

따라서 `MIRACL nDCG@10 71.5`는 정확도 71.5%라는 뜻이 아니다. 여러 query의 normalized ranking quality를 평균낸 뒤 100을 곱해 표기한 값이다. 클릭률, 답변 정확도, 매출, latency를 뜻하지도 않는다.

### Recall@100은 상위 100개 안에 정답을 얼마나 담았는지 본다

Recall은 전체 관련 문서 중 검색 결과에 들어온 비율이다.

```text
정답 문서 4개 중 상위 100개에 3개가 들어옴
Recall@100 = 3 / 4 = 0.75
```

Recall이 높아도 정답이 99등에만 몰려 있으면 사용자가 보기에는 좋지 않을 수 있다. 반대로 nDCG@10이 높아도 상위 100개 전체의 정답 회수율은 낮을 수 있다. 그래서 candidate retriever를 볼 때 Recall, 최종 ranking을 볼 때 nDCG를 함께 보는 편이 낫다.

### 이 논문의 정답표는 어디서 왔나

| benchmark | 문제 | 주 metric | 정답의 성격 |
|---|---|---|---|
| MIRACL | 같은 언어의 Wikipedia passage 검색 | nDCG@10 | native speaker가 query와 관련 passage를 판정한 공개 benchmark |
| MKQA | 여러 언어 query로 영어 Wikipedia passage 검색 | Recall@100 | answer가 있는 passage를 찾는 cross-lingual benchmark |
| MLDR | 여러 언어의 긴 문서 검색 | nDCG@10 | 논문 저자들이 긴 문서의 paragraph를 바탕으로 GPT-3.5 질문을 생성한 dataset |
| NarrativeQA | 영어 장문 narrative 검색 | nDCG@10 | 기존 장문 benchmark |

이 차이가 중요하다. MIRACL의 사람 판정과 MLDR의 synthetic question-source document 관계는 같은 종류의 정답이 아니다. 더구나 논문의 multilingual fine-tuning data에는 MIRACL train data가 포함됐다. 공개 표의 숫자는 `model + training data + corpus + query + qrels + metric + candidate budget`을 한꺼번에 평가한 결과다.

```text
검색 성능 숫자
= 모델의 표현력만의 점수 X
= 특정 평가 설계 전체에서 나온 결과 O
```

## 결과를 어떻게 읽었나

### MIRACL에서는 Dense가 중심이었다

MIRACL 개발셋의 평균 `nDCG@10`은 다음과 같다.

| 방식 | nDCG@10 |
|---|---:|
| BM25, XLM-R tokenizer | 31.9 |
| mDPR | 41.8 |
| mContriever | 43.1 |
| mE5-large | 66.6 |
| E5-Mistral-7B | 63.4 |
| M3 Sparse | 53.9 |
| M3 Dense | 69.2 |
| M3 Multi-vector | 70.5 |
| M3 Dense + Sparse | 70.4 |
| M3 All | 71.5 |

M3 Dense는 비교한 Dense baseline 모두보다 높았다. 다만 mE5-large와의 차이는 2.6점이었고, 7B 규모의 E5-Mistral-7B가 mE5-large보다 낮았다. parameter 수만으로 multilingual retrieval 순위를 예상할 수 없었다.

M3 내부에서는 Dense가 Sparse보다 15.3점 높았다. Dense+Sparse는 Dense보다 1.2점, All은 Dense보다 2.3점 높았다. 결합은 좋아졌지만 이 dataset에서 중심 retriever는 Dense였다.

한국어만 보면 Dense 69.9, Sparse 61.5, Dense+Sparse 71.2, All 72.1이었다. 한국어에서도 결합이 높았지만 이 값 역시 사내 한국어 문서에 대한 zero-shot 결과가 아니다.

가장 중요한 조건은 학습 데이터다. 논문의 multilingual fine-tuning data에는 MIRACL과 Mr. TyDi가 포함됐다. 따라서 표 1의 MIRACL 결과를 “MIRACL을 보지 않은 모델의 일반화 성능”으로 해석할 수 없다. BEIR의 zero-shot 결과와 같은 종류의 증거가 아니다.

### MLDR에서는 Sparse가 Dense보다 높았다

multilingual long-document retrieval dataset인 MLDR test 결과는 다음과 같다.

| 방식 | nDCG@10 |
|---|---:|
| BM25, XLM-R tokenizer | 53.6 |
| M3 Dense | 52.5 |
| M3 Sparse | 62.2 |
| M3 Multi-vector | 57.6 |
| M3 Dense + Sparse | 64.8 |
| M3 All | 65.0 |

이 데이터에서는 Sparse가 Dense보다 9.7점 높았고, Dense+Sparse가 Sparse보다 2.6점 높았다. 긴 문서의 특정 term signal을 하나의 `[CLS]` vector보다 sparse token weight가 더 잘 보존했을 가능성을 생각할 수 있다.

하지만 MLDR도 사내 long-document 일반화의 증거로 바로 쓸 수 없다. 논문은 Wikipedia, Wudao, mC4의 긴 문서에서 학습·개발·시험 split을 직접 만들고, 같은 성격의 synthetic question data를 fine-tuning에 사용했다. held-out test이지만 unknown domain zero-shot 평가와는 다르다.

### 강한 BM25 analyzer를 넣으면 해석이 달라졌다

부록의 tokenizer 비교가 가장 인상적이었다.

| 방식 | MIRACL | MKQA | MLDR |
|---|---:|---:|---:|
| BM25, XLM-R tokenizer | 31.9 | 39.9 | 53.6 |
| BM25, Lucene analyzer | 38.5 | 40.9 | 64.1 |
| M3 Sparse, XLM-R tokenizer | 53.9 | 45.3 | 62.2 |

MLDR에서 Lucene analyzer를 사용한 BM25는 64.1로 M3 Sparse 62.2보다 높았고, Dense+Sparse 64.8에 0.7점 차이까지 접근했다.

논문 본표의 BM25는 M3와 vocabulary와 latency 조건을 맞추기 위해 XLM-R tokenizer를 사용했다. 공정한 통제 실험이라는 이유는 이해된다. 그러나 실제 서비스를 고르는 사람에게는 잘 설정한 lexical baseline이 더 중요하다. main table의 BM25 한 줄만 보고 learned sparse의 우위를 일반화하면 안 된다.

이 결과로 내 생각이 바뀌었다.

> BM25의 `k1`, `b`나 embedding model을 바꾸기 전에 tokenizer와 analyzer가 실제로 만든 term부터 확인해야 한다.

### Cross-lingual에서는 Sparse의 역할이 작아졌다

MKQA는 여러 언어의 query로 영어 passage를 찾는 cross-lingual retrieval이다. 평균 `Recall@100`은 Dense 75.1, Sparse 45.3, Dense+Sparse 75.3, All 75.5였다.

query와 passage의 언어가 다르면 같은 token이 존재해야 점수를 얻는 Sparse 방식이 불리하다. 논문도 두 언어 사이에 공통 term이 적기 때문이라고 설명한다. 이 결과는 Sparse를 무조건 결합할 필요가 없다는 반례다.

## 그럼 Google 검색과 Naver 검색도 Dense+Sparse인가

짧게 답하면 **두 회사의 전체 검색을 BGE-M3의 Dense+Sparse라고 부를 근거는 없다.** 공개된 자료를 보면 lexical·semantic·품질·신뢰도·최신성·사용 환경 등 여러 신호를 함께 쓰는 다단계 검색이라는 점까지는 확인할 수 있다. 어떤 index와 model을 어떤 weight로 결합하는지는 공개되지 않았다.

### Google 검색에서 공식적으로 확인할 수 있는 것

[Google Search Central](https://developers.google.com/search/docs/fundamentals/how-search-works)은 검색 과정을 크게 세 단계로 설명한다.

```text
Crawling → Indexing → Serving search results
```

- crawler가 web page를 발견하고 가져온다.
- text, image, video, title, alt 같은 내용을 분석하고 중복·대표 URL을 판단해 index에 저장한다.
- query가 들어오면 index에서 relevant하고 quality가 높은 결과를 찾고 순위를 정한다.

Google은 ranking에 수백 가지 factor가 들어갈 수 있고 location, language, device 같은 맥락도 사용한다고 밝힌다. [공식 ranking systems 문서](https://developers.google.com/search/docs/appearance/ranking-systems-guide)에는 BERT, neural matching, RankBrain, passage ranking, freshness, link analysis, original content, reliable information 같은 서로 다른 system이 함께 나온다.

여기서 neural matching과 RankBrain은 exact query term이 모두 없어도 관련 concept을 이해한다고 설명된다. Dense retrieval의 목표와 닮았지만, **Google이 web candidate를 BGE-M3 같은 dense vector index로 검색한다고 공개한 것은 아니다.** PageRank, link, freshness, spam, locality까지 포함된 web ranking 전체를 BM25 대 Dense 구도로 축소하면 실제 시스템을 지나치게 단순화하게 된다.

### Naver 검색에서 공식적으로 확인할 수 있는 것

[Naver Search Advisor](https://searchadvisor.naver.com/guide/report-crawl-refine)는 web content가 다음 단계를 거친다고 설명한다.

```text
수집 → 정제 → 색인 → 랭킹
```

Naver 통합검색은 web 문서만 한 줄로 정렬하는 화면이 아니다. [검색 가이드](https://help.naver.com/service/5626/contents/19010?osType=COMMONOS)에서 설명하듯 blog, cafe, image, video 같은 영역의 결과를 한 화면에 종합한다. 검색어에 따라 어떤 collection과 block을 보여줄지 정하고, 각 영역 안에서도 다시 순위를 정해야 한다.

공식 고객센터는 일반 검색 결과가 [사용자 선호도와 검색어 연관성 등을 종합한 값](https://help.naver.com/service/5626/contents/23842?lang=ko&osType=COMMONOS)으로 자동 노출된다고 설명한다. Blog 검색에는 범위를 더 구체적으로 공개했다.

- [C-Rank](https://help.naver.com/service/5626/contents/22927?lang=ko)는 개별 문서보다 출처의 관심사 집중도, content 품질, 소비·생산의 연쇄반응을 종합해 출처 신뢰도와 인기도를 본다.
- [D.I.A.](https://help.naver.com/service/5626/contents/22926?osType=COMMONOS)는 keyword별 선호 문서, 주제 적합도, 경험 정보, 정보 충실성, 의도, 독창성, 적시성 등을 문서 ranking에 반영한다.

이것도 Naver 전체 검색의 수식은 아니다. C-Rank와 D.I.A.는 공식 문서상 blog 검색 설명이다. Shopping, Place, News, Cafe, Web은 data와 relevance 기준이 다르므로 같은 weight 하나로 묶였다고 가정할 수 없다.

### 두 검색엔진을 보고 내가 얻은 결론

확인할 수 있는 범위에서 Google과 Naver 모두 `query-document 유사도 하나`만으로 끝나는 검색이 아니다. 후보를 만들고, 여러 relevance·quality signal을 합치고, query intent와 surface에 맞는 결과 형태를 고른다.

`추정:` 두 회사 모두 exact lexical signal과 neural semantic signal을 어떤 형태로든 함께 사용할 가능성은 높다. 그러나 내부 구현이 BM25인지, learned sparse인지, dense ANN인지, cross-encoder인지, query마다 어떤 경로를 타는지는 공개 자료만으로 확정할 수 없다. 따라서 “Google도 Dense+Sparse다” 또는 “Naver는 BM25다”라고 단정하지 않는 것이 정확하다.

## 그럼 어떤 경우에 쓰면 좋을까

### BGE-M3가 좋은 후보가 되는 경우

- 같은 encoder로 Dense와 learned sparse를 함께 실험하고 싶을 때
- 여러 언어의 같은-language document search가 필요할 때
- 짧은 query부터 긴 document까지 하나의 model family로 다루고 싶을 때
- Dense와 Sparse의 candidate overlap과 결합 효과를 빠르게 비교할 때
- 상위 소수 후보에만 Multi-vector reranking을 적용할 때
- 별도 sparse model과 dense model을 각각 운영하기 전에 강한 통합 후보가 필요할 때

하나의 encoder가 세 출력을 제공하므로 모델 차이보다 retrieval function 차이에 집중하기 좋다. 사내 평가의 실험 후보로는 매력적이다.

### 먼저 다른 경로를 봐야 하는 경우

- 정확한 품번·LOT·W/O 조회에서는 exact field나 registry가 먼저다.
- query와 document 언어가 다른 검색에서는 Sparse overlap이 작아질 수 있어 Dense 중심 평가가 먼저다.
- CPU·memory·latency budget이 매우 작다면 Multi-vector와 8K encoding 비용을 감당할 이유를 먼저 증명해야 한다.
- corpus가 작고 lexical signal이 강하면 잘 설정한 BM25가 요구 품질을 이미 만족할 수 있다.
- 최신 재고·WIP·납기 계산에는 embedding보다 API·SQL이 맞다.
- 표의 row·column 관계가 핵심이면 text embedding 전에 구조화 parsing과 field search가 필요하다.

## 과연 이것이 정답일까

내 답은 강한 후보이지 정답은 아니다.

논문의 `All이 최고의 성능`이라는 문장은 **저자들이 선택한 benchmark, metric, candidate 구성, weight에서 평균 점수가 가장 높았다**는 뜻이다. 모든 언어와 모든 query에서 이겼다는 뜻도 아니다.

MLDR 표를 언어별로 다시 세어보면 `All`은 `Dense+Sparse`보다 13개 언어 중 5개에서 높고 8개에서 낮다. 그런데 언어 평균은 65.0 대 64.8로 All이 0.2점 높다. 일부 언어의 큰 이득이 평균을 올렸기 때문이다. 평균 1위와 모든 조건의 우승은 같은 말이 아니다.

또한 논문이 말하는 performance는 주로 nDCG와 Recall이라는 relevance quality다. p95 latency, index 크기, GPU 비용, update freshness, 사용자 클릭 만족도까지 합친 `최고`가 아니다. 실제 서비스에서는 품질이 0.2점 오르더라도 비용이 크게 늘면 Dense+Sparse를 선택할 수 있고, 특정 업무 query에서 All이 나쁘면 query routing으로 끌 수도 있다.

BGE-M3가 해결한 것은 “한 모델에서 여러 retrieval score를 만들 수 있는가”와 “그 기능들을 함께 학습하면 성능이 오르는가”이다. 사내 MES에서 어떤 경로가 이기는지는 답하지 않는다.

일반화를 막는 조건도 있다.

1. MIRACL은 fine-tuning data에 포함됐다.
2. MLDR은 논문이 구성한 long-document data와 가까운 조건에서 학습·평가됐다.
3. Hybrid는 단일 retriever와 candidate budget이 같지 않을 수 있다.
4. Multi-vector는 Dense top 200에 의존하는 reranker다.
5. 최대 8,192 token 지원은 그 길이의 모든 정보를 손실 없이 검색한다는 뜻이 아니다.
6. 100개 이상 언어 지원은 언어별 품질이 같다는 뜻이 아니며, 저자도 이를 limitation으로 적었다.

모델이 세 기능을 제공해도 exact lookup, metadata filter, access control, freshness, table structure는 따로 설계해야 한다.

## 이것보다 더 좋은 것은 무엇일까

하나의 모델 이름으로 답할 수 없었다. 실패 종류마다 더 나은 선택이 달라진다.

| 목표 | 먼저 비교할 대상 | 왜 더 나을 수 있나 |
|---|---|---|
| 식별자 exact hit | keyword field / registry | 한 글자 차이를 semantic similarity로 뭉개지 않는다. |
| 낮은 비용의 lexical search | 언어별 analyzer를 적용한 BM25 | model inference 없이 강한 baseline을 만들 수 있다. |
| domain 표현 차이 | 사내 relevance data로 학습한 Dense·Sparse | 공개 Wikipedia와 다른 업무 어휘를 직접 학습한다. |
| 후보 순서 개선 | Cross-Encoder reranker | candidate를 query와 함께 읽어 더 세밀하게 비교한다. |
| token-level matching | ColBERT 계열 late interaction | 하나의 `[CLS]`에 모두 압축하지 않는다. |
| fusion 안정성 | RRF 또는 validation으로 조정한 score fusion | score scale과 query 유형에 따른 가중치 문제를 줄일 수 있다. |
| 구조화·실시간 질문 | SQL / API / metric layer | 최신성과 정확한 field 조건을 문서 유사도에 맡기지 않는다. |

BGE-M3보다 무조건 좋은 방식 목록이 아니다. 사내 query 유형별로 실패를 더 직접 고치는 후보 목록이다.

예를 들어 Dense candidate `Recall@100`은 충분한데 top-10 순서만 나쁘다면 retriever를 바꾸기보다 Cross-Encoder가 맞을 수 있다. 반대로 정답이 candidate에 없으면 reranker가 아니라 Sparse나 다른 Dense candidate source를 추가해야 한다.

## 새로 배운 것

### Sparse라는 이름만으로 BM25를 뜻하지 않는다

이 논문을 읽으며 가장 먼저 바로잡은 부분이다. M3 Sparse는 inverted index를 사용할 수 있지만 term weight는 neural model이 학습한다. BM25, learned sparse, exact lookup은 모두 term을 다뤄도 서로 다른 방식이다.

### Hybrid 성능에는 candidate budget이 섞인다

Dense top 1,000과 Sparse top 1,000의 union을 재정렬하면 후보 수 자체가 늘 수 있다. 결합 점수의 효과와 candidate recall 증가를 나눠 보려면 같은 최종 candidate 수, union 크기, overlap을 같이 기록해야 한다.

### 긴 context 지원과 긴 문서 검색 품질은 다르다

8,192 token을 입력할 수 있어도 Dense는 전체를 하나의 vector로 압축한다. MLDR에서 Sparse가 Dense보다 높았다는 결과는 입력 가능 길이만 보고 long-document retrieval이 해결됐다고 말할 수 없음을 보여줬다.

### Tokenizer도 retrieval model의 일부다

MLDR BM25가 tokenizer 변경만으로 53.6에서 64.1로 올라갔다. 한국어에서는 형태소 분석, compound 분해, user dictionary, 품번 보존 방식이 점수 함수만큼 중요할 수 있다. 앞으로 “BM25와 BGE-M3를 비교했다”가 아니라 analyzer와 field mapping까지 함께 적어야 한다.

### Benchmark를 보기 전에 training data를 봐야 한다

MIRACL 69.2라는 점수만 보면 multilingual generalization처럼 보일 수 있다. training data 표를 보면 MIRACL 자체가 fine-tuning에 포함돼 있다. 모델 표의 숫자를 읽기 전에 train/dev/test의 관계부터 확인해야 한다.

## 느낀 점

BGE-M3의 가장 큰 장점은 높은 숫자보다 비교 가능한 세 retrieval 기능을 한 encoder에 넣었다는 점이라고 느꼈다. Dense와 Sparse를 별도 model로 구성하면 어느 차이가 architecture 때문이고 어느 차이가 training data 때문인지 분리하기 어렵다. 같은 backbone에서 세 score를 제공하면 적어도 실험 출발점은 단순해진다.

반대로 하나의 model이라는 표현이 운영까지 단순하게 만들어 주는 것은 아니다. Dense index와 sparse index가 따로 필요하고, Multi-vector reranking에는 token embedding과 추가 연산이 필요하다. candidate merge, score weight, top-N, latency budget도 정해야 한다. model artifact는 하나여도 retrieval system은 하나의 함수가 아니었다.

Sparse가 Dense보다 높았던 MLDR 결과도 인상적이었지만, 부록의 Lucene BM25 64.1을 보고 판단을 다시 멈췄다. 새로운 model이 강해 보여도 baseline의 tokenizer가 약하면 개선 폭을 과대평가할 수 있다. 논문 본표보다 부록이 실제 도입 판단에 더 중요했던 사례였다.

이 논문을 읽고 남은 질문은 “BGE-M3를 쓸까?”가 아니다.

> 우리 query에서 Dense만 찾은 정답, Sparse만 찾은 정답, exact lookup만 찾아야 하는 정답은 각각 무엇인가?

이 분해가 있어야 세 기능을 모두 켤 이유와 끌 이유를 설명할 수 있다.

## 이 논문의 한계

- MIRACL이 fine-tuning data에 포함돼 있어 해당 결과는 zero-shot 일반화 평가가 아니다.
- 저자들이 만든 MLDR와 synthetic long-document training data는 실제 기업 문서와 분포가 다를 수 있다.
- 언어별 학습 데이터가 불균형하며 100개 이상 언어의 품질 차이를 충분히 분석하지 않았다.
- 8,192 token 처리에는 큰 training·inference 비용이 붙고, 그보다 긴 문서는 별도 전략이 필요하다.
- Dense+Sparse와 단일 retriever의 candidate budget이 동일하지 않다.
- Multi-vector 결과는 Dense 후보 품질에 의존한다.
- benchmark는 access control, freshness, metadata filter, exact identifier 같은 운영 요구를 평가하지 않는다.

## MES에 적용한다면 다음에 확인할 것

1. Exact, analyzer를 적용한 BM25, M3 Sparse, M3 Dense를 각각 독립 baseline으로 만든다.
2. 품번·메뉴·절차·장애 증상별로 각 retriever만 찾은 정답을 기록한다.
3. Dense와 Sparse의 top-k overlap, union size, `Recall@k`를 측정한다.
4. 같은 최종 candidate budget에서 score fusion과 rank fusion을 비교한다.
5. Multi-vector는 Dense top-N을 바꾸며 `nDCG@10` 증가와 p95 latency를 함께 잰다.
6. 8K 문서 전체 입력과 의미 단위 chunking을 같은 corpus에서 비교한다.
7. 한국어 analyzer·사용자 사전·exact field를 고정하지 말고 실험 축으로 둔다.
8. 공개 benchmark 결과와 사내 평가 결과를 별도 표로 관리한다.

BGE-M3는 Dense와 Sparse 중 하나를 고르라는 답 대신, 같은 query에서 세 관점을 비교할 수 있는 좋은 실험 도구를 제공했다. 채택 여부는 기능 수가 아니라 사내 실패 query를 실제로 줄였는지로 결정해야 한다.

## Reference

- Jianlv Chen, Shitao Xiao, Peitian Zhang, Kun Luo, Defu Lian, Zheng Liu. [M3-Embedding 논문](https://aclanthology.org/2024.findings-acl.137/). Findings of ACL 2024, 2024. [arXiv v5](https://arxiv.org/abs/2402.03216).
- Xinyu Zhang et al. [MIRACL 논문](https://aclanthology.org/2023.tacl-1.63/). Transactions of the Association for Computational Linguistics, 2023.
- Gautier Izacard et al. [Unsupervised Dense Information Retrieval with Contrastive Learning](https://arxiv.org/abs/2112.09118). Transactions on Machine Learning Research, 2022.
- Liang Wang et al. [Text Embeddings by Weakly-Supervised Contrastive Pre-training](https://arxiv.org/abs/2212.03533). arXiv 2212.03533, 2022.
- Liang Wang et al. [Multilingual E5 Text Embeddings](https://arxiv.org/abs/2402.05672). arXiv 2402.05672, 2024.
- Liang Wang et al. [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368). arXiv 2401.00368, 2024.
- Google Search Central. [Google 검색의 작동 방식](https://developers.google.com/search/docs/fundamentals/how-search-works), [검색 순위 시스템 가이드](https://developers.google.com/search/docs/appearance/ranking-systems-guide).
- Naver Search Advisor. [수집·정제·색인·랭킹](https://searchadvisor.naver.com/guide/report-crawl-refine).
- Naver 검색 고객센터. [검색 노출 순서](https://help.naver.com/service/5626/contents/22925?lang=ko), [C-Rank](https://help.naver.com/service/5626/contents/22927?lang=ko), [D.I.A.](https://help.naver.com/service/5626/contents/22926?osType=COMMONOS).
