YYYEJI

[MAC] 데이터 로더 후 텍스트 분할(Text Splitting) 하는 과정 본문

인공지능 챗봇

[MAC] 데이터 로더 후 텍스트 분할(Text Splitting) 하는 과정

YEJI ⍢ 2025. 8. 5. 10:53
728x90

안녕하세요! 송로지입니다 🌷

 

 

어제는 데이터를 로더하는 방법에 대해 알아보았는데요 :)

 

[MAC] RAG 기술 사용할 때 웹 문서(WebBaseLoader), CSV(CSVLoader), PDF 파일(PyPDFLoader) 로더하는 방법

↓↓↓ RAG 기본 개념 관련해서 살펴보기 ↓↓↓ RAG 개념 알아보기RAG란 무엇일까요??Retrieval-Augmented Geteration의 줄임말이며,LLM의 한계를 보완하기 위해 외부 지식 베이스에서 정보를 검색(Retrieval)

yyyeji.tistory.com

오늘은 가져온 데이터들을 어떻게 분할할건지 정리해보겠습니다!! 🤩

 

 

텍스트 분할은 대규모 텍스트 문서를 처리할 때 매우 중요한 전처리 단계라고 할 수 있는데요!

문서의 구조와 형식, 원하는 청크 크기, 문맥 보존의 중요도, 처리 속도를 고려해야 됩니다.

 

 

우선 pdf파일의 데이터들을 로더하고 데이터를 분할해보도록 하겠습니다!

(데이터 로더는 위 사이트를 참고해주세요 ><)

 


1. CharacterTextSplitter

이 함수는 가장 기본적인 분할 방식입니다!

문자 수를 기준으로 텍스트를 분할하게 되는데 문맥을 고려하지 않는다는 단점이 존재해요,,

설치는 pip install langchain_text_splitters 또는 uv add langchain_text_splitters로 하시면 됩니다 ㅎ

from langchain_text_splitters import CharacterTextSplitter

# 텍스트 분할기 초기화 (기본 설정값 적용 )
text_splitter = CharacterTextSplitter(
    separator="\n",  		# 구분자: 개행문자 (문장 단위로 쪼개기)
    chunk_size=10,  		# 청크 크기
    chunk_overlap=2,  		# 청크 중첩
    length_function=len, 	# 글자 수를 기준으로 분할
)
# 텍스트 분할 - split_text() 메서드 사용
texts = text_splitter.split_text(long_text)

# 분할된 텍스트 개수 출력
print(f"분할된 텍스트의 수: {len(texts)}")

# 첫 번째 분할된 텍스트 출력
print(f"첫 번째 분할된 텍스트: {texts[0]}")

CharacterTextSplitter()는 데이터를 분할하는 함수이고 그 안에 요소들을 살펴보면

separator은 어디서 자를지에 대한 기준,

chunk_size는 한 번에 담을 최대 길이,

chunk_overlap은 다음 청크와 겹치는 부분의 길이를 나타냅니다!

(length_function은 길이를 계산하는 기준 함수에요)

 

 

texts = text_splitter.split_text(long_text)는

long_text라는 긴 문자열을 지정한 기준대로 잘라서 리스트로 반환하게 돼요

 

 

len(texts)는 분할된 청크의 개수,

texts[0]는 분할된 텍스트의 첫 번째 내용을 출력합니다.

 


2. RecursiveCharacterTextSplitter

RecursiveCharacterTextSplitter()는 재귀적으로 텍스트를 분할하게 됩니다!

구분자를 순차적으로 적용하여 큰 청크에서 시작하여 점진적으로 더 작은 단위로 분할합니다.

이 함수의 장점은 문맥을 더 잘 보존할 수 있다는 점입니다!!

 

 

문단 → 문장 → 단어 → 글자 순으로 잘게 나누는 코드를 살펴보겠습니다!

from langchain_text_splitters import RecursiveCharacterTextSplitter

# 재귀적 텍스트 분할기 초기화
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,           	 	 	 # 청크 크기
    chunk_overlap=200,         			 # 청크 중 중복되는 부분 크기
    length_function=len,         		 # 글자 수를 기준으로 분할
    separators=["\n\n", "\n", " ", ""],  # 구분자 - 재귀적으로 순차적으로 적용 [문단, 문장, 띄어쓰기, 공백]
)

# split_documents() 메서드 사용 : Document 객체를 여러 개의 작은 청크 문서로 분할
chunks = text_splitter.split_documents(pdf_docs)
print(f"생성된 텍스트 청크 수: {len(chunks)}")
print(f"각 청크의 길이: {list(len(chunk.page_content) for chunk in chunks)}")
print()

# 각 청크의 시작 부분과 끝 부분 확인 - 5개 청크만 출력
for chunk in chunks[:5]:
    print(chunk.page_content[:200])
    print("-" * 100)
    print(chunk.page_content[-200:])
    print("=" * 100)
    print()

RecursiveCharacterTextSplitter()은 큰 덩어리부터 자르고,

chunk_size를 넘어가면 점점 더 작은 단위로 쪼개는 방식이며 안에 요소들을 살펴보면!

chunk_size는 한 청크(조각)의 최대 길이,

chunk_overlap은 현재 청크와 다음 청크가 (문맥 유지를 위해) 200자씩 겹치게 됩니다

length_function=len은 글자 수 기준으로 길이를 계산

separators는 재귀적으로 자를 때의 기준 

(\n\n → 문단, \n → 줄바꿈 "  " → 단어, " " → 글자)

을 나타냅니다!

 


3. 정규표현식 사용

RechursiveCharacterTextSplitter()을 사용하면서 

일반적인 문자 길이(len) 대신 토큰 단위로 청크를 나눠볼건데요!

 

 

분할하는 정규표현식을 살펴볼게요!

from langchain_text_splitters import RecursiveCharacterTextSplitter

# TikToken 인코더를 사용하여 재귀적 텍스트 분할기 초기화
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    #encoding_name="cl100k_base",
    model_name="gpt-4o-mini",
    chunk_size=10,
    chunk_overlap=0,
)

# split_documents() 메서드 사용 : Document 객체를 여러 개의 작은 청크 문서로 분할
chunks = text_splitter.split_documents([pdf_docs[0]])  # 첫 번째 문서만 분할

print(f"생성된 청크 수: {len(chunks)}")
print(f"각 청크의 길이: {list(len(chunk.page_content) for chunk in chunks)}")

# 각 청크의 시작 부분과 끝 부분 확인
for chunk in chunks[:5]:
    print(chunk.page_content[:50])
    print("-" * 50)
    print(chunk.page_content[-50:])
    print("=" * 50)
    print()

.from_tiktoken_encoder은 OpenAI의 토큰화 방식이고

model_name 해당 모델의 토큰 규칙에 마춰 분할을 하게 되고

chunk_size=10은 한 청크 최대 10 토큰,

chunk_overlap=0은 청크 간 겹침 없음을 의미합니다!

 

 

문자 단위가 아니라 토큰 단위여서 영어 단어 하나가 여러 토큰으로 나뉠 수도 있으며,

한글 한 글자는 보통 1토큰입니다.!!

 

이 방식을 사용하면 토큰 단위로 자르기 때문에 

모델 입력 제한(token limit)에 맞춰서 안전하게 청크를 만들 수 있고,

같은 1000자라도 토큰 개수가 달라질 수 있으니, 모델 기준에 맞추는 게 더 정확합니다!

 

 

그렇다면 왜 분할을 할까요?

LLM은 한 번 처리할 수 있는 토큰 수가 제한되어 있어서, 

긴 문서를 RAG나 검색에 쓰려면 이렇게 잘게 나누고 중복을 조금 포함시켜

문맥이 끊기지 않게 해야 됩니다!!

 

 

이러한 분할은 

긴 문서를 LLM이 처리할 수 있게 잘라주며

문맥이 끊기지 않도록 일부 겹치게 overlap하며

문단 → 문장 → 단어 → 글자 순으로 유연하게 쪼개며

RAG(검색+생성) 구조에서 거의 필수적으로 사용된다고 보시면 됩니다!!

728x90