Phase 7: RAG | 예상 학습 시간: 40분
🎯 학습 목표
- 임베딩 모델이 텍스트를 어떤 형태의 벡터로 변환하는지, 그 의미를 설명할 수 있다
- 임베딩 모델 선택 시 고려해야 할 기준(차원 수, 도메인 적합성, 비용)을 이해한다
- 코사인 유사도/내적/유클리드 거리의 차이와 ANN 검색이 필요한 이유를 설명할 수 있다
1. 임베딩이란 무엇인가
**임베딩(Embedding)**은 텍스트(단어, 문장, 문서)를 고정된 길이의 실수 벡터로 변환한 것입니다. 예를 들어 “고양이는 귀엽다"라는 문장은 [0.021, -0.384, 0.157, ..., 0.093]처럼 768차원 또는 1536차원의 숫자 배열이 됩니다.
핵심 아이디어는 의미가 비슷한 텍스트는 벡터 공간에서 가까운 위치에 놓인다는 것입니다. “강아지"와 “개"의 임베딩 벡터는 서로 가깝고, “강아지"와 “주식시장"의 벡터는 멀리 떨어집니다. 이는 임베딩 모델이 대규모 텍스트 코퍼스로 학습되면서 단어/문장 간의 의미적 관계를 벡터 공간의 기하학적 거리로 인코딩하도록 훈련되었기 때문입니다.
RAG에서 임베딩이 하는 역할은 명확합니다 — 사용자 질문과 문서 청크를 같은 벡터 공간에 투영한 뒤, “질문 벡터와 가장 가까운 청크 벡터들"을 찾는 것이 곧 “질문과 가장 관련 있는 문서를 찾는 것"이 됩니다. 이것이 키워드 매칭이 아니라 **의미 기반 검색(semantic search)**이 가능한 이유입니다.
💡 실무 팁: 임베딩 모델은 한 번 선택하면 전체 지식베이스를 그 모델로 재임베딩하기 전까지는 바꾸기 어렵습니다. 서로 다른 임베딩 모델이 만든 벡터는 같은 벡터 공간에 있지 않으므로 절대 섞어 쓰면 안 됩니다.
2. 임베딩 모델 선택 기준
| 기준 | 설명 |
|---|---|
| 차원 수 (dimension) | 벡터 길이. 클수록 표현력은 높아지지만 저장 공간과 검색 연산 비용이 증가 |
| 도메인 적합성 | 법률/의료/코드 등 특수 도메인은 범용 모델보다 도메인 특화 모델이 유리할 수 있음 |
| 최대 입력 토큰 | 청크 크기가 이 한도를 넘으면 잘리거나 에러가 발생 |
| 다국어 지원 | 한국어 등 비영어 콘텐츠를 다룬다면 다국어 학습 모델인지 확인 필수 |
| 비용/지연시간 | API 호출형(OpenAI, Cohere) vs 로컬 오픈소스(BGE, E5) — 대량 문서 임베딩 시 비용 차이가 큼 |
| 벤치마크 성능 | MTEB(Massive Text Embedding Benchmark) 리더보드로 검색/분류 태스크 성능 비교 |
대표 모델 비교:
| 모델 | 차원 | 특징 |
|---|---|---|
OpenAI text-embedding-3-small | 1536 (축소 가능) | 저비용, 준수한 범용 성능, API 호출형 |
OpenAI text-embedding-3-large | 3072 (축소 가능) | 고성능, 비용/저장공간 증가 |
Cohere embed-v3 | 1024 | 멀티링구얼 강점, 검색 특화 |
BAAI bge-m3 (오픈소스) | 1024 | 로컬 실행 가능, 다국어/장문 지원, 무료 |
text-embedding-ada-002 (구형) | 1536 | 레거시, 신규 프로젝트에는 비권장 |
OpenAI의 text-embedding-3 계열은 dimensions 파라미터로 벡터 차원을 축소할 수 있습니다 — 성능을 약간 희생하고 저장 공간과 검색 속도를 확보하는 트레이드오프입니다.
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
model="text-embedding-3-small",
input="RAG 파이프라인에서 임베딩의 역할",
dimensions=512, # 기본 1536에서 축소
)
vector = response.data[0].embedding
💡 실무 팁: 벤치마크 순위만 보고 고르지 말고, 실제 사용할 문서 도메인과 유사한 데이터로 직접 검색 정확도를 측정하세요. 범용 벤치마크 1위 모델이 특정 도메인(예: 사내 법무 문서)에서는 하위권 모델보다 못할 수 있습니다.
3. 유사도 측정 방법: 코사인 vs 내적 vs 유클리드
두 벡터가 “얼마나 가까운가"를 측정하는 방법은 세 가지가 대표적입니다.
코사인 유사도(Cosine Similarity)
두 벡터 사이의 각도만 비교하며, 벡터의 크기(길이)는 무시합니다.
cosine_similarity(A, B) = (A · B) / (‖A‖ × ‖B‖)
값의 범위는 -1(정반대) ~ 1(완전히 동일한 방향)이며, 텍스트 임베딩에서 가장 널리 쓰입니다. 문서 길이에 따라 벡터 크기가 달라져도 “방향(의미)“만 비교하기 때문에 텍스트 유사도 측정에 적합합니다.
내적(Dot Product)
dot_product(A, B) = A · B = Σ(A_i × B_i)
벡터 크기까지 반영합니다. 임베딩 모델이 이미 벡터를 정규화(unit norm)해서 출력하는 경우, 내적과 코사인 유사도는 수학적으로 동일한 순위를 만들어냅니다 — 이 경우 정규화 연산을 생략할 수 있어 계산이 더 빠릅니다. OpenAI 임베딩은 이미 정규화되어 있어 내적을 써도 무방합니다.
유클리드 거리(Euclidean / L2 Distance)
euclidean(A, B) = sqrt(Σ(A_i - B_i)²)
벡터 공간에서의 “직선 거리"입니다. 값이 작을수록 유사합니다. 정규화된 벡터에서는 코사인 유사도와 단조적으로(monotonically) 연관되지만, 정규화되지 않은 벡터에서는 크기 차이가 결과를 왜곡할 수 있습니다.
| 지표 | 벡터 크기 영향 | 계산 비용 | 주 사용처 |
|---|---|---|---|
| 코사인 유사도 | 무시 | 중간(정규화 필요) | 텍스트 임베딩 검색 (기본 선택) |
| 내적 | 반영 | 가장 낮음 | 이미 정규화된 임베딩(OpenAI 등) |
| 유클리드 거리 | 반영 | 중간 | 이미지/좌표 기반 임베딩, 클러스터링 |
💡 실무 팁: 벡터 DB에 컬렉션을 생성할 때 거리 지표를 명시적으로 선택해야 합니다(Qdrant는
Distance.COSINE,Distance.DOT,Distance.EUCLID중 선택). 사용하는 임베딩 모델이 정규화된 벡터를 출력하는지 먼저 확인하고 지표를 고르세요.
4. 왜 정확한 k-NN은 스캔되지 않는가
가장 단순한 유사도 검색은 완전 탐색(Brute-force / Exact k-NN) 입니다 — 쿼리 벡터와 저장된 모든 벡터 사이의 거리를 하나하나 계산해 가장 가까운 k개를 찾는 방식입니다.
문제는 확장성입니다. 벡터가 N개, 차원이 D일 때 완전 탐색의 비용은 O(N × D)입니다. 문서 100만 개, 1536차원이라면 질문 하나당 15억 번의 연산이 필요합니다. 실시간 응답이 필요한 서비스에서 이는 감당할 수 없는 지연시간입니다.
**ANN(Approximate Nearest Neighbor)**은 “100% 정확한 최근접 이웃"을 포기하는 대신 “거의 정확하면서 훨씬 빠른” 검색을 제공합니다. 실무에서는 재현율(recall) 95~99% 수준이면 충분한 경우가 대부분이라 ANN이 표준입니다.
HNSW (Hierarchical Navigable Small World)
가장 널리 쓰이는 ANN 알고리즘. 벡터들을 여러 층(layer)으로 구성된 그래프로 연결합니다. 상위 레이어는 듬성듬성 연결된 “고속도로” 역할을 하고, 하위 레이어로 내려갈수록 촘촘하게 연결됩니다. 검색 시 상위 레이어에서 대략적인 위치를 빠르게 찾은 뒤 하위 레이어로 내려가며 점점 정밀하게 탐색합니다. Qdrant, Weaviate, Milvus 등 대부분의 현대 벡터 DB가 기본 인덱스로 채택합니다.
IVF (Inverted File Index)
전체 벡터 공간을 k-means 등으로 여러 개의 클러스터(cell)로 미리 나눠 둡니다. 검색 시 쿼리 벡터와 가까운 클러스터 몇 개만 골라 그 안에서만 완전 탐색을 수행합니다 — 탐색 범위를 전체가 아닌 일부 클러스터로 좁히는 방식입니다. FAISS에서 널리 쓰이며, IVF+PQ(Product Quantization)를 결합해 메모리 사용량까지 줄이는 경우가 많습니다.
| 알고리즘 | 검색 속도 | 정확도 | 메모리 사용 | 인덱스 구축 시간 |
|---|---|---|---|---|
| Exact (Brute-force) | 느림 | 100% | 낮음 | 없음 |
| HNSW | 매우 빠름 | 높음(튜닝 가능) | 높음 | 김 |
| IVF | 빠름 | 중~높음(튜닝 가능) | 중간 | 중간 |
💡 실무 팁: 데이터가 수만 건 이하로 작다면 완전 탐색으로도 충분히 빠릅니다. ANN 인덱스 튜닝(HNSW의
ef_construction,m파라미터 등)은 실제로 지연시간이 문제가 되는 규모에 도달했을 때 시작해도 늦지 않습니다.
📝 핵심 요약
- 임베딩은 텍스트를 의미가 비슷할수록 벡터 공간에서 가까워지는 실수 벡터로 변환한 것이며, RAG의 의미 기반 검색을 가능하게 하는 핵심 요소다
- 임베딩 모델 선택은 차원 수, 도메인 적합성, 비용, 다국어 지원을 종합적으로 고려해야 하며 한 번 선택하면 전체 재임베딩 없이는 교체가 어렵다
- 코사인 유사도는 벡터 크기를 무시하고 방향만 비교하며, 정규화된 벡터에서는 내적으로 대체해 계산 비용을 줄일 수 있다
- 완전 탐색 k-NN은 데이터가 커질수록 O(N×D) 비용이 감당 불가능해지므로, HNSW/IVF 같은 ANN 알고리즘으로 속도와 정확도를 트레이드오프한다
🔗 참고 자료
⬅️ 이전: Day 18 — RAG 개념과 Chunking 전략 | 다음: Day 20 — Hybrid Search & Reranker ➡️