* 이 게시물은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
(RAG 시리즈 ③) 임베딩 고르기
검색이 안 좋을 때 사람들이 제일 나중에 의심하는 게 임베딩 모델이다. 나도 그랬다. 청크도 다시 잘라보고 검색 개수도 바꿔보다가, 결국 범인은 임베딩이었던 적이 있다.
임베딩은 문장을 수백~수천 차원짜리 벡터 하나로 바꿔주는 모델이다. 의미가 비슷한 문장은 벡터 공간에서 가까이 놓인다. 그 “가까움”을 보통 코사인 유사도로 재는데, 대부분의 벡터 DB가 코사인 기준이라 임베딩을 정규화(normalize)해서 넣는 게 보통이다. 어쨌든 이 모델이 한국어 의미를 얼마나 잘 잡느냐가 검색 품질을 그대로 결정한다. 문제는, 영어 리더보드 상단에 있는 모델이 한국어에선 의외로 헤매는 경우가 있다는 거다.
그래서 나는 후보를 그냥 내 문서로 붙여보고 고른다. MTEB 같은 벤치마크의 한국어 항목을 출발점으로는 참고하되, 최종 판단은 실제 우리 데이터로 한다. 검색 정답 셋을 몇십 개만 만들어 두고 Recall을 재보면 승부가 금방 난다.
유사도 자체는 코사인 한 줄이다. 중요한 건 색인과 검색에 같은 임베딩 모델을 쓰는 것.
import numpy as np
def cosine(a, b):
a, b = np.array(a), np.array(b)
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
q = embed("반품 규정 알려줘") # 검색도 색인과 같은 모델로
ranked = sorted(chunks, key=lambda c: cosine(q, c.vec), reverse=True)
top = ranked[:5]
선택지는 대략 이렇게 갈린다.
- 상용 API — Voyage AI(
voyage-3계열), OpenAI(text-embedding-3-small/large). 품질 좋고 관리 편하다. OpenAI 쪽은 차원을 줄여 받는 옵션이 있어 저장·속도를 아낄 수 있다. - 오픈소스 다국어 — BGE-M3(긴 문서와 다국어에 강하고 밀집·희소 검색을 한 모델로 뽑아줌), multilingual-e5. 직접 서빙하면 호출 비용이 0에 데이터가 밖으로 안 나간다.
- 한국어 특화 — ko-sroberta 등. 다국어 모델이 워낙 좋아져서 요즘은 꼭 이긴다고 장담 못 한다. 그래서 비교가 답이다.
참고로 Claude를 생성 쪽에 쓴다고 임베딩까지 Anthropic 걸 쓰는 게 아니다. Anthropic은 임베딩 API를 따로 안 내놓고 공식 문서에서 Voyage를 권한다. 임베딩과 생성은 그냥 다른 회사 걸 섞어 쓰는 게 보통이다.
차원 얘기를 조금 하면, 차원이 크면 표현력이 좋은 대신 저장 용량과 검색 시간이 늘어난다. 요즘 모델 중엔 마트료시카(Matryoshka) 방식으로 한 벡터를 앞에서 잘라 써도 성능이 크게 안 떨어지게 학습된 것들이 있어서, 정확도와 비용 사이를 차원으로 조절할 수 있다. 문서가 수백만 건이면 이게 은근히 크게 먹힌다.
승부를 내는 법 — 30분짜리 Recall 측정
“내 문서로 붙여보고 고른다”의 실체는 이 정도 코드다. 정답 셋 몇십 개(질문과, 그 답이 들어 있는 문서 id)만 만들면 후보 모델들의 승부가 숫자로 난다.
testset = [("환불 규정이 어떻게 되나요?", "doc-041"), ...] # 30~50개면 충분
for model in ["bge-m3", "multilingual-e5-large", "voyage-3"]:
emb = load_embedder(model)
index = build_index(docs, emb)
hit = sum(1 for q, gold in testset
if gold in [d.id for d in index.search(emb(q), k=5)])
print(f"{model}: Recall@5 = {hit}/{len(testset)}")
포인트 둘. ① 정답 셋은 실제 사용자가 물을 법한 문장으로 — 문서 제목을 그대로 베낀 질문은 어떤 모델이든 잘 맞혀서 변별력이 없다. ② 모델을 바꾸면 전체 재색인이므로, 이 측정은 데이터가 커지기 전에 해두는 게 싸다.
쿼리와 문서는 같은 모델로
당연하지만 자주 놓치는 것 — 문서를 넣을 때와 질문으로 검색할 때 반드시 같은 임베딩 모델을 쓴다. 모델이 다르면 벡터 공간이 달라 비교가 무의미하다. 또 일부 모델은 질문용/문서용 프롬프트 접두어(query:, passage:)를 요구하니 모델 문서를 봐야 한다. 임베딩 모델을 바꾸면 전체 재색인이 필요하다는 점도 기억하자. 이렇게 고른 임베딩 위에 하이브리드 검색(4편)을 얹으면 한국어 검색이 한결 단단해진다.
마지막으로 제일 자주 하는 실수 하나. 색인할 때 쓴 임베딩 모델이랑 검색할 때 쓴 모델이 다르면 안 된다. 벡터 공간이 아예 어긋나서 검색이 통째로 무의미해진다. 모델을 바꿀 거면 전체를 다시 인덱싱해야 한다. 이거 때문에 한나절 날린 적 있다.
자주 묻는 질문 (FAQ)
한국어는 어떤 임베딩 모델이 좋나요?
범용 다국어 모델보다 한국어에 강한 최신 모델이 유리한 경우가 많습니다. 다만 절대 강자는 없으니, 내 실제 데이터로 검색 품질을 비교(리트리벌 평가)해 고르는 것이 가장 확실합니다.
OpenAI 임베딩이면 한국어도 충분한가요?
쓸 만하지만 도메인 용어나 한국어 뉘앙스에서는 한계가 보일 수 있습니다. 성능뿐 아니라 비용·프라이버시(로컬 실행 여부)까지 함께 보고 결정하세요.

답글 남기기