
01. 텍스트 분할의 개념과 중요성
RAG에서 LOAD 이후의 과정
사용 이유
LLM의 input token 길이 제한에 걸리지 않기 위해
참고할만한 정보는 일부이기에 리소스 최적화
할루시네이션 방지
chunk를 잘 만들면, 유사도도 잘 잡힐 것이다.
불필요한 답변을 포함하면 제대로된 답변으로 연결되지 않는다.
분할 전략
chunk overlap : 겹쳐지는 부분
02. CharacterTextSplitter
기본적으로 \n\n(문단)을 기준으로 문자 단위로 텍스트를 분할
청크의 크기를 문자 수로 측정
210자 언저리로 분할
chunk_size, chunk_overlap 지정
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
separator="\\n\\n",
chunk_size=210,
chunk_overlap=0,
length_function=len,
)
# 메타데이터와 문서 함께 전달
metadatas = [
{"document": 1},
{"document": 2},
] # 문서에 대한 메타데이터 리스트를 정의
documents = text_splitter.create_documents(
[
file,
file,
], # 분할할 텍스트 데이터를 리스트로 전달
metadatas=metadatas, # 각 문서에 해당하는 메타데이터를 전달
)
text_splitter.split_documents([file]) # 문서분할
text_splitter.split_text(file)[0] # 텍스트 분할
03. RecursiveCharacterTextSplit
가장 많이 쓰이는 분할기
재귀적인 분할로 효율적인 방식
문단(\n\n) → 문장(\n) → 단어(” “) → 글자수 순서로 재귀적 분할
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=250,
chunk_overlap=50,
length_function=len,
is_separator_regex=False,
)
Document 객체로 자르고 싶으면
create_documents
사용
# text_splitter를 사용하여 file 텍스트를 문서로 분할
texts = text_splitter.create_documents([file])
print(texts[0]) # 분할된 문서의 첫 번째 문서를 출력
print(texts[1]) # 분할된 문서의 두 번째 문서를 출력
결국 split_text는 반환값이
문자열
, create_documents는Documents 객체
이다.
04. TokenTextSplitter
LLM에 입력될때는 token 개수로 입력되기에, 토큰 개수로 자르는 방식
Tokenizer: 다양한 알고리즘으로 청킹 결과가 달라짐
언어모델에는 토큰 제한이 있기에, 토큰 제한을 초과하지 않게 만들어준다.
tiktoken
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=300,
chunk_overlap=0,
)
# file 텍스트를 청크 단위로 분할, 약간 클 수 있음
texts = text_splitter.split_text(file)
RecursiveCharacterTextSplitter또한 from_tiktoken_encoder를 사용 가능
청크 크기보다 작음을 보장
TokenTextSplitter
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(
chunk_size=300,
chunk_overlap=0,
)
texts = text_splitter.split_text(file)
print(texts[0])
SpaCy
고급 자연어처리를 위한 오픈소스 라이브러리
import warnings
from langchain_text_splitters import SpacyTextSplitter
warnings.filterwarnings("ignore")
text_splitter = SpacyTextSplitter(
chunk_size=200,
chunk_overlap=50,
)
SentenceTransformers
sentence-transformer 모델에 특화된 텍스트 분할기
from langchain_text_splitters import SentenceTransformersTokenTextSplitter
splitter = SentenceTransformersTokenTextSplitter(chunk_size=200, chunk_overlap=0)
count_start_and_stop_tokens = 2 # 시작과 종료 토큰의 개수를 2로 설정
# 텍스트의 토큰 개수에서 시작과 종료 토큰의 개수를 뺍니다.
text_token_count = splitter.count_tokens(text=file) - count_start_and_stop_tokens
print(text_token_count)
text_chunks = splitter.split_text(text=file) # 텍스트를 청크로 분할
NLTK
from langchain_text_splitters import NLTKTextSplitter
text_splitter = NLTKTextSplitter(
chunk_size=200,
chunk_overlap=0,
)
texts = text_splitter.split_text(file)
print(texts[0])
KoNLPy
한글 처리에 유용, 형태소 분석기 포함
import chunk
from langchain_text_splitters import KonlpyTextSplitter
text_splitter = KonlpyTextSplitter(chunk_size=200, chunk_overlap=50)
texts = text_splitter.split_text(file) # 한국어 문서를 문장 단위로 분할
print(texts[0])
HuggingFace Tokenizer
from transformers import GPT2TokenizerFast
hf_tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
# 허깅페이스 토크나이저를 사용하여 CharacterTextSplitter 객체를 생성
hf_tokenizer,
chunk_size=300,
chunk_overlap=50,
)
texts = text_splitter.split_text(file)
05. SemanticChunker
텍스트를 의미론적 유사성에 기반해 분할
chunk_size, chunk_overlap 옵션을 설정하지 않는다.
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings
# OpenAI 임베딩을 사용하여 의미론적 청크 분할기를 초기화
text_splitter = SemanticChunker(OpenAIEmbeddings())
Breakpoints
분리할 시점을 결정해 작동
기준은 어떻게 설정할까?
breakpoint_threshold_type
지정Percentile: 백분위수 기준
Standard Deviation: 표준편차 기준
Interquartile: 사분위수 기준
06. Code Splitter
code기반 RAG를 하고자할 때 사용
RecursiveCharacterTextSplitter.get_separators_for_language(Language.PYTHON)
파이썬 code splitter 지정['\nclass ', '\ndef ', '\n\tdef ', '\n\n', '\n', ' ', '']
PYTHON_CODE = """
def hello_world():
print("Hello, World!")
hello_world()
"""
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON, chunk_size=50, chunk_overlap=0
)
python_docs = python_splitter.create_documents([PYTHON_CODE])
python_docs
markdown , HTML splitter는 전용 분할기가 따로 있음
07. MarkdownHeaderTextSplitter
Header를 기준으로 분할
분할하는 header를 튜플 형식으로 지정 가능
메타 정보를 같이 줘서 트리구조를 알 수 있다.
headers_to_split_on = [
(
"#",
"Header 1",
),
(
"##",
"Header 2",
),
(
"###",
"Header 3",
),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
md_header_splits = markdown_splitter.split_text(markdown_document)
for header in md_header_splits:
print(f"{header.page_content}")
print(f"{header.metadata}", end="\\n=====================\\n")
08. HTMLHeaderTextSplitter
구조인식 청크 생성기
마크다운 분할기와 유사
좀 더 재귀적
headers_to_split_on = [
("h1", "Header 1"),
("h2", "Header 2"),
("h3", "Header 3"),
]
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
html_header_splits = html_splitter.split_text(html_string)
for header in html_header_splits:
print(f"{header.page_content}")
print(f"{header.metadata}", end="\\n=====================\\n")
다른 splitter와 파이프라인으로 연결할 수 있다. (url을 가져올 때 사용)
때로는 특정 header를 누락할 수 있다.
09. RecursiveJSONSplitter
DFS 알고리즘을 사용해 더 작은 JSON 청크를 생성
리스트 객체는 분할하지 않는다.