초급 과정으로 돌아가기
Chapter 3: 청킹 전략의 모든 것
문서를 AI가 이해하기 쉽게 나누는 기술
3.1 청킹이 중요한 이유
임베딩과 검색 품질의 핵심
🎯 청킹의 목적
- •의미적 완결성: 하나의 청크가 독립적인 의미를 가져야 함
- •검색 효율성: 적절한 크기로 정확한 정보 검색
- •컨텍스트 보존: 문맥 정보가 손실되지 않도록
⚠️ 잘못된 청킹의 문제
- •너무 작은 청크: 문맥 손실, 의미 파편화
- •너무 큰 청크: 부정확한 검색, 노이즈 증가
- •일관성 없음: 검색 품질 편차 발생
3.2 주요 청킹 전략
1. 고정 크기 청킹 (Fixed Size Chunking)
설정 예시
chunk_size: 1000
overlap: 200장점
- • 구현 간단
- • 예측 가능한 크기
- • 빠른 처리 속도
단점
- • 문맥 단절 가능
- • 의미 단위 무시
- • 문장 중간 절단
시각적 예시:
[0-1000자]
[800-1800자]
[1600-2600자]
→ 200자 중첩2. 의미 단위 청킹 (Semantic Chunking)
작동 원리:
1
문단별 임베딩 생성
각 문단을 벡터로 변환
2
유사도 계산
인접 문단 간 코사인 유사도 측정
3
경계 결정
유사도가 낮은 지점에서 분할
✅ 추천 상황
- • 기술 문서, 매뉴얼
- • 논문, 보고서
- • 구조화된 콘텐츠
⚡ 성능 팁
- • 임계값: 0.7-0.8 권장
- • 최소 청크: 200자 이상
- • 최대 청크: 1500자 이하
3. 문서 구조 기반 청킹
활용 가능한 구조 요소:
Markdown
# ## ### 헤더
# ## ### 헤더
HTML
<h1> <p> <div>
<h1> <p> <div>
LaTeX
\section \chapter
\section \chapter
PDF
페이지, 섹션
페이지, 섹션
💡 프로 팁
문서 구조와 의미적 청킹을 하이브리드로 사용하면 최상의 결과를 얻을 수 있습니다. 예: 섹션 단위로 먼저 나누고, 각 섹션 내에서 의미적 청킹 적용
청킹 베스트 프랙티스
📏 크기 가이드라인
- 일반 문서: 500-1000자
- 기술 문서: 1000-1500자
- 대화형: 300-500자
- 법률/의학: 1500-2000자
🔄 중첩(Overlap) 설정
- 표준: 10-20%
- 높은 정밀도: 25-30%
- 빠른 처리: 5-10%
- 0% 중첩: 권장하지 않음
🎯 성능 최적화
- 청크별 메타데이터 추가
- 문서 타입별 전략 차별화
- A/B 테스트로 최적값 찾기
- 사용자 피드백 반영
🔥 실전 코드: 스마트 청킹 구현
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.text_splitter import CharacterTextSplitter
# 1. 기본 고정 크기 청킹
def basic_chunking(text):
splitter = CharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separator="\n"
)
return splitter.split_text(text)
# 2. 재귀적 문자 분할 (추천!)
def smart_chunking(text):
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ". ", " ", ""],
length_function=len,
)
return splitter.split_text(text)
# 3. 의미적 청킹 (고급)
def semantic_chunking(text, embeddings):
# 문단별로 분할
paragraphs = text.split("\n\n")
# 각 문단 임베딩
embeddings_list = [embeddings.embed_query(p) for p in paragraphs]
# 유사도 계산 및 병합
chunks = []
current_chunk = paragraphs[0]
for i in range(1, len(paragraphs)):
similarity = cosine_similarity(
embeddings_list[i-1],
embeddings_list[i]
)
if similarity > 0.7: # 유사하면 병합
current_chunk += "\n\n" + paragraphs[i]
else: # 다르면 새 청크 시작
chunks.append(current_chunk)
current_chunk = paragraphs[i]
chunks.append(current_chunk)
return chunks