문서 분할은 Retrieval-Augmented Generation (RAG) 시스템의 두 번째 단계로서, 로드된 문서들을 효율적으로 처리하고, 시스템이 정보를 보다 잘 활용할 수 있도록 준비하는 중요한 과정이다.
이 단계의 목적은 크고 복잡한 문서를 LLM이 받아들일 수 있는 효율적인 작은 규모의 조각으로 나누는 작업이다.
나중에 사용자가 입력한 질문에 대하여 보다 효율적인 정보만 압축/선별하여 가져오기 위함이다.
(예시) 구글이 앤스로픽에 투자한 금액은 얼마야?
핀포인트 정보 검색(정확성): 문서를 세분화함으로써 질문(Query)에 연관성이 있는 정보만 가져오는데 도움이 된다. 각각의 단위는 특정 주제나 내용에 초점을 맞추므로, 관련성이 높은 정보를 제공한다.
리소스 최적화(효율성): 전체 문서를 LLM으로 입력하게 되면 비용이 많이 발생할 뿐더러, 효율적인 답변을 많은 정보 속에 발췌하여 답변하지 못하게 된다. 때로는 이러한 문제가 할루시네이션으로 이어지게 된다. 따라서, 답변에 필요한 정보만 발췌하기 위한 목적도 있다.
문서 구조 파악: PDF 파일, 웹 페이지, 전자 책 등 다양한 형식의 문서에서 구조를 파악한다. 이는 문서의 헤더, 푸터, 페이지 번호, 섹션 제목 등을 식별하는 과정을 포함할 수 있다.
단위 선정: 문서를 어떤 단위로 나눌지 결정한다. 이는 페이지별, 섹션별, 또는 문단별일 수 있으며, 문서의 내용과 목적에 따라 다르다.
단위 크기 선정(chunk size): 문서를 몇 개의 토큰 단위로 나눌 것인지를 정한다.
청크 오버랩(chunk overlap): 분할된 끝 부분에서 맥락이 이어질 수 있도록 일부를 겹쳐서(overlap) 분할하는 것이 일반적이다.
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 단계 2: 문서 분할(Split Documents)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
splits = text_splitter.split_documents(docs)
\n\n
을 기준으로 문자 단위로 텍스트를 분할하고, 청크의 크기를 문자 수로 측정한다.%pip install -qU langchain-text-splitters
./data/appendix-keywords.txt
파일을 열어 내용을 읽어들인다.file
변수에 저장한다.# data/appendix-keywords.txt 파일을 열어서 f라는 파일 객체를 생성합니다.
with open("./data/appendix-keywords.txt") as f:
file = f.read() # 파일의 내용을 읽어서 file 변수에 저장합니다.
# 파일으로부터 읽은 내용을 일부 출력합니다.
print(file[:500])
출력 예시:
Semantic Search
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.
예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.
연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝
Embedding
정의: 임베딩은 단어나 문장 같은 텍스트 데이터를 저차원의 연속적인 벡터로 변환하는 과정입니다. 이를 통해 컴퓨터가 텍스트를 이해하고 처리할 수 있게 합니다.
예시: "사과"라는 단어를 [0.65, -0.23, 0.17]과 같은 벡터로 표현합니다.
연관키워드: 자연어 처리, 벡터화, 딥러닝
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
# 텍스트를 분할할 때 사용할 구분자를 지정합니다. 기본값은 "\n\n"입니다.
# separator=" ",
# 분할된 텍스트 청크의 최대 크기를 지정합니다.
chunk_size=250,
# 분할된 텍스트 청크 간의 중복되는 문자 수를 지정합니다.
chunk_overlap=50,
# 텍스트의 길이를 계산하는 함수를 지정합니다.
length_function=len,
# 구분자가 정규식인지 여부를 지정합니다.
is_separator_regex=False,
)
file
텍스트를 문서 단위로 분할하고, 분할된 문서 리스트 중 첫 번째 문서를 출력한다.texts = text_splitter.create_documents([file])
print(texts[0]) # 분할된 문서 중 첫 번째 문서를 출력합니다.
출력 예시:
page_content='Semantic Search\n\n정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.\n예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.\n연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝\n\nEmbedding'
metadatas = [
{"document": 1},
{"document": 2},
] # 문서에 대한 메타데이터 리스트를 정의합니다.
documents = text_splitter.create_documents(
[file, file], # 분할할 텍스트 데이터를 리스트로 전달합니다.
metadatas=metadatas, # 각 문서에 해당하는 메타데이터를 전달합니다.
)
print(documents[0]) # 분할된 문서 중 첫 번째 문서를 출력합니다.
출력 예시:
page_content='Semantic Search\n\n정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.\n예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.\n연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝\n\nEmbedding' metadata={'document': 1}
split_text()
메서드를 사용하여 텍스트를 분할한다.text_splitter.split_text(file)[0]
출력 예시:
'Semantic Search\n\n정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.\n예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.\n연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝\n\nEmbedding'
["\n\n", "\n", " ", ""]
에 의해 분할된다.%pip install -qU langchain-text-splitters
appendix-keywords.txt
파일을 열어 내용을 읽어들인다.file
변수에 저장한다.# appendix-keywords.txt 파일을 열어서 f라는 파일 객체를 생성합니다.
with open("./data/appendix-keywords.txt") as f:
file = f.read() # 파일의 내용을 읽어서 file 변수에 저장합니다.
# 파일으로부터 읽은 내용을 일부 출력합니다.
print(file[:500])
출력 예시:
Semantic Search
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.
예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.
연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝
Embedding
정의: 임베딩은 단어나 문장 같은 텍스트 데이터를 저차원의 연속적인 벡터로 변환하는 과정입니다. 이를 통해 컴퓨터가 텍스트를 이해하고 처리할 수 있게 합니다.
예시: "사과"라는 단어를 [0.65, -0.23, 0.17]과 같은 벡터로 표현합니다.
연관키워드: 자연어 처리, 벡터화, 딥러닝
Token
정의: 토큰은 텍스트를 더 작은 단위로 분할하는 것을 의미합니다. 이는 일반적으로 단어, 문장, 또는 구절일 수 있습니다.
예시: 문장 "나는 학교에 간다"를 "나는", "학교에", "간다"로 분할합니다.
연관키워드: 토큰화, 자연어
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=250, # 청크 크기를 매우 작게 설정합니다. 예시를 위한 설정입니다.
chunk_overlap=50, # 청크 간의 중복되는 문자 수를 설정합니다.
length_function=len, # 문자열 길이를 계산하는 함수를 지정합니다.
is_separator_regex=False, # 구분자로 정규식을 사용할지 여부를 설정합니다.
)
file
텍스트를 문서 단위로 분할하고, 분할된 문서의 첫 번째와 두 번째 문서를 출력한다.texts = text_splitter.create_documents([file])
print(texts[0]) # 분할된 문서의 첫 번째 문서를 출력합니다.
print("===" * 20)
print(texts[1]) # 분할된 문서의 두 번째 문서를 출력합니다.
출력 예시:
page_content='Semantic Search\n\n정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.\n예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.\n연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝\n\nEmbedding'
============================================================
page_content='Embedding\n\n정의: 임베딩은 단어나 문장 같은 텍스트 데이터를 저차원의 연속적인 벡터로 변환하는 과정입니다. 이를 통해 컴퓨터가 텍스트를 이해하고 처리할 수 있게 합니다.\n예시: "사과"라는 단어를 [0.65, -0.23, 0.17]과 같은 벡터로 표현합니다.\n연관키워드: 자연어 처리, 벡터화, 딥러닝\n\nToken'
text_splitter.split_text(file)[:2]
출력 예시:
['Semantic Search\n\n정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.\n예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.\n연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝\n\nEmbedding', 'Embedding\n\n정의: 임베딩은 단어나 문장 같은 텍스트 데이터를 저차원의 연속적인 벡터로 변환하는 과정입니다. 이를 통해 컴퓨터가 텍스트를 이해하고 처리할 수 있게 합니다.\n예시: "사과"라는 단어를 [0.65, -0.23, 0.17]과 같은 벡터로 표현합니다.\n연관키워드: 자연어 처리, 벡터화, 딥러닝\n\nToken']
%pip install --upgrade --quiet langchain-text-splitters tiktoken
appendix-keywords.txt
파일을 열어 내용을 읽어들입니다.file
변수에 저장합니다.# data/appendix-keywords.txt 파일을 열어서 f라는 파일 객체를 생성합니다.
with open("./data/appendix-keywords.txt") as f:
file = f.read() # 파일의 내용을 읽어서 file 변수에 저장합니다.
# 파일으로부터 읽은 내용을 일부 출력합니다.
print(file[:500])
출력 예시:
Semantic Search
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.
예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.
연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝
Embedding
정의: 임베딩은 단어나 문장 같은 텍스트 데이터를 저차원의 연속적인 벡터로 변환하는 과정입니다. 이를 통해 컴퓨터가 텍스트를 이해하고 처리할 수 있게 합니다.
예시: "사과"라는 단어를 [0.65, -0.23, 0.17]과 같은 벡터로 표현합니다.
연관키워드: 자연어 처리, 벡터화, 딥러닝
Token
정의: 토큰은 텍스트를 더 작은 단위로 분할하는 것을 의미합니다. 이는 일반적으로 단어, 문장, 또는 구절일 수 있습니다.
예시: 문장 "나는 학교에 간다"를 "나는", "학교에", "간다"로 분할합니다.
연관키워드: 토큰화, 자연어
from_tiktoken_encoder
메서드를 사용하여 Tiktoken 인코더 기반의 텍스트 분할기를 초기화합니다.from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=300, # 청크 크기를 300으로 설정합니다.
chunk_overlap=0, # 청크 간 중복되는 부분이 없도록 설정합니다.
)
texts = text_splitter.split_text(file)
print(len(texts)) # 분할된 청크의 개수를 출력합니다.
출력 예시:
51
texts
리스트의 첫 번째 요소를 출력합니다.# texts 리스트의 첫 번째 요소를 출력합니다.
print(texts[0])
출력 예시:
Semantic Search
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(
chunk_size=200, # 청크 크기를 200으로 설정합니다.
chunk_overlap=0, # 청크 간 중복을 0으로 설정합니다.
)
texts = text_splitter.split_text(file)
print(texts[0]) # 분할된 텍스트의 첫 번째 청크를 출력합니다.
출력 예시:
Semantic Search
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.
예시: 사용자가 "태양계 행성"�
from transformers import GPT2TokenizerFast
# GPT-2 모델의 토크나이저를 불러옵니다.
hf_tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
from_huggingface_tokenizer
메서드를 통해 허깅페이스 토크나이저를 사용하여 텍스트 분할기를 초기화합니다.text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
hf_tokenizer,
chunk_size=300,
chunk_overlap=50,
)
texts = text_splitter.split_text(file)
texts
리스트의 1번째 요소를 출력합니다.print(texts[1])
출력 예시:
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.
예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.
연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝
SemanticChunker는 텍스트를 의미론적 유사성에 기반하여 분할하는 도구입니다. 이 도구는 텍스트를 문장 단위로 분할한 후, 유사한 의미를 가진 문장들을 그룹화하여 청크(chunk)로 나눕니다. 이를 통해 유사한 내용을 가진 문장들이 함께 처리되며, 효율적인 텍스트 분석과 정보 검색이 가능합니다.
%pip install -qU langchain_experimental langchain_openai
appendix-keywords.txt
파일을 열어 내용을 읽어들입니다.# data/appendix-keywords.txt 파일을 열어서 f라는 파일 객체를 생성합니다.
with open("./data/appendix-keywords.txt") as f:
file = f.read() # 파일의 내용을 읽어서 file 변수에 저장합니다.
# 파일로부터 읽은 내용을 일부 출력합니다.
print(file[:350])
출력 예시:
Semantic Search
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다.
예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다.
연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝
Embedding
정의: 임베딩은 단어나 문장 같은 텍스트 데이터를 저차원의 연속적인 벡터로 변환하는 과정입니다. 이를 통해 컴퓨터가 텍스트를 이해하고 처리할 수 있게 합니다.
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings
# OpenAI 임베딩을 사용하여 의미론적 청크 분할기를 초기화합니다.
text_splitter = SemanticChunker(OpenAIEmbeddings())
text_splitter
를 사용하여 file
텍스트를 분할합니다.chunks = text_splitter.split_text(file)
# 분할된 청크 중 첫 번째 청크를 출력합니다.
print(chunks[0])
출력 예시:
Semantic Search
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다. 예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다. 연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝
Embedding
정의: 임베딩은 단어나 문장 같은 텍스트 데이터를 저차원의 연속적인 벡터로 변환하는 과정입니다. 이를 통해 컴퓨터가 텍스트를 이해하고 처리할 수 있게 합니다. 예시: "사과"라는 단어를 [0.65, -0.23, 0.17]과 같은 벡터로 표현합니다. 연관키워드: 자연어 처리, 벡터화, 딥러닝
text_splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=70,
)
docs = text_splitter.create_documents([file])
for i, doc in enumerate(docs[:5]):
print(f"[Chunk {i}]", end="\n\n")
print(doc.page_content)
print("===" * 20)
출력 예시:
[Chunk 0]
Semantic Search
정의: 의미론적 검색은 사용자의 질의를 단순한 키워드 매칭을 넘어서 그 의미를 파악하여 관련된 결과를 반환하는 검색 방식입니다. 예시: 사용자가 "태양계 행성"이라고 검색하면, "목성", "화성" 등과 같이 관련된 행성에 대한 정보를 반환합니다. 연관키워드: 자연어 처리, 검색 알고리즘, 데이터 마이닝
text_splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="standard_deviation",
breakpoint_threshold_amount=1.25,
)
docs = text_splitter.create_documents([file])
for i, doc in enumerate(docs[:5]):
print(f"[Chunk {i}]", end="\n\n")
print(doc.page_content)
print("===" * 20)
text_splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="interquartile",
breakpoint_threshold_amount=0.5,
)
docs = text_splitter.create_documents([file])
for i, doc in enumerate(docs[:5]):
print(f"[Chunk {i}]", end="\n\n")
print(doc.page_content)
print("===" * 20)
SemanticChunker는 다양한 기준(Percentile, Standard Deviation, Interquartile)을 사용하여 텍스트를 의미론적으로 관련된 청크로 나누어 줍니다. 이는 LLM(대규모 언어 모델) 기반 응용 프로그램에서 효율적으로 텍스트를 처리하고, 정확한 정보를 제공하는 데 매우 유용합니다.
코드 분할은 다양한 프로그래밍 언어로 작성된 코드를 청크 단위로 분할하여 처리하는 과정입니다. 이를 통해 코드 내 특정 기능이나 문법을 쉽게 분석하거나 검색할 수 있습니다. 여기서는 RecursiveCharacterTextSplitter
와 Language
클래스를 사용하여 Python, JavaScript, Markdown, LaTeX 등의 코드를 분할하는 방법을 설명합니다.
%pip install -qU langchain-text-splitters
from langchain_text_splitters import Language, RecursiveCharacterTextSplitter
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])
for doc in python_docs:
print(doc.page_content, end="\n==================\n")
출력 예시:
def hello_world():
print("Hello, World!")
==================
hello_world()
==================
JS_CODE = """
function helloWorld() {
console.log("Hello, World!");
}
helloWorld();
"""
js_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.JS, chunk_size=60, chunk_overlap=0
)
js_docs = js_splitter.create_documents([JS_CODE])
for doc in js_docs:
print(doc.page_content, end="\n==================\n")
출력 예시:
function helloWorld() {
console.log("Hello, World!");
}
==================
helloWorld();
==================
markdown_text = """
# 🦜️🔗 LangChain
⚡ LLM을 활용한 초스피드 애플리케이션 구축 ⚡
## 빠른 설치
```bash
pip install langchain
빠르게 발전하는 분야의 오픈 소스 프로젝트 입니다. 많관부 🙏
"""
md_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.MARKDOWN,
chunk_size=60,
chunk_overlap=0,
)
md_docs = md_splitter.create_documents([markdown_text])
for doc in md_docs:
print(doc.page_content, end="\n==================\n")
출력 예시:
# 🦜️🔗 LangChain
⚡ LLM을 활용한 초스피드 애플리케이션 구축 ⚡
==================
## 빠른 설치
```bash
pip install langchain
==================
# 빠르게 발전하는 분야의 오픈 소스 프로젝트 입니다. 많관부 🙏
==================
latex_text = """
\documentclass{article}
\begin{document}
\maketitle
\section{Introduction}
LLM은 방대한 양의 텍스트 데이터로 학습하여 사람과 유사한 언어를 생성할 수 있는 기계 학습 모델의 한 유형입니다.
\subsection{History of LLMs}
초기 LLM은 1980년대와 1990년대에 개발되었지만, 처리할 수 있는 데이터 양과 당시 사용 가능한 컴퓨팅 능력으로 인해 제한되었습니다.
\subsection{Applications of LLMs}
LLM은 챗봇, 콘텐츠 생성, 가상 어시스턴트 등 산업 분야에서 많은 응용 분야를 가지고 있습니다.
\end{document}
"""
latex_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.LATEX, chunk_size=60, chunk_overlap=0
)
latex_docs = latex_splitter.create_documents([latex_text])
for doc in latex_docs:
print(doc.page_content, end="\n==================\n")
출력 예시:
\documentclass{article}
\begin{document}
\maketitle
==================
\section{Introduction}
LLM은 방대한 양의 텍스트 데이터로 학습하여 사람과 유사한
==================
언어를 생성할 수 있는 기계 학습 모델의 한 유형입니다.
==================
\subsection{History of LLMs}
초기 LLM은 1980년대와 1990년대에 개발되었지만, 처리할 수 있는 데이터 양과 당시 사용 가능한 컴퓨팅 능력으로 인해 제한되었습니다.
==================
\subsection{Applications of LLMs}
LLM은 챗봇, 콘텐츠 생성, 가상 어시스턴트 등 산업 분야에서 많은 응용 분야를 가지고 있습니다.
==================
SOL_CODE = """
pragma solidity ^0.8.20;
contract HelloWorld {
function add(uint a, uint b) pure public returns(uint) {
return a + b;
}
}
"""
sol_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.SOL, chunk_size=128, chunk_overlap=0
)
sol_docs = sol_splitter.create_documents([SOL_CODE])
for doc in sol_docs:
print(doc.page_content, end="\n==================\n")
출력 예시:
pragma solidity ^0.8.20;
==================
contract HelloWorld {
function add(uint a, uint b) pure public returns(uint) {
return a + b;
}
}
==================
C_CODE = """
using System;
class Program
{
static void Main()
{
Console.WriteLine("Enter a number (1-5):");
int input = Convert.ToInt32(Console.ReadLine());
for (int i = 1; i <= input; i++)
{
if (i % 2 == 0)
{
Console.WriteLine($"{i} is even.");
}
else
{
Console.WriteLine($"{i} is odd.");
}
}
Console.WriteLine("Goodbye!");
}
}
"""
c_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.CSHARP, chunk_size=128, chunk_overlap=0
)
c_docs = c_splitter.create_documents([C_CODE])
for doc in c_docs:
print(doc.page_content, end="\n==================\n")
출력 예시:
using System;
==================
class Program
{
static void Main()
{
Console.WriteLine("Enter a number (1-5):");
==================
int input = Convert.ToInt32(Console.ReadLine());
for (int i = 1; i <= input; i++)
{
==================
if (i % 2 == 0)
{
Console.WriteLine($"{i} is even.");
}
else
==================
{
Console.WriteLine($"{i} is odd.");
}
}
Console.WriteLine("Goodbye!");
}
}
==================
RecursiveCharacterTextSplitter
를 사용하면 다양한 프로그래밍 언어로 작성된 코드를 손쉽게 청크 단위로 분할할 수 있습니다. 이 과정은 코드 분석, 검색, 이해에 유용하며, 코드의 구조를 잘 유지하면서 필요한 정보만을 효과적으로 추출할 수 있게 합니다.
MarkdownHeaderTextSplitter는 마크다운 파일의 구조를 이해하고 효율적으로 다루는 데 유용한 도구입니다. 문서의 전체적인 맥락과 구조를 고려하여 텍스트를 의미 있는 방식으로 분할할 수 있습니다. 특히, 마크다운 파일의 헤더별로 내용을 나눠서 각 헤더 아래의 내용을 별도의 청크로 관리할 수 있습니다.
%pip install -qU langchain-text-splitters
from langchain_text_splitters import MarkdownHeaderTextSplitter
# 마크다운 형식의 문서를 문자열로 정의합니다.
markdown_document = "# Title\n\n## 1. SubTitle\n\nHi this is Jim\n\nHi this is Joe\n\n### 1-1. Sub-SubTitle \n\nHi this is Lance \n\n## 2. Baz\n\nHi this is Molly"
headers_to_split_on = [ # 문서를 분할할 헤더 레벨과 해당 레벨의 이름을 정의합니다.
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]
# 마크다운 헤더를 기준으로 텍스트를 분할하는 MarkdownHeaderTextSplitter 객체를 생성합니다.
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
# markdown_document를 헤더를 기준으로 분할하여 md_header_splits에 저장합니다.
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")
출력 예시:
Hi this is Jim
Hi this is Joe
{'Header 1': 'Title', 'Header 2': '1. SubTitle'}
=====================
Hi this is Lance
{'Header 1': 'Title', 'Header 2': '1. SubTitle', 'Header 3': '1-1. Sub-SubTitle'}
=====================
Hi this is Molly
{'Header 1': 'Title', 'Header 2': '2. Baz'}
=====================
기본적으로 MarkdownHeaderTextSplitter
는 청크 내에서 헤더를 제거합니다. 이를 비활성화하여 헤더를 유지하고 싶다면 strip_headers=False
로 설정할 수 있습니다.
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on,
strip_headers=False,
)
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")
출력 예시:
# Title
## 1. SubTitle
Hi this is Jim
Hi this is Joe
{'Header 1': 'Title', 'Header 2': '1. SubTitle'}
=====================
### 1-1. Sub-SubTitle
Hi this is Lance
{'Header 1': 'Title', 'Header 2': '1. SubTitle', 'Header 3': '1-1. Sub-SubTitle'}
=====================
## 2. Baz
Hi this is Molly
{'Header 1': 'Title', 'Header 2': '2. Baz'}
=====================
MarkdownHeaderTextSplitter
로 분할된 결과를 다시 RecursiveCharacterTextSplitter
와 같은 다른 분할기를 사용하여 더 세부적으로 분할할 수 있습니다.
from langchain_text_splitters import RecursiveCharacterTextSplitter
markdown_document = "# Intro \n\n## History \n\nMarkdown[9] is a lightweight markup language for creating formatted text using a plain-text editor. John Gruber created Markdown in 2004 as a markup language that is appealing to human readers in its source code form.[9] \n\nMarkdown is widely used in blogging, instant messaging, online forums, collaborative software, documentation pages, and readme files. \n\n## Rise and divergence \n\nAs Markdown popularity grew rapidly, many Markdown implementations appeared, driven mostly by the need for \n\nadditional features such as tables, footnotes, definition lists,[note 1] and Markdown inside HTML blocks. \n\n#### Standardization \n\nFrom 2012, a group of people, including Jeff Atwood and John MacFarlane, launched what Atwood characterised as a standardisation effort. \n\n## Implementations \n\nImplementations of Markdown are available for over a dozen programming languages."
headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
]
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on, strip_headers=False
)
md_header_splits = markdown_splitter.split_text(markdown_document)
chunk_size = 200
chunk_overlap = 20
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size, chunk_overlap=chunk_overlap
)
splits = text_splitter.split_documents(md_header_splits)
# 분할된 결과를 출력합니다.
for header in splits:
print(f"{header.page_content}")
print(f"{header.metadata}", end="\n=====================\n")
출력 예시:
# Intro
## History
{'Header 1': 'Intro', 'Header 2': 'History'}
=====================
Markdown[9] is a lightweight markup language for creating formatted text using a plain-text editor. John Gruber created Markdown in 2004 as a markup language that is appealing to human readers in its
{'Header 1': 'Intro', 'Header 2': 'History'}
=====================
readers in its source code form.[9]
{'Header 1': 'Intro', 'Header 2': 'History'}
=====================
Markdown is widely used in blogging, instant messaging, online forums, collaborative software, documentation pages, and readme files.
{'Header 1': 'Intro', 'Header 2': 'History'}
=====================
## Rise and divergence
As Markdown popularity grew rapidly, many Markdown implementations appeared, driven mostly by the need for
{'Header 1': 'Intro', 'Header 2': 'Rise and divergence'}
=====================
additional features such as tables, footnotes, definition lists,[note 1] and Markdown inside HTML blocks.
#### Standardization
{'Header 1': 'Intro', 'Header 2': 'Rise and divergence'}
=====================
From 2012, a group of people, including Jeff Atwood and John MacFarlane, launched what Atwood characterised as a standardisation effort.
{'Header 1': 'Intro', 'Header 2': 'Rise and divergence'}
=====================
## Implementations
Implementations of Markdown are available for over a dozen programming languages.
{'Header 1': 'Intro', 'Header 2': 'Implementations'}
=====================
MarkdownHeaderTextSplitter
와 같은 도구를 사용하면 문서의 구조를 유지하면서도 각 헤더별로 분할된 텍스트를 관리할 수 있습니다. 이 방법을 통해 텍스트의 공통된 맥락을 유지하면서도 세부적인 청크를 효과적으로 다룰 수 있습니다.
HTMLHeaderTextSplitter는 HTML 문서의 구조를 이해하고 각 요소를 분할하여 메타데이터를 추가하는 "구조 인식" 청크 생성기입니다. 이는 문서의 구조적 맥락을 보존하면서 관련 텍스트를 그룹화하고, 다양한 텍스트 처리를 용이하게 합니다.
%pip install -qU langchain-text-splitters
from langchain_text_splitters import HTMLHeaderTextSplitter
html_string = """
<!DOCTYPE html>
<html>
<body>
<div>
<h1>Foo</h1>
<p>Some intro text about Foo.</p>
<div>
<h2>Bar main section</h2>
<p>Some intro text about Bar.</p>
<h3>Bar subsection 1</h3>
<p>Some text about the first subtopic of Bar.</p>
<h3>Bar subsection 2</h3>
<p>Some text about the second subtopic of Bar.</p>
</div>
<div>
<h2>Baz</h2>
<p>Some text about Baz</p>
</div>
<br>
<p>Some concluding text about Foo</p>
</div>
</body>
</html>
"""
headers_to_split_on = [
("h1", "Header 1"), # 분할할 헤더 태그와 해당 헤더의 이름을 지정합니다.
("h2", "Header 2"),
("h3", "Header 3"),
]
# 지정된 헤더를 기준으로 HTML 텍스트를 분할하는 HTMLHeaderTextSplitter 객체를 생성합니다.
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
# HTML 문자열을 분할하여 결과를 html_header_splits 변수에 저장합니다.
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")
출력 예시:
Foo
{}
=====================
Some intro text about Foo.
Bar main section Bar subsection 1 Bar subsection 2
{'Header 1': 'Foo'}
=====================
Some intro text about Bar.
{'Header 1': 'Foo', 'Header 2': 'Bar main section'}
=====================
Some text about the first subtopic of Bar.
{'Header 1': 'Foo', 'Header 2': 'Bar main section', 'Header 3': 'Bar subsection 1'}
=====================
Some text about the second subtopic of Bar.
{'Header 1': 'Foo', 'Header 2': 'Bar main section', 'Header 3': 'Bar subsection 2'}
=====================
Baz
{'Header 1': 'Foo'}
=====================
Some text about Baz
{'Header 1': 'Foo', 'Header 2': 'Baz'}
=====================
Some concluding text about Foo
{'Header 1': 'Foo'}
=====================
이 예시에서는 웹 URL로부터 HTML 콘텐츠를 로드한 후, 이를 HTML 헤더를 기준으로 분할하고, 이후에 다른 분할기를 사용하여 추가로 처리합니다.
from langchain_text_splitters import RecursiveCharacterTextSplitter
url = "https://plato.stanford.edu/entries/goedel/" # 분할할 텍스트의 URL을 지정합니다.
headers_to_split_on = [ # 분할할 HTML 헤더 태그와 해당 헤더의 이름을 지정합니다.
("h1", "Header 1"),
("h2", "Header 2"),
("h3", "Header 3"),
("h4", "Header 4"),
]
# HTML 헤더를 기준으로 텍스트를 분할하는 HTMLHeaderTextSplitter 객체를 생성합니다.
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
# URL에서 텍스트를 가져와 HTML 헤더를 기준으로 분할합니다.
html_header_splits = html_splitter.split_text_from_url(url)
chunk_size = 500 # 텍스트를 분할할 청크의 크기를 지정합니다.
chunk_overlap = 30 # 분할된 청크 간의 중복되는 문자 수를 지정합니다.
text_splitter = RecursiveCharacterTextSplitter( # 텍스트를 재귀적으로 분할하는 RecursiveCharacterTextSplitter 객체를 생성합니다.
chunk_size=chunk_size, chunk_overlap=chunk_overlap
)
# HTML 헤더로 분할된 텍스트를 다시 청크 크기에 맞게 분할합니다.
splits = text_splitter.split_documents(html_header_splits)
# 분할된 텍스트 중 80번째부터 85번째까지의 청크를 출력합니다.
for header in splits[80:85]:
print(f"{header.page_content}")
print(f"{header.metadata}", end="\n=====================\n")
출력 예시:
We see that Gödel first tried to reduce the consistency problem for analysis to that of arithmetic. This seemed to require a truth definition for arithmetic, which in turn led to paradoxes, such as the Liar paradox (“This sentence is false”) and Berry’s paradox (“The least number not defined by an expression consisting of just fourteen English words”). Gödel then noticed that such paradoxes would not necessarily arise if truth were replaced by provability. But this means that arithmetic truth
{'Header 1': 'Kurt Gödel', 'Header 2': '2. Gödel’s Mathematical Work', 'Header 3': '2.2 The Incompleteness Theorems', 'Header 4': '2.2.1 The First Incompleteness Theorem'}
=====================
means that arithmetic truth and arithmetic provability are not co-extensive — whence the First Incompleteness Theorem.
{'Header 1': 'Kurt Gödel', 'Header 2': '2. Gödel’s Mathematical Work', 'Header 3': '2.2 The Incompleteness Theorems', 'Header 4': '2.2.1 The First Incompleteness Theorem'}
=====================
This account of Gödel’s discovery was told to Hao Wang very much after the fact; but in Gödel’s contemporary correspondence with Bernays and Zermelo, essentially the same description of his path to the theorems is given. (See Gödel 2003a and Gödel 2003b respectively.) From those accounts we see that the undefinability of truth in arithmetic, a result credited to Tarski, was likely obtained in some form by Gödel by 1931. But he neither publicized nor published the result; the biases logicians
{'Header 1': 'Kurt Gödel', 'Header 2': '2. Gödel’s Mathematical Work', 'Header 3': '2.2 The Incompleteness Theorems', 'Header 4': '2.2.1 The First Incompleteness Theorem'}
=====================
result; the biases logicians had expressed at the time concerning the notion of truth, biases which came vehemently to the fore when Tarski announced his results on the undefinability of truth in formal systems 1935, may have served as a deterrent to Gödel’s publication of that theorem.
{'Header 1': 'Kurt Gödel', 'Header 2': '2. Gödel’s Mathematical Work', 'Header 3': '2.2 The Incompleteness Theorems', 'Header 4': '2.2.1 The First Incompleteness Theorem'}
=====================
We now describe the proof of the two theorems, formulating Gödel’s results in Peano arithmetic. Gödel himself used a system related to that defined in Principia Mathematica, but containing Peano arithmetic. In our presentation of the First and Second Incompleteness Theorems we refer to Peano arithmetic as P, following Gödel’s notation.
{'Header 1': 'Kurt Gödel', 'Header 2': '2. Gödel’s Mathematical Work', 'Header 3': '2.2 The Incompleteness Theorems', 'Header 4': '2.2.2 The proof of the First Incompleteness Theorem'}
=====================
HTMLHeaderTextSplitter는 문서 구조의 차이를 처리하려고 시도하지만, 특정 경우에는 헤더를 누락할 수 있습니다. 예를 들어, 헤더가 예상하지 못한 하위 트리에 위치할 경우 관련된 텍스트와 헤더가 메타데이터에 제대로 나타나지 않을 수 있습니다. 이러한 점을 고려하여 HTML 문서의 구조를 분석하고 텍스트를 분할하는 것이 중요합니다.
RecursiveJsonSplitter는 JSON 데이터를 깊이 우선 탐색하여 작은 청크로 분할하는 도구입니다. 이 분할기는 중첩된 JSON 구조를 가능한 한 보존하면서, 청크 크기를 설정된 범위 내에 유지하려고 합니다.
%pip install -qU langchain-text-splitters
import requests
from langchain_text_splitters import RecursiveJsonSplitter
# JSON 데이터를 로드합니다.
json_data = requests.get("https://api.smith.langchain.com/openapi.json").json()
# JSON 데이터를 최대 300 크기의 청크로 분할하는 RecursiveJsonSplitter 객체를 생성합니다.
splitter = RecursiveJsonSplitter(max_chunk_size=300)
# JSON 데이터를 재귀적으로 분할합니다.
json_chunks = splitter.split_json(json_data=json_data)
# JSON 데이터를 기반으로 문서를 생성합니다.
docs = splitter.create_documents(texts=[json_data])
# JSON 데이터를 기반으로 문자열 청크를 생성합니다.
texts = splitter.split_text(json_data=json_data)
# 첫 번째 문서를 출력합니다.
print(docs[0].page_content)
print("===" * 20)
# 첫 번째 문자열 청크를 출력합니다.
print(texts[0])
출력 예시:
{"openapi": "3.1.0", "info": {"title": "LangSmith", "version": "0.1.0"}, "paths": {"/api/v1/sessions/{session_id}": {"get": {"tags": ["tracer-sessions"], "summary": "Read Tracer Session", "description": "Get a specific session."}}}}
============================================================
{"openapi": "3.1.0", "info": {"title": "LangSmith", "version": "0.1.0"}, "paths": {"/api/v1/sessions/{session_id}": {"get": {"tags": ["tracer-sessions"], "summary": "Read Tracer Session", "description": "Get a specific session."}}}}
JSON 데이터에서 큰 리스트 객체가 포함된 경우, RecursiveJsonSplitter는 리스트 객체를 분할하지 않습니다. 리스트를 딕셔너리 형태로 변환하는 방법은 다음과 같습니다.
import json
# 청크의 크기를 확인해 봅시다.
print([len(text) for text in texts][:10])
# 더 큰 청크 중 하나를 검토해 보면 리스트 객체가 있는 것을 볼 수 있습니다.
print(texts[1])
# JSON 모듈을 사용하여 2번 index 청크를 파싱합니다.
json_data = json.loads(texts[2])
print(json_data["paths"])
출력 예시:
[232, 197, 469, 210, 213, 237, 271, 191, 232, 215]
{"paths": {"/api/v1/sessions/{session_id}": {"get": {"operationId": "read_tracer_session_api_v1_sessions__session_id__get", "security": [{"API Key": []}, {"Tenant ID": []}, {"Bearer Auth": []}]}}}}
리스트를 index:item 형태로 변환하여 분할하는 방법입니다.
# JSON을 전처리하고 리스트를 인덱스:항목을 키:값 쌍으로 하는 딕셔너리로 변환합니다.
texts = splitter.split_text(json_data=json_data, convert_lists=True)
# 리스트가 딕셔너리로 변환되었고, 그 결과를 확인합니다.
print(texts[2])
출력 예시:
{"paths": {"/api/v1/sessions/{session_id}": {"get": {"parameters": [{"name": "session_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid", "title": "Session Id"}}, {"name": "include_stats", "in": "query", "required": false, "schema": {"type": "boolean", "default": false, "title": "Include Stats"}}, {"name": "accept", "in": "header", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Accept"}}]}}}}
RecursiveJsonSplitter
를 사용하면 복잡한 JSON 데이터를 구조적으로 이해하고, 이를 적절한 크기의 청크로 분할할 수 있습니다. 이 과정은 JSON 데이터의 분석 및 처리를 용이하게 하며, 청크 크기에 대한 엄격한 제한이 필요한 경우 추가적인 텍스트 분할기를 사용할 수 있습니다.