6.2 텍스트 벡터화 실습 (문서, 문장, 문단)
본 절에서는 OpenAI의 Embedding API를 활용하여 텍스트 데이터를 벡터로 변환(embedding)하는 실습을 수행합니다. "텍스트 벡터화"란 자연어를 다차원 수치 벡터로 변환함으로써 기계가 의미적으로 이해하고 비교할 수 있도록 만드는 처리 과정입니다. 이 절의 목표는 문서, 문단, 문장 단위로 텍스트를 벡터화하고, 이를 통해 검색엔진, 추천 시스템 또는 의미 유사도 계산에 적용 가능한 기반 데이터를 구축하는 것입니다.
6.2.1 Embedding이란?
"Embedding"은 고차원에서 문장의 의미나 컨텍스트를 표현하는 수치 벡터(예: 1536차원 벡터)입니다. 같은 의미의 문장은 비슷한 벡터 값을 가지며, 유사도 기반 검색(cosine similarity 등)에 매우 유용합니다.
Embedding의 예시
"푸바오의 나이는 몇 살이야?" 와 "푸바오가 태어난 지 얼마나 됐어?" 는 의미가 유사하므로 유사한 벡터를 가집니다.
반면, "내일 날씨 어때?" 와는 의미가 다르므로 수치 공간상 멀리 떨어져 있습니다.
OpenAI의 embedding 모델(예: text-embedding-ada-002)은 일반적인 문장 의미 표현에 뛰어난 성능을 보입니다.
사전 준비: OpenAI Python SDK 설치
pip install openai
API 키는 다음처럼 환경변수로 등록해 두는 것이 좋습니다:
export OPENAI_API_KEY="sk-xxxxx"
기본 Embedding 코드 예제
다음은 문장 하나를 벡터로 변환하는 기본 예제입니다:
import openai
openai.api_key = "YOUR_API_KEY" # 또는 환경변수에서 가져오기
response = openai.Embedding.create(
input="푸바오는 매우 귀여운 아기 판다입니다.",
model="text-embedding-ada-002"
)
embedding_vector = response['data'][0]['embedding']
print(f"벡터 길이: {len(embedding_vector)}") # 일반적으로 1536 차원
💡 주의: input은 문자열 또는 문자열 리스트(list[str]) 모두 지원합니다.
6.2.4 문서, 문단, 문장 단위 벡터화 전략 비교
텍스트 벡터화를 수행할 때 어떤 단위로 분할해서 벡터화하느냐는 용도에 따라 달라집니다.
문서
법률 계약서 전체, 논문
전체 문서 유사도 비교, 문서 탐색
문단
기사 하나의 단락
주제 파악, 상세 컨텍스트 검색
문장
한 문장의 뉴스 타이틀
아주 정밀한 질의 응답, 미세한 의미 비교
문서 단위 임베딩은 정보 밀도가 낮고, 매우 긴 입력의 경우 토큰 제한(8191 또는 32768 토큰 제한 등)을 초과할 수 있습니다.
문장 단위는 너무 세분화되어 문맥이 단절될 수 있어 검색 성능이 저하될 수 있습니다.
가장 일반적인 선택은 “적절한 길이의 문단 단위 임베딩”입니다. (~512 tokens 추천)
6.2.5 텍스트 분할 및 벡터화 실습 과정
다음 실습은 긴 문서를 문단 단위로 분할한 후, 각각의 문단을 벡터화하고 리스트 형태로 저장합니다.
준비 데이터:
예를 들어, 한국어 뉴스 기사 본문 1개를 사용.
news_text = """
푸바오는 한국에서 태어난 첫 번째 판다 아기입니다. 에버랜드 동물원에서 태어나고 자라며 많은 관심과 사랑을 받고 있습니다.
푸바오의 부모는 아이바오와 러바오로, 중국 쓰촨성에서 한국으로 온 자이언트 판다 부부입니다. 푸바오는 태어난 지 3년이 되어가며, 최근 젖을 떼고 독립 생활도 시작했습니다.
에버랜드 측은 푸바오의 활발한 움직임과 건강한 성장에 대해 긍정적인 평가를 내놓고 있습니다. 관람객들의 방문 수가 크게 증가한 데에도 푸바오의 역할이 컸다는 분석입니다.
"""
Step 1: 문단 단위 분리 (간단한 줄바꿈 기준)
paragraphs = [p.strip() for p in news_text.strip().split('\n') if p.strip()]
print(paragraphs)
Step 2: OpenAI Embedding API 호출 및 벡터화
response = openai.Embedding.create(
input=paragraphs,
model="text-embedding-ada-002"
)
vectors = [item['embedding'] for item in response['data']]
Step 3: 문단-벡터 딕셔너리로 저장
paragraph_embeddings = list(zip(paragraphs, vectors))
# 예시 출력
for para, vec in paragraph_embeddings:
print(f"문단: {para[:30]}... → 벡터 길이: {len(vec)}")
6.2.6 일괄 벡터화 시 주의사항
1회 요청당 최대 input 길이는 약 8191 tokens 또는 요청 수에 따라 제한됩니다.
성능 최적화를 위해 list[str] 형태로 batch 처리하세요.
중복 데이터 벡터화 방지는 캐싱 전략을 사용하세요.
예: 해시값 기반 캐시
import hashlib
def get_text_hash(text: str):
return hashlib.md5(text.encode('utf-8')).hexdigest()
6.2.7 고급 팁: SentencePiece, Tokenizer 기반 분할
보다 현명한 분할을 위해 cl100k_base tokenizer를 활용할 수 있습니다. 이를 통해 문단의 토큰 수를 확인하고 512 토큰 미만으로 분할하는 전략을 구현합니다.
import tiktoken
tokenizer = tiktoken.get_encoding("cl100k_base")
def count_tokens(text):
return len(tokenizer.encode(text))
# 토큰 측정
for para in paragraphs:
print(f"{para[:30]}... → {count_tokens(para)} tokens")
이 정보를 활용해 너무 긴 문단은 문장 단위로 재분할하거나 요약 후 벡터화하세요.
6.2.8 벡터 저장 및 향후 검색을 위한 포맷
향후 cosine similarity 검색 등을 위해 구축한 벡터는 numpy array나 faiss에 저장합니다.
import numpy as np
vector_store = {
get_text_hash(text): {
"text": text,
"embedding": np.array(embedding)
}
for text, embedding in paragraph_embeddings
}
또는 다음 장의 FAISS/Pinecone/Weaviate에 연결하여 검색 API로 활용할 수도 있습니다.
마무리 요약
Embedding API는 자연어 텍스트를 의미 벡터로 변환해주는 핵심 도구입니다.
문서, 문단, 문장 단위로 다양한 수준의 의미 검색을 구성할 수 있습니다.
문단 단위 (약 100
300단어, 300500 tokens)가 가장 이상적인 벡터화 단위입니다.tiktoken 등 tokenizer를 활용하면 토큰 기반 최적화를 실현할 수 있습니다.
결과 벡터는 검색엔진 구축(Faiss 등)에 연계하기 위해 key-value 딕셔너리나 벡터DB 포맷으로 관리하세요.
다음 절(6.3)에서는 이렇게 생성한 임베딩 벡터를 FAISS와 같은 벡터 검색 엔진과 연동하는 방법을 알아봅니다.
Last updated