Chunk 알고리즘

텍스트 분할

청크는 질문과의 유사도를 계산해 관련성 높은 정보를 추출하는데 사용된다. 잘 나뉜 청크는 질문에 적합한 정보를 효과적으로 가져온다. 그치만 너무 크게 나누면 유사도가 떨어지고 너무 작게 나누면 컨텍스트를 잃을 수 있다. 문서를 분할하는 전략은 다양하며, 실험을 통해 문서와 질문에 적합한 방식을 찾아야 한다. 또 오버랩은 청크 사이에 일부 겹치는 부분을 만들어 문장이 잘리지 않도록 하는 방법을 말한다.

문자 단위로 분할하기

기본적으로 “\n\n”을 기준으로 글자 단위로 텍스트를 분할한다.

with open("data.txt") as f:
    file = f.read()

from langchai_text_splitters import CharacterTextSplitter

text_splitter = CharacterTextSplitter(
    seperator="\n\n",
    chunk_size=210,
    chunk_overlap=0,
    length_function=len,
)

text = text_splitter.create_documents([file])

# 이렇게도 쓸 수 있다.
text2 = text_splitter.create_documents([
    file, file
])

문자 단위로 재귀적으로 분할하기

특정 문자 목록을 기준으로 텍스트를 나누며, 기본적으로 단락(\n\n)을 먼저 분할하고, 이후 문장, 단어, 개별 문자까지 재귀적으로 세분화한다. 즉 설정된 청크 크기가 기준에 맞게 충분히 작아질 때까지 단락 -> 문장 -> 단어 -> 글자 순서로 점점 더 작은 단위로 나누는 것을 계속해서 청크 크기를 조절한다. 이 방식을 사용하면 단락, 문장, 단어가 서로 밀접하게 관련된 의미를 가진 하나의 덩어리로 보고 가능한 한 이런 단위가 끊어지지 않도록 유지하는 장점이 있다.

사용 방법

with open("data.txt") as f:
    file = f.read()

from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharactorTextSplitter(
    chunk_size=250,
    chunk_overlap=50,
    length_function=len,
    is_seperator_regex=False
)

texts = text_splitter.create_documents([file])

이렇게 하면 텍스트 스플리터가 파일 텍스트를 문서 단위로 분할하고, texts 리스트에 분할된 문서가 저장된다. 여기서 create_documents 메서드로 텍스트와 메터데이터가 반환된다.

여기에 split_text 메서드를 적용하면 텍스트를 분할해서 문자열 리스트로 반환한다.

text_splitter.split_text(file)

토큰 단위로 분할하기

텍스트를 효율적으로 분할하기 위해 토크나이저를 활용한다. 이는 텍스트를 토큰으로 변환하는데 사용되는 알고리즘이다. 토크나이저에 따라서 토큰 개수를 계산하는 방식이나 청크 분할 결과가 달라지므로 모델에 입력하기 전에 적절한 토크나이저를 선택하는 것이 중요하다.

텍스트 분할 방식에 따른 크기 제한의 크기

재귀적 방식을 사용하면 항상 토큰 크기 제한 내에 텍스트가 들어오게 된다. 흠.. 토크나이저 종류를 소개하는데 각각의 차이가 뭔지 잘 모르겠고 찾아봐야겟음

  • tiktoken
  • TokenTextSplitter
  • spaCy
  • SentenceTransformers
  • NLTK
  • KoNLPy
  • HuggingFace 토크나이저 중 GPT2TokenizerFast

의미 단위로 분할하기

일반적으로 텍스트를 분할할 때 글자 수나 토큰 수를 기준으로 나누지 않고 의미적으로 유사한 문장끼리 묶을 수도 있다. 이런 방식은 청크의 크기가 일정하지 않게 된다는 특징이 있다. 그렇지만 실제로는 문서에서 문단의 길이는 상황에 따라 달라질 수 있으므로 의미적으로는 사람이 이해하는 방식과 더 유사하다고 할 수 있다.

종류

  • SemanticChunker

이 토크나이저를 사용할때 문장 분할 기준점은이렇게 계산된다.

  • 먼저 문장간 유사도를 계산한다.
  • 각 문장 쌍 사이의 거리로 나타난다.
  • 계산된 거리 값들을 그래프 형태로 표현하여 문장들 간 거리가 가까운 경우와 먼 경우를 파악한다
  • 문장 간 거리를 기준으로 텍스트를 나누는 지점인 분할 기준점을 지정한다. 분할 기준점은 백분위수, 표준편차, 사분위수 범위 등으로 지정할 수 있다.
  • 임계값을 넘는 지점에서 문장이 분리되어 하나의 청크를 이루게 된다.

마크다운 헤더로 분할하기

문서를 지정된 헤더를 기준으로 분할한다.

사용 방식

from langchain_text_splitters import MarkdownHeaderTextSplitter

markdown_document = " ..."

headers_to_split_on = [
    ("#","Header 1"),
    ("##","Header 2"),
    ("###","Header 3")
]

markdown_splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=headers_to_split_on
)

splits = markdown_splitter.split_text(markdown_document)
  • 여기서 strip_headers 옵션으로 헤더 포함 여부를 결정할 수 있다.

이렇게 분할된 결과를 다시 recursive 하게 스플릿할 수 있다.

chunk_size=200
chunk_overlap=20
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=chunk_size, chunk_overlap=chunk_overlap
)
split = text_splitter.split_documents(splits)