AI/Natural language processing

[NLP] 임베딩 / 임베딩 차원 / 임베딩 모델 최대 토큰길이

hjjummy 2025. 11. 13. 18:02
“임베딩 차원이 높을수록 무조건 좋은가?”
“청크 길이는 얼마나 줘야 해? 그냥 길게 하면 좋은 거 아닌가?”

 

RAG를 처음 만들 때  고민하게 되는 점들인데, 차원 수(d), 최대 토큰 길이(context length), 청크 사이즈(chunk size) 세 가지가 서로 물려 돌아가는 구조라는 걸 몸으로 깨닫게 되었다.

 

이번 글은 그 경험을 바탕으로
차원 수 선택법 → 메모리/속도 트레이드오프 → 임베딩 모델의 최대 길이 → 청크 사이즈 설계법까지 전체를 하나로 정리해본 내용이다.


1. 임베딩(Embedding)이란?

우리가 사용하는 자연어 문장은 기계 입장에서 보면 그저 문자열일 뿐이다.
하지만 AI 모델은 문자열로는 의미를 계산할 수 없다.
그래서 문장 하나를 숫자 벡터로 바꿔주는데, 이 변환 과정을 임베딩이라고 부른다.

즉, 임베딩은:

  • 문장 → 숫자 벡터
  • 단어/문단/문서의 의미를 숫자로 표현하는 방식
  • 검색·추천·분류에서 공통적으로 쓰이는 핵심 기술

이라고 이해하면 된다.


2. 그렇다면 임베딩 차원(d)은?

임베딩 차원은 벡터의 길이다.

예를 들어 모델이 다음과 같다고 해보자

  • 384차원 모델
  • 768차원 모델
  • 1024차원 모델

이 모델들은 모두 같은 문장을 입력받지만, 이를 표현할 때 사용하는 숫자의 개수가 다르다.

“사과는 과일이다.” 
→ [0.12, -0.04, 0.22, ...]  (길이 384)
→ [0.91, -0.11, 0.03, 0.25, 0.44, 0.42 ...]  (길이 768)

즉,

임베딩 차원(d) = 한 문장을 표현하기 위해 사용하는 숫자의 개수 = 문장을 벡터로 표현할 때 사용할 수 있는 ‘의미 저장 공간의 크기’

 

한 청크를 d차원 실수 벡터로 매핑한 결과의 길이,  이게 바로 임베딩 차원이다.

이 d는 모델이 학습 중 의미를 담을 수 있는 용량이다.


3. 차원이 다르면 의미 표현 방식이 달라질까?

임베딩 모델은 차원이 높아질수록 더 많은 정보 축(axis)을 사용할 수 있다.
그래서 직관적으로는 이렇게 이해하면 편하다.

✔ 차원이 낮으면 (256~384)

  • 덜 세밀한 의미 표현
  • 더 압축된 정보 -> 가벼움 -> 계산·저장이 빠름

✔ 차원이 높으면 (768~1024+)

  • 미세한 의미 차이를 더 잘 구분
  • 비슷하지만 다른 문장들을 더 정확히 분리
  • 대신 메모리가 많이 들고 계산량이 증가

그렇다고 “차원이 큰 게 무조건 좋다”는 것도 아니다. 차원이 올라가면 다음 부작용도 함께 따라온다.

  • 벡터 저장 용량이 그대로 증가
  • 검색 시 연산량 증가
  • (진짜 중요) 허브니스(Hubness): 특정 벡터가 너무 자주 Top-k로 등장

그래서 차원은 무작정 높게 쓰는 게 아니라,
데이터 양 / 질 / 문서 길이 / 성능 요구사항에 따라 조절해야 한다.

 

이 기본 개념 위에서
“청크 길이”,
“검색 성능”,
“메모리·속도 트레이드오프”가 모두 이어진다.

 

이 부분은 뒤에서 더 깊게 설명할 예정이다.


4. 임베딩 차원은 모델이 이미 정해 놓는다

그러면 임베딩 차원을 내가 마음대로 설정 할 수 있는가? 답은 X..

사용자가 차원을 직접 정하는 게 아니라, 임베딩 모델이 학습될 때 이미 구조가 정해진다. 그래서 모델별로 사용해야하는 임베딩 차원수가 정해져있다. 

예를 들어

  • bge-base → 768차원
  • bge-small → 384차원
  • E5-base → 768차원
  • MiniLM-L6 → 384차원
  • OpenAI text-embedding-3-small → 1536차원
  • text-embedding-3-large → 3072차원

그래서 차원을 고려하지 않고 임베딩 모델만 변경하면 에러나는 원인이 바로 이것이다.

그래서 RAG 구축 시 첫 번째 체크는 “우리 모델의 임베딩 차원이 정확히 몇인가?” 이다.

 

5. 임베딩 모델의  최대 토큰 길이(max_length)

임베딩 차원이 무엇인지 감이 잡혔다면, 그다음으로 꼭 알아야 하는 게 하나 있다. 

임베딩 모델은 한 번에 넣을 수 있는 문서 길이, 즉 max_length가 정해져 있다.
생각보다 많은 사람들이 여기서 실수를 하는데, 바로 "임베딩 모델마다 처리할 수 있는 최대 토큰 길이가 다르다”는 점이다. 

 

모델  최대 길이
bge-base / large 512 tokens
E5-base / large 512 tokens
SBERT 계열 512 tokens
OpenAI text-embedding-3 계열 8192 tokens

 

이 차이가 왜 중요하냐면,
문서가 모델이 허용하는 최대 길이를 넘어가면 뒤가 통째로 잘려(truncation) 의미가 사라진다.
그리고 검색 품질은 자연스럽게 흔들리게 된다.

실제로 청크를 700~900 토큰으로 잘라놓고 “RAG 품질이 안 좋아요”라고 말하는 경우가 많은데,
대부분은 모델이 512 이상을 애초에 못 먹는다는 사실을 몰랐던 것에서 발생한다.


6. 청크 사이즈 정하기 -> max_length × 0.7~0.8

max_length=512인 모델을 쓴다면 청크는 320~380 tokens 정도로 잘라야 한다.
그 이상을 욕심내면 토크나이저 특성에 따라 금방 500을 넘겨서 잘린다.

청크를 짧게 잡아야 하는 이유는 다음과 같다:

  • 한국어는 토큰화 효율이 낮아 길이가 금방 늘어남
  • 마크다운/공백/HTML 등 메타데이터로 토큰 수 급등
  • 잘린 순간 의미 정보가 손실됨
  • 임베딩의 품질이 곧 검색 품질로 직결됨

그래서 실무에서는 거의 공식처럼 다음을 적용한다.

 
chunk_size = max_length × 0.7~0.8

반대로 GPT 임베딩처럼 8k를 지원하면
청크를 5000~6000 tokens까지 길게 가져가도 전혀 문제가 없다.
긴 문서 RAG에서 압도적인 성능 차이가 발생하는 이유도 여기 있다.


7. 차원(d)과 청크 길이 함께 고려!

임베딩 차원(d)은 “문장을 얼마나 정밀하게 담는가”,
최대 길이(max_length)는 “문장을 얼마나 길게 담는가”를 결정하는 요소다.

따라서 실무에서는 이 둘을 항상 묶어서 조정한다.

예를 들어보자.

📌 시나리오 A — 한국어 문단 중심 RAG

  • 문서가 길고 의미 밀도 높음
    → 768차원 + 청크 320~380 tokens (512 모델 기준)
    이 조합이 가장 안정적이다.

📌 시나리오 B — FAQ/고객센터처럼 짧은 텍스트

  • 문장 길이가 짧아 차원 과투자 불필요
    → 384~512 차원 + 청크 150~200 tokens

📌 시나리오 C — 수백만 문서 + 응답시간 엄격

  • 속도와 메모리가 가장 중요
    → 384차원 + 청크 250~350 tokens
    → 필요하면 Cross-Encoder로 재랭킹

📌 시나리오 D — OpenAI 8k embedding 사용

  • 청크 길이를 5000~6000까지 확장 가능
    → 긴 문서 검색 정확도 대폭 증가

이걸 정리하면:

문서가 길면 차원을 크게, 문서가 짧으면 차원을 작게
모델의 max_length 안에서 청크를 최대한 효율적으로 자르는 전략이 필요

이라는 공식이 나온다.


허브니스(Hubness)

고차원 공간에서는 일부 벡터가 이유 없이 다양한 문장과 “비슷한 것처럼” 느껴지는 현상이 발생한다.
이게 바로 허브니스다.

증상은 간단하다:

  • 특정 문서가 검색 결과 Top-k에 지나치게 자주 등장한다
  • 문서 간 거리 분포가 비정상적으로 좁아진다

그리고 해결책도 이미 정리가 잘 되어 있다.

✔ 허브니스 완화법

  • L2 정규화 (가장 기본)
  • PCA/OPQ 등 차원 축소
  • Mutual Proximity / Local Scaling
  • ANN → Cross-Encoder 2단계 검색

특히 Cross-Encoder 기반 Rerank는
한국어 RAG에서 확실히 품질 안정화 효과가 있다.


모델 교체 시 가장 흔한 두 가지 실수

❌ 1) Elasticsearch dense_vector dims를 모델 차원과 다르게 설정

  • 모델은 768
  • ES는 384로 설정되어 있음
    → 즉시 색인 실패

❌ 2) 모델 max_length보다 훨씬 긴 청크를 사용

  • 모델 제한: 512 tokens
  • 청크: 650 tokens
    → 뒷부분 날아감 → 검색 품질 급락

둘 다 정말 자주 발생하는 실수라서
임베딩 파이프라인을 만들 때 가장 먼저 체크해야 한다.


임베딩 설계 체크리스트

마지막으로 실제 프로젝트에서 내가 가장 많이 사용하는 규칙들을 정리하면 다음과 같다.

✔ 1) 모델 max_length 먼저 확인

가장 중요한 출발점이다.

  • 대부분 512
  • OpenAI는 8192

✔ 2) 청크 길이 = max_length × 0.7~0.8

  • 512 → 350 tokens
  • 8192 → 5500~6000 tokens

✔ 3) 차원(d)은 문서 길이와 도메인 복잡도에 따라 선택

  • 긴 문서 RAG: 768
  • 짧은 텍스트: 384
  • 대규모 인덱스: 384~512
  • Rerank 사용 예정이면 차원 낮아도 OK

✔ 4) 임베딩은 무조건 L2 normalize

(코사인 안정화)

✔ 5) 필요하면 2단계 검색(ANN → Cross-Encoder Rerank)

특히 한국어 장문에 유효함.


마무리

많은 사람들이 차원(d)과 청크 길이를 각각 독립된 요소로 생각한다.
하지만 실제로 좋은 RAG 시스템을 만들려면
이 둘을 하나의 세트로 보고 설계해야 한다.

  • 문장을 얼마나 촘촘하게 담을 것인가(차원)
  • 문서를 얼마나 길게 넣을 수 있는가(max_length)
  • 우리는 얼마나 잘게 쪼개서 넣을 것인가(chunking)

이 세 가지를 균형 있게 맞춰야
RAG의 검색 품질이 안정적으로 유지된다.

나 역시 이 관계를 제대로 이해한 뒤에야
RAG 품질이 흔들리지 않고 안정되는 느낌을 확실히 받았다.