본문 바로가기
AI2026년 8월 2일5분 읽기

임베딩 실전 — 정규화·차원 축소(Matryoshka)·유사도 함정

YS
김영삼
조회 8
임베딩 실전 — 정규화·차원 축소(Matryoshka)·유사도 함정

임베딩(embedding)은 텍스트를 고차원 벡터로 바꿔 "의미가 비슷하면 벡터도 가깝다"는 성질을 만드는 기술이다. RAG·시맨틱 검색·추천의 바닥에 깔린 부품이다. 개념 설명은 흔하니, 이 글에서는 실제로 붙일 때 발이 걸리는 지점 — 정규화, 차원 축소(Matryoshka), 유사도 함정 — 을 정리한다.

임베딩은 "모델 골라서 벡터 뽑으면 끝" 같아 보이지만, 실전에서는 사소한 설정 하나가 검색 품질을 통째로 망친다. 나도 코사인이랑 내적을 헷갈려 며칠 날린 적이 있다. 이런 걸 미리 알면 안 겪어도 될 삽질이다.

정규화와 유사도 함정

가장 먼저 짚을 것. 벡터를 비교하는 척도는 여럿이다. 코사인 유사도(방향만 봄), 내적(dot product)(방향+크기), 유클리드 거리(직선 거리). 여기서 사고가 난다.

임베딩 벡터를 L2 정규화(길이를 1로 맞춤)하면 코사인 유사도와 내적이 같아진다. 그런데 정규화하지 않은 벡터에 내적을 쓰면, 문서 길이나 벡터 크기가 큰 쪽이 부당하게 높은 점수를 받는다.

즉 "이 모델은 코사인을 쓰라"고 안내돼 있는데 벡터 DB 인덱스는 내적으로 설정해두면, 정규화 안 된 벡터에서 엉뚱한 결과가 나온다. 규칙은 단순하다.

  • 모델이 코사인을 권장 → 벡터를 L2 정규화하고 코사인(또는 정규화 후 내적) 사용
  • DB 인덱스의 거리 척도와 저장 벡터의 정규화 여부를 반드시 일치시킬 것
  • 질의 벡터와 문서 벡터에 같은 전처리·정규화 적용
import numpy as np
def l2_normalize(v):
    return v / (np.linalg.norm(v) + 1e-12)
# 정규화 후에는 내적 = 코사인. 인덱스도 동일 척도로 맞춘다.
doc_vecs = np.stack([l2_normalize(e) for e in raw_embeddings])

Matryoshka — 차원을 잘라 쓰기

임베딩 차원이 클수록(예: 1536, 3072) 표현력은 좋지만 저장·검색 비용이 커진다. Matryoshka 표현 학습(MRL)으로 훈련된 임베딩은 앞쪽 차원에 중요한 정보를 몰아넣도록 학습되어, 벡터의 앞부분만 잘라 써도 성능이 크게 안 떨어진다. 러시아 인형처럼 큰 벡터 안에 작은 벡터가 들어 있는 셈이다.

그래서 3072차원 임베딩을 768차원으로 잘라 저장하면 저장·검색 비용이 크게 줄면서도 정확도 손실은 작다. 다만 자른 뒤에는 반드시 다시 L2 정규화해야 한다 — 부분 벡터는 길이가 1이 아니기 때문이다. 이 재정규화를 빼먹으면 유사도가 미묘하게 틀어진다.

차원저장 비용정확도(대략)
3072(전체)가장 큼최고
10241/3 수준근소한 손실
256매우 작음체감되는 손실

대규모 인덱스에서 유용한 전략이 있다. 잘린 저차원으로 후보를 빠르게 넓게 뽑고(1차), 전체 차원으로 상위 후보만 정밀 재계산(2차)하는 것이다. 리랭킹과 같은 계단식 발상이다.

실전에서 더 챙길 것

몇 가지 자잘하지만 중요한 것들.

  • 비대칭 임베딩: 어떤 모델은 질의와 문서에 서로 다른 프리픽스/프롬프트를 붙이도록 설계돼 있다(예: "query:", "passage:"). 이걸 빼먹으면 성능이 눈에 띄게 떨어진다.
  • 모델 버전 고정: 임베딩 모델을 바꾸면 벡터 공간이 통째로 바뀐다. 기존 인덱스와 새 질의 벡터를 섞어 쓰면 안 된다 — 전체 재색인이 필요하다.
  • 청크 길이: 임베딩 모델에는 입력 토큰 한도가 있다. 청크가 너무 길면 뒷부분이 잘려 벡터가 앞부분에만 치우친다.

특히 모델 교체 시 재색인은 실수하기 쉽다. 나는 임베딩 모델을 업그레이드하면서 인덱스를 안 갈아엎어, 옛 벡터와 새 질의가 다른 공간에서 비교되는 바람에 검색이 조용히 망가진 적이 있다. 로그에 에러도 안 뜨고 그냥 결과가 이상해져서 원인 찾는 데 한참 걸렸다.

자주 묻는 질문

코사인과 내적 중 뭘 써야 하나요?

모델 문서의 권장 척도를 따르는 게 첫째다. 대부분 코사인을 권장하며, 이 경우 벡터를 L2 정규화하면 내적과 코사인이 동일해져 어느 쪽을 인덱스 척도로 잡아도 결과가 같다. 중요한 건 저장 벡터의 정규화 여부와 인덱스 척도를 일치시키는 것이다.

Matryoshka로 차원을 얼마나 줄여도 되나요?

도메인과 데이터 규모에 따라 다르므로 평가셋으로 확인해야 한다. 흔히 절반 정도까지는 손실이 작고, 그 아래로 내려가면 서서히 정확도가 떨어진다. 저차원으로 후보를 뽑고 전체 차원으로 재정렬하는 계단식 구성을 쓰면, 비용을 줄이면서 정확도 손실을 상당히 만회할 수 있다.

임베딩 모델을 바꾸면 뭘 해야 하나요?

벡터 공간이 달라지므로 전체 문서를 새 모델로 다시 임베딩해 재색인해야 한다. 옛 벡터와 새 질의 벡터를 섞으면 유사도가 무의미해진다. 무중단이 필요하면 새 인덱스를 병렬로 구축한 뒤 스위치하는 방식을 쓴다.

질의와 문서를 같은 방식으로 임베딩해야 하나요?

기본은 그렇지만, 일부 모델은 질의와 문서에 서로 다른 프리픽스나 지시를 붙이도록 설계돼 있다(비대칭). 그런 모델은 안내대로 각각의 프리픽스를 붙여야 제 성능이 난다. 모델별 사용법을 확인하고, 정한 전처리를 색인과 질의 양쪽에 일관되게 적용하는 게 핵심이다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록