7장. 자체 임베딩과 벡터DB, 검색 품질
임베딩 모델 고르기
신발을 살 때는 발 크기, 쓰임새, 무게, 값을 따집니다. 임베딩 모델도 우리 문서의 언어, PC 의 사양, 쓸 수 있는 범위에 맞춰 고릅니다.
네 카드가 임베딩 모델을 고르는 기준입니다. 한국어를 배운 모델인지, 내 PC 에서 돌릴 만한 크기인지, 벡터가 몇 차원이라 저장 공간이 얼마나 드는지, 회사에서 써도 되는 라이선스인지 봅니다. 여기서 발 크기가 언어, 신발 무게가 모델 크기입니다.
이 장에서 쓰는 모델
| 모델 | 하는 일 | 크기 | 차원 | 라이선스 |
|---|---|---|---|---|
intfloat/multilingual-e5-small | 임베딩(바이 인코더). 한국어 포함 100여 언어 | 파라미터 1억 1,800만, 파일 470MB 쯤 | 384 | MIT |
cross-encoder/mmarco-mMiniLMv2-L12-H384-v1 | 재정렬(크로스 인코더). 여러 언어 | 파라미터 1억 1,800만쯤 | 점수 하나 | Apache 2.0 |
이 PC 의 CPU 에서 임베딩은 조각 30개에 1초가 안 걸렸고, 재정렬은 질문 하나(후보 10개)에 0.2초쯤 걸렸습니다. 더 큰 모델(예: multilingual-e5-large, BAAI/bge-m3)은 검색이 나아질 수 있지만 몇 배 크고 느립니다. 공개된 순위표(MTEB 등)에서 한국어 점수를 참고하되, 결국 우리 문서와 질문으로 재서 고릅니다.
저장 공간 어림하기
임베딩 하나는 차원 × 4바이트(float32)입니다.
for dim in [384, 768, 1024]:
for n in [30, 100_000]:
mb = n * dim * 4 / 1024 ** 2 # 조각 수 × 차원 × 4바이트
print(f"차원 {dim:4d} · 조각 {n:7,d}개 → {mb:8.2f} MB")조각 10만 개라면 384차원은 146MB, 1024차원은 391MB 입니다. 이 장의 30개는 0.04MB 남짓이라 걱정할 것이 없습니다.
코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
100_000 | 십만이다. 밑줄은 읽기 쉽게 끊어 쓴 것이고 값에는 영향이 없다 |
n * dim * 4 | 조각 수 × 숫자 개수 × 숫자 하나의 바이트 수다. float32(32비트 실수)는 숫자 하나에 4바이트를 쓴다 |
/ 1024 ** 2 | 바이트를 MB(메가바이트)로 바꾼다. 1MB 를 1024 × 1024 바이트로 셈했다 |
f"{n:7,d}" | 정수를 7칸에 맞추고 세 자리마다 쉼표를 넣는다 |
f"{mb:8.2f}" | 실수를 8칸에 맞추고 소수 둘째 자리까지 적는다 |
이 코드가 뜻하는 것
이사할 때 "상자 수 × 상자 하나의 크기"로 트럭 크기를 어림하는 것과 같습니다. 여기서 상자가 조각 하나의 임베딩, 상자 크기가 "차원 × 4바이트"입니다.
| 셈 | 384차원 · 조각 10만 개 |
|---|---|
| 임베딩 하나 | 384 × 4 = 1,536바이트 |
| 조각 10만 개 | 1,536 × 100,000 = 153,600,000바이트 |
| MB 로 | 153,600,000 ÷ (1024 × 1024) = 약 146MB |
차원이 384 에서 1024 로 2.7배쯤 늘면 저장 공간도 똑같이 2.7배쯤 늡니다. 이 셈에는 원문과 메타데이터, 인덱스가 따로 쓰는 공간이 빠져 있으므로 실제로는 이보다 조금 더 듭니다.
주의할 점
- 모델 카드에 적힌 최대 길이(e5-small 은 512토큰)를 넘는 글은 잘려서 뒷부분이 임베딩에 들어가지 않습니다. 조각을 알맞게 나누는 까닭 가운데 하나입니다.
- 라이선스는 모델 카드의 표기를 확인합니다. 학습 데이터의 조건이 따로 붙는 모델도 있습니다.