"RAG 핵심 학습 (8/26) — 임베딩 모델 비교: BGE-M3 / OpenAI / Upstage / E5 / Jina / Voyage"
청크와 메타데이터가 준비됐다. 다음은 어떤 임베딩 모델로 인덱싱할 것인가.
임베딩 모델 선택은 RAG에서 후회가 가장 큰 결정이다. 한 번 인덱싱하면 모델 교체가 전체 재인덱싱을 부르고, 비용·속도·언어 성능은 프로덕션에서야 차이가 드러난다. 본 편은 2026년 기준 주류 6개 — BGE-M3 / OpenAI text-embedding-3 / Upstage Solar Embeddings / E5-mistral / Jina v3 / Voyage 3 — 을 차원·다국어(특히 한국어)·비용·로컬 가능 여부·max input·model card 6축으로 비교한다. 그리고 사용자 노트 35장 2절의 Model-aware Ingestion 원칙을 모델 카드 → 스키마 매핑 흐름으로 푼다.
0. Prerequisites
- 7편(메타데이터). 모델을 바꾸면 메타데이터까지 재복제해야 한다.
- 코사인 유사도·내적의 기본 (Bi-encoder 식은 10편에서 자세히).
- 토큰 ≠ 문자 — 모델별 토크나이저 차이를 의식.
1. 학습 목표
- 6개 주류 모델의 한 줄 차이를 안다.
- 벡터 차원·max input·정규화가 검색 품질·비용에 미치는 영향을 식으로 안다.
- 모델 카드 읽는 법 — 학습 입력 형식이 ingestion schema를 결정.
- 교체 비용 — 모델 변경 시 무엇을 다시 해야 하는지 안다.
2. 핵심 요약
BGE-M3(BAAI)는 오픈소스 다국어 표준 — 1024차원, 한국어·영어 둘 다 강함. OpenAI text-embedding-3-small/large는 API 접근성과 Matryoshka(가변 차원). Upstage Solar는 한국어 특화 상용 임베딩. E5-mistral-7b-instruct는 instruction-tuned 오픈소스 최강 — 7B 모델 추론 필요. Jina v3는 task-aware(검색·매칭·분류 prefix 분리). Voyage 3는 retrieval 특화 상용 — 한국어 미지원이 약점. 선택 기준 세 줄: 언어 분포가 한국어 ≥ 30%면 BGE-M3 또는 Upstage, 완전 오픈/로컬이면 BGE-M3 또는 E5, API 단순함이 우선이면 OpenAI. 모델 카드의 학습 입력 형식(title/body/query prefix)을 ingestion 스키마에 그대로 반영해야 잠재 성능이 나온다 — 사용자 노트 35장 2절의 핵심.
3. 직관 — 같은 query, 다른 임베딩, 다른 답
"보안 정책의 예외 신청 절차?"를 6개 모델로 임베딩하면 모두 다른 벡터가 나온다. 같은 코퍼스를 동일 청크로 인덱싱하더라도 top-K가 서로 다르다. 어떤 모델은 "예외"라는 어휘 자체에 강하고, 어떤 모델은 "신청 → 절차"의 의미 연쇄에 강하다.
어떤 모델이 내 코퍼스에 가장 강한지는 벤치마크 평균이 아니라 내 평가셋(14편)으로 측정해야 한다. 이 글이 줄 수 있는 건 후보를 좁히는 표이지 정답은 아니다.
4. 정의 — 6개 주류 모델 비교 표 (2026년 기준)
| 모델 | 차원 | Max input | 다국어 / 한국어 (0-3) | 비용 (1M tokens) | 로컬 가능 | 특징 |
|---|---|---|---|---|---|---|
| BGE-M3 (BAAI) | 1024 | 8192 | 3 / 3 | $0 (자체 GPU) | ✅ | dense+sparse+colbert 3-in-1 |
| OpenAI text-embedding-3-small | 1536 (가변) | 8191 | 2 / 2 | $0.02 | ❌ | Matryoshka, 저비용 |
| OpenAI text-embedding-3-large | 3072 (가변) | 8191 | 3 / 2 | $0.13 | ❌ | 최고 정확도 |
| Upstage Solar Embeddings 1.5 | 4096 | 4000 | 2 / 3 | $0.10 | ❌ | 한국어 특화 |
| E5-mistral-7b-instruct | 4096 | 32K | 3 / 2 | $0 (7B GPU) | ✅ (요건 큼) | instruction-tuned, MTEB 상위 |
| Jina embeddings v3 | 1024 (가변) | 8192 | 3 / 2 | $0.018 | ✅ (Apache 2.0) | task-aware prefix (검색/매칭/분류) |
| Voyage 3 | 1024 | 32K | 2 / 0 | $0.06 | ❌ | retrieval 최적화, 한국어 약함 |
점수는 0~3 상대 평가 (MTEB-Korean·MTEB 다국어 평균 기반). 절대 점수는 14편 평가셋으로 측정.
5. 식 — 차원·정규화·유사도
임베딩 검색의 비용·정확도는 세 변수로 정리된다.
- \(d\) = 벡터 차원
- \(N\) = 인덱스 청크 수
- \(\|x\|\) = 임베딩 벡터의 노름
저장 비용 (float32 기준):
$$\text{Storage} = N \cdot d \cdot 4 \text{ bytes}$$
예: \(N=1M\), \(d=1024\) → 4 GB. \(d=4096\) → 16 GB. 4배 차이가 그대로 비용·메모리 차이.
검색 latency (HNSW 기준 근사):
$$\text{Latency} \approx \mathcal{O}(d \cdot \log N)$$
\(d\)에 선형. 1024 vs 4096는 latency 4배 차이.
유사도 함수:
- 코사인: \(\cos(\theta) = \frac{x \cdot y}{\|x\| \|y\|}\). 노름과 무관, 방향만 비교.
- 내적(dot): \(x \cdot y\). 노름 포함 — 짧은 벡터 페널티.
- 유클리드: \(\|x - y\|\). 거리 기반.
중요 원칙: 모델이 L2 normalized vectors를 출력하면 코사인 = 내적 = 거리의 단조 변환이라 결과 동일. Voyage·OpenAI는 정규화 출력, BGE-M3·E5도 표준 사용 시 정규화. 다만 raw output을 정규화 안 하면 결과가 달라진다 — 명시적 정규화가 안전.
Matryoshka 임베딩 (OpenAI 3, Jina v3): 학습 시 여러 차원 동시 학습 → 추론 시 앞 d'차원만 잘라 써도 성능 손실 최소. \(d'=256\)으로 줄이면 저장 16배 절약.
6. 원리 워크스루 — 모델 카드를 ingestion 스키마로
6.1 모델 카드에서 읽어야 할 4가지
| 항목 | 무엇을 결정하나 | 예시 |
|---|---|---|
| 학습 입력 형식 | ingestion 스키마 | E5: "passage: {text}" / "query: {text}" |
| Max input length | 청크 max size | BGE-M3=8192, Voyage 3=32K |
| 정규화 여부 | 유사도 함수 선택 | BGE-M3 정규화 권장 → 코사인 |
| Instruction tuning | query 전처리 | E5-instruct: query 앞에 task instruction |
6.2 BGE-M3 — 표준 예시
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-m3")
doc_emb = model.encode(
chunk_text,
normalize_embeddings=True, # 코사인 사용을 위해
)
query_emb = model.encode(
"예외 신청 절차?",
normalize_embeddings=True,
)
6.3 E5-mistral — instruction prefix가 결정적
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("intfloat/e5-mistral-7b-instruct")
TASK = "Given a web search query, retrieve relevant passages that answer the query"
query_text = f"Instruct: {TASK}\nQuery: 예외 신청 절차?"
query_emb = model.encode(query_text, normalize_embeddings=True)
doc_emb = model.encode(chunk_text, normalize_embeddings=True)
이 prefix를 생략하면 E5의 성능이 체감 가능하게 떨어진다. 모델 카드를 말 그대로 따라야 한다.
6.4 Jina v3 — task-aware prefix
from transformers import AutoModel
model = AutoModel.from_pretrained("jinaai/jina-embeddings-v3", trust_remote_code=True)
query_emb = model.encode(["예외 신청 절차?"], task="retrieval.query")
doc_emb = model.encode([chunk_text], task="retrieval.passage")
같은 모델인데 task별로 다른 학습 가중치를 거쳐 임베딩. 검색 task는 query/passage 비대칭을 의식.
6.5 OpenAI — Matryoshka로 차원 축소
from openai import OpenAI
client = OpenAI()
resp = client.embeddings.create(
model="text-embedding-3-large",
input=chunk_text,
dimensions=512, # 3072 → 512로 축소, 학습 시 그렇게 설계됨
)
emb = resp.data[0].embedding
저장·latency가 \(\times 1/6\). 정확도 손실은 과제별로 다르지만 보통 1~3%p. 비용 결정이 큰 프로젝트에 강함.
6.6 Upstage Solar — 한국어 특화 사용
from openai import OpenAI
client = OpenAI(api_key=UPSTAGE_KEY, base_url="https://api.upstage.ai/v1/solar")
resp = client.embeddings.create(
model="solar-embedding-1-large-passage", # passage / query 모델 분리
input=chunk_text,
)
-passage와 -query 두 모델이 분리돼 있다. query 임베딩은 반드시 -query 모델로. 비대칭 임베딩의 전형.
7. 변형과 사례
7.1 BGE-M3의 3-in-1 — Dense + Sparse + ColBERT
- 무엇이 바뀌나: 한 모델이 dense, sparse(BM25 유사), late interaction(ColBERT) 3가지 출력을 동시에 낸다.
- 왜 쓰나: 12편 Hybrid Search를 한 모델로 — 운영 단순화.
- 무엇이 가능해졌나: 별도 sparse 모델·ColBERT 가중치 관리 불필요.
- 어디에 적합한가: 다국어 + hybrid + 운영 단순 우선 프로젝트.
- 한계: 단일 모드(예: 순수 dense)에선 전문 모델에 약간 밀림.
7.2 OpenAI 3-large의 차원 축소 trade-off
- 무엇이 바뀌나: 3072 → 1024 → 512 → 256으로 학습된 차원 단계.
- 왜 쓰나: 저장·검색 비용 \(\times 1/d_{\text{ratio}}\).
- 무엇이 가능해졌나: 인덱스가 수 TB가 되는 코퍼스에서 비용 결정.
- 어디에 적합한가: 대규모(>10M chunks), 비용 민감.
- 한계: 정확도 1~3%p 손실. 평가셋(14편)으로 확인 필수.
7.3 E5-mistral — 7B 임베딩의 비용 트레이드
- 무엇이 바뀌나: 임베딩 자체가 7B 파라미터 LLM. 추론에 GPU 필요.
- 왜 쓰나: instruction-tuned 임베딩의 MTEB 상위 성능.
- 무엇이 가능해졌나: task별 instruction으로 같은 모델을 다용도 사용.
- 어디에 적합한가: 자체 GPU 인프라, 모델 통제권 우선.
- 한계: 추론 latency 100~500ms(GPU 사양), 청크당 비용 \(\approx\) GPU/h 비례.
7.4 Voyage 3 — retrieval 특화의 한국어 약점
- 무엇이 바뀌나: retrieval에 집중 학습. RAG 벤치마크에서 상위.
- 왜 쓰나: 영어 retrieval에선 OpenAI보다 종종 강함.
- 무엇이 가능해졌나: 영어 코퍼스 + 비용/성능 균형.
- 어디에 적합한가: 영어 중심 RAG.
- 한계: 한국어 학습 비중 낮음 → 한국어 코퍼스엔 권장하지 않음. 사내 다국어 코퍼스에선 BGE-M3 또는 Upstage.
7.5 Jina v3 — task별 가중치 분리
- 무엇이 바뀌나: 같은 모델 가중치에 task adapter가 붙어 task별 다른 임베딩.
- 왜 쓰나: 검색·매칭·분류 task가 섞인 시스템에서 모델 1개로 해결.
- 무엇이 가능해졌나: 모델 수 1개, 운영 단순화.
- 어디에 적합한가: RAG + 다른 NLP task 동시 운영.
- 한계: task prefix 잘못 넘기면 체감 가능한 성능 저하.
8. 한계와 실패 양상
8.1 모델 변경 = 전체 재인덱싱
- 왜 본질적인가: 임베딩 공간 자체가 다르므로 옛 인덱스와 새 인덱스가 호환 불가. 점진 마이그레이션 불가.
- 진단: 모델 변경 결정 시 재인덱싱 비용(API 호출 또는 GPU 시간) + 재인덱싱 동안의 검색 가용성 둘 다 추정.
- 완화: 모델 후보를 초기 평가셋으로 좁히고, 변경 시 blue-green 인덱스(둘 다 띄우고 query 시 라우팅).
- 다음 편: 9편(벡터DB의 collection 분리).
8.2 비대칭 임베딩 무시 — query/passage 혼동
- 왜 본질적인가: E5, Upstage, Jina는 query와 passage가 서로 다른 prefix·모델. 한쪽 prefix를 빠뜨리면 벡터 공간이 어긋남.
- 진단: 같은 코퍼스에서 문서끼리 유사도가 query-문서 유사도보다 높음. retrieval recall 비정상 저하.
- 완화: 코드에 명시적 분기 —
embed_query(),embed_documents()를 분리. LangChain은 두 함수가 기본 분리돼 있음. - 다음 편: 10편(Dense Retrieval 식 — 비대칭의 의미).
8.3 정규화 누락 — 코사인이 망가짐
- 왜 본질적인가: 모델 출력이 정규화 안 됐는데 인덱스가 코사인 가정하면 내적이 노름 차이를 학습. 짧은 청크·긴 청크의 순위가 왜곡.
- 진단: 임베딩 노름의 분포 측정. \(\|x\|\)가 1에서 멀리 떨어진 분포면 정규화 안 됨.
- 완화: 임베딩 함수에
normalize_embeddings=True명시, 또는 클라이언트 단에서x / np.linalg.norm(x). - 다음 편: 9편(벡터DB의 metric=cosine vs dot 설정).
8.4 Max input 초과 — 청크 잘림
- 왜 본질적인가: 청크 토큰 수가 모델의 max input을 넘으면 조용히 잘림 — 끝부분이 임베딩에 반영되지 않음.
- 진단: 청크 토큰 분포 측정. 95p가 max input의 80%를 넘으면 위험.
- 완화: 청크 max size를 모델 max input \(\times 0.8\)로 제한. 경계 통계 모니터링.
- 다음 편: 5편 청킹과 8편 모델 연동.
8.5 벤치마크 평균 맹신
- 왜 본질적인가: MTEB 평균이 모든 도메인의 평균. 특정 도메인(법무·의료·한국어 사내 문서)에선 완전히 다른 순위.
- 진단: 자체 평가셋(14편) 없이 MTEB 1등만 선택했을 때 retrieval recall 저조.
- 완화: 후보 3~4개로 좁히고 자체 평가셋으로 결정. 50~200쌍의 query-document 쌍이면 충분.
- 다음 편: 14편(평가셋 만들기), 15편(검색 품질 지표).
8.5 Common Pitfalls
- "MTEB 1등 = 내 코퍼스에도 1등." — 8.5절.
- "한 모델로 query와 passage 둘 다." — 8.2절. E5·Upstage·Jina는 비대칭.
- "정규화는 라이브러리가 알아서." — 8.3절. 명시적 보장이 안전.
- "max input이 8K니까 7K 청크는 안전." — 토크나이저별 차이 + 메타데이터 prefix 포함하면 초과 가능.
- "OpenAI 3-large가 가장 정확하니 무조건." — 한국어 비중 30% 이상이면 BGE-M3 또는 Upstage가 더 강할 수 있음.
9. 정리된 결론
Q1. 한국어 비중 ≥ 30% 코퍼스의 1순위 후보는?
BGE-M3 (오픈, 1024d, 다국어 강함) 또는 Upstage Solar Embeddings (상용, 한국어 특화). Chapter: 2장, 4장.
Q2. 모델 변경 비용이 큰 이유를 한 줄로 말하라.
임베딩 공간이 서로 호환 불가 — 전체 재인덱싱 필수, 옛 인덱스와 점진 병행 불가. Chapter: 8.1절.
Q3. E5-mistral의 query 임베딩에 반드시 추가해야 할 것은?
Task instruction prefix — Instruct: {task}\nQuery: {q} 형태. 빠뜨리면 성능 체감 저하.
Chapter: 6.3절, 8.2절.
Q4. Matryoshka 임베딩이 저장 비용에 미치는 영향은?
\(d=3072 \to d'=512\)면 저장 \(\times 1/6\). 정확도 손실 1~3%p가 일반적. Chapter: 5장, 7.2절.
Q5. 임베딩 모델 선택의 마지막 결정 도구는?
자체 평가셋 (14편). 50~200쌍의 query-document 쌍으로 후보 3~4개를 비교. Chapter: 8.5절.
10. 추가 학습
1차 자료
- BAAI. BGE-M3: Multi-Lingual, Multi-Functionality, Multi-Granularity Text Embeddings. arXiv:2402.03216 (2024).
- OpenAI. New embedding models and API updates (2024-01 blog) — Matryoshka 도입.
- Wang, L. et al. Improving Text Embeddings with Large Language Models (E5-mistral). arXiv:2401.00368 (2024).
- Jina AI. jina-embeddings-v3: Multilingual Embeddings With Task LoRA. arXiv:2409.10173 (2024).
- Upstage. Solar Embeddings: a Korean-optimized embedding model (2024 tech report).
- MTEB Leaderboard:
https://huggingface.co/spaces/mteb/leaderboard(절대 점수는 코퍼스별로 다름 유의).
공식 docs
- BGE-M3 model card:
https://huggingface.co/BAAI/bge-m3 - OpenAI Embeddings:
https://platform.openai.com/docs/guides/embeddings - Upstage Embeddings:
https://developers.upstage.ai/docs/apis/embeddings - Voyage AI:
https://docs.voyageai.com/ - Jina v3:
https://huggingface.co/jinaai/jina-embeddings-v3
보조 자료
- 사용자 노트 7장 — 임베딩.
- 사용자 노트 35장 2절 — Model-aware Ingestion, Title/Body Schema, Field-aware Embedding.
Cheat Sheet
| 시나리오 | 1순위 | 2순위 |
|---|---|---|
| 한국어 ≥ 30%, 오픈/로컬 | BGE-M3 | E5-mistral |
| 한국어 ≥ 30%, 상용 API | Upstage Solar | OpenAI 3-large |
| 영어 중심, API | Voyage 3 또는 OpenAI 3-large | Jina v3 |
| 영어 중심, 오픈/로컬 | BGE-M3 | E5-mistral |
| 대규모(>10M chunks), 비용 우선 | OpenAI 3-small + Matryoshka 512d | BGE-M3 + sparse |
| RAG + 분류·매칭 task 동시 | Jina v3 (task-aware) | OpenAI 3 |
선택 기준 한 줄: 언어 분포·운영 형태(API vs 로컬)·코퍼스 크기 → 후보 3개 → 평가셋으로 결정.
Bridge — 다음 편
다음 — RAG 핵심 학습 (9/26) 벡터DB 비교: FAISS / Chroma / Qdrant / Milvus / Weaviate / Pinecone / pgvector.
임베딩이 정해졌으면 다음은 어디에 저장할 것인가다. 7개 주류 벡터DB를 ANN 알고리즘(HNSW/IVF), metadata filter 표현력, update/delete 지원, 운영 비용으로 비교한다. 7편 메타데이터 설계가 벡터DB별 지원과 어떻게 만나는지가 핵심.
시리즈 전체 안내: 시리즈 목차
댓글
댓글 쓰기