"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. 학습 목표

  1. 6개 주류 모델의 한 줄 차이를 안다.
  2. 벡터 차원·max input·정규화가 검색 품질·비용에 미치는 영향을 식으로 안다.
  3. 모델 카드 읽는 법 — 학습 입력 형식이 ingestion schema를 결정.
  4. 교체 비용 — 모델 변경 시 무엇을 다시 해야 하는지 안다.

2. 핵심 요약

BGE-M3(BAAI)는 오픈소스 다국어 표준 — 1024차원, 한국어·영어 둘 다 강함. OpenAI text-embedding-3-small/large는 API 접근성과 Matryoshka(가변 차원). Upstage Solar한국어 특화 상용 임베딩. E5-mistral-7b-instructinstruction-tuned 오픈소스 최강 — 7B 모델 추론 필요. Jina v3task-aware(검색·매칭·분류 prefix 분리). Voyage 3retrieval 특화 상용 — 한국어 미지원이 약점. 선택 기준 세 줄: 언어 분포가 한국어 ≥ 30%면 BGE-M3 또는 Upstage, 완전 오픈/로컬이면 BGE-M3 또는 E5, API 단순함이 우선이면 OpenAI. 모델 카드의 학습 입력 형식(title/body/query prefix)을 ingestion 스키마에 그대로 반영해야 잠재 성능이 나온다 — 사용자 노트 35장 2절의 핵심.


3. 직관 — 같은 query, 다른 임베딩, 다른 답

"보안 정책의 예외 신청 절차?"를 6개 모델로 임베딩하면 모두 다른 벡터가 나온다. 같은 코퍼스를 동일 청크로 인덱싱하더라도 top-K가 서로 다르다. 어떤 모델은 "예외"라는 어휘 자체에 강하고, 어떤 모델은 "신청 → 절차"의 의미 연쇄에 강하다.

diagram-1

어떤 모델이 내 코퍼스에 가장 강한지벤치마크 평균이 아니라 내 평가셋(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별 지원과 어떻게 만나는지가 핵심.

시리즈 전체 안내: 시리즈 목차

댓글

이 블로그의 인기 게시물

"LLM 핵심 학습 (1/6) — 기본: 토큰화·임베딩·어텐션·위치 인코딩"

"LLM 핵심 학습 (2/6) — 파인튜닝: LoRA·QLoRA·증류·Adapter"

"ML 기초 학습 (1/9) — 머신러닝과 sklearn: 학습의 좌표계"