LLM 어플리케이션을 위한 Chunking Strategies

Chunking Strategies for LLM Applications

The Significance of Chunking, Text Splitting, and Conversation Memory for Prompt Engineers

LLM(언어 모델 학습) 관련 애플리케이션을 개발할 때 chunking은 큰 텍스트를 작은 세그먼트로 분할하는 과정을 의미합니다. 이 과정은 LLM을 사용하여 콘텐츠를 임베딩 한 후 벡터 데이터베이스에서 검색된 콘텐츠의 관련성을 향상시키는 데 중요합니다. 이 블로그 게시물에서는 청킹이 LLM 관련 애플리케이션의 효율성과 정확도를 개선하는 데 어떻게 도움이 되는지 살펴보겠습니다.

우리는 알고 있듯이, Pinecone에 색인화할 어떤 콘텐츠든지 먼저 임베딩해야 합니다. 청킹의 주요 목적은 가능한 한 적은 노이즈로 의미론적으로 관련성 있는 콘텐츠 조각을 임베딩하는 것을 보장하기 위함입니다.

예를 들어, 의미론적 검색에서는 특정 주제에 대한 유용한 정보를 포함하는 문서의 말뭉치를 색인화합니다. 효과적인 청킹 전략을 적용함으로써 사용자의 쿼리의 본질을 정확하게 포착하는 검색 결과를 보장할 수 있습니다. 만약 우리의 청킹이 너무 작거나 너무 크다면, 부정확한 검색 결과를 초래하거나 관련 콘텐츠를 표면화할 기회를 놓칠 수 있습니다. 경험적으로, 텍스트 청크가 인간에게 주변 맥락 없이도 의미가 있다면, 언어 모델에게도 의미가 있을 것입니다. 따라서 말뭉치 내의 문서에 대해 최적의 청킹 크기를 찾는 것은 검색 결과가 정확하고 관련성 있게 보장하는 데 중요합니다.

다른 예로는 우리가 이전에 Python과 Javascript를 사용하여 다룬 대화형 에이전트가 있습니다. 우리는 임베디드 청크를 사용하여 신뢰할 수 있는 정보에 기반한 에이전트의 지식 베이스에 기반한 대화형 에이전트의 맥락을 구축합니다. 이 상황에서 청킹 전략에 대한 올바른 선택을 하는 것이 두 가지 이유로 중요합니다: 첫째, 맥락이 실제로 우리의 프롬프트에 관련이 있는지 여부를 결정합니다. 둘째, 요청당 보낼 수 있는 토큰 수에 대한 제한을 고려하여 검색된 텍스트를 외부 모델 제공자(예: OpenAI)에 보내기 전에 맥락에 맞출 수 있는지 여부를 결정합니다. 일부 경우, 예를 들어 32k 맥락 윈도우를 가진 GPT-4를 사용할 때, 청크를 맞추는 것은 문제가 되지 않을 수 있습니다. 그러나 우리는 매우 큰 청크를 사용할 때 주의해야 합니다. 이것은 Pinecone에서 받은 결과의 관련성에 부정적인 영향을 미칠 수 있습니다.

이 글에서는 여러 청킹 방법을 살펴보고 청킹 크기와 방법을 선택할 때 고려해야 할 트레이드오프에 대해 논의할 것입니다. 마지막으로, 애플리케이션에 적합한 최적의 청킹 크기와 방법을 결정하기 위한 몇 가지 권장 사항을 제시할 것입니다.

짧고 긴 콘텐츠 임베딩 (Embedding short and long content)

콘텐츠를 임베딩할 때 콘텐츠의 길이(문장처럼 짧은 것 또는 문단 또는 전체 문서처럼 긴 것)에 따라 다양한 동작을 예상할 수 있습니다.

문장이 임베딩되면 결과 벡터는 문장의 구체적인 의미에 중점을 둡니다. 이는 다른 문장 임베딩과 비교할 때 자연스럽게 그 수준에서 비교가 이루어진다는 것을 의미합니다. 이는 또한 임베딩이 문단이나 문서에서 찾을 수 있는 더 넓은 맥락 정보를 놓칠 수 있음을 의미합니다.

전체 문단 또는 문서가 임베딩되면 임베딩 과정은 전체 맥락과 텍스트 내의 문장과 구절 사이의 관계를 모두 고려합니다. 이로 인해 텍스트의 더 넓은 의미와 주제를 포착하는 더 포괄적인 벡터 표현이 생성될 수 있습니다. 반면에 더 큰 입력 텍스트 크기는 노이즈를 도입하거나 개별 문장이나 구절의 중요성을 희석시켜 인덱스를 쿼리할 때 정확한 일치를 찾는 것이 더 어려워질 수 있습니다.

해당 텍스트는 짧은 콘텐츠와 긴 콘텐츠를 임베딩하는 것의 차이와 함의에 대해 설명합니다. 이는 문장을 임베딩하면 그 특정한 의미에 중점을 둔다고 언급하지만, 문단이나 문서와 같은 긴 텍스트를 임베딩하면 전반적인 맥락과 문장 및 구절 간의 상호 관계를 고려하며, 이는 더 포괄적인 벡터 표현을 이끌어낼 수 있습니다. 그러나 또한 큰 텍스트 입력은 노이즈를 도입하거나 개별 문장 또는 구절의 중요성을 줄일 수 있어 인덱스 쿼리 중 정확한 매칭을 어렵게 만들 수 있다고 지적합니다.

쿼리의 길이도 임베딩이 서로 어떻게 관련되는지에 영향을 미칩니다. 단일 문장이나 구절과 같은 짧은 쿼리는 구체적인 사항에 중점을 둘 것이며, 문장 수준의 임베딩과 매칭하기에 더 적합할 수 있습니다. 한 문장 이상이나 문단을 포괄하는 긴 쿼리는 문단 또는 문서 수준의 임베딩과 더 조화를 이룰 수 있습니다. 왜냐하면 이는 더 넓은 맥락이나 주제를 찾고 있을 가능성이 있기 때문입니다.

색인은 또한 비균질적일 수 있으며 다양한 크기의 청킹에 대한 임베딩을 포함할 수 있습니다. 이것은 쿼리 결과의 관련성 측면에서 도전을 제시할 수 있지만, 긍정적인 결과를 가져올 수도 있습니다. 한편으로는, 긴 콘텐츠와 짧은 콘텐츠의 의미론적 표현 사이의 차이 때문에 쿼리 결과의 관련성이 변동할 수 있습니다. 반면에, 비균질적인 색인은 텍스트의 다양한 정밀도 수준을 나타내는 다양한 청킹 크기로 인해 더 넓은 범위의 맥락과 정보를 포착할 수 있습니다. 이것은 더 유연하게 다양한 유형의 쿼리를 수용할 수 있습니다.

해당 텍스트는 쿼리의 길이가 임베딩 간의 관계에 어떻게 영향을 미치는지, 그리고 다양한 청킹 크기를 가진 비균질 인덱스를 가질 때의 잠재적인 도전과 이점에 대해 논의합니다. 짧은 쿼리는 문장 수준의 임베딩과 더 잘 매칭될 수 있으며, 반면에 긴 쿼리는 문단 또는 문서 수준의 임베딩과 더 잘 맞을 수 있습니다. 비균질 인덱스는 의미론적 표현의 차이로 인해 쿼리 결과의 관련성에 도전을 제시할 수 있지만, 또한 더 넓은 범위의 맥락과 정보를 포착하고 더 유연하게 다양한 유형의 쿼리를 수용할 수도 있습니다.

청킹 고려 사항(Chunking Considerations)

최적의 청킹 전략을 결정하는 데에는 여러 변수가 작용하며, 이러한 변수는 사용 사례에 따라 달라집니다. 다음은 유의해야 할 몇 가지 주요 측면입니다:

  1. 색인화되는 콘텐츠의 성격은 무엇인가요? 긴 문서(예: 기사 또는 책)와 같은 긴 콘텐츠를 다루고 있나요, 아니면 트윗이나 즉시 메시지와 같은 짧은 콘텐츠를 다루고 있나요? 답변은 당신의 목표에 더 적합한 모델과 따라서 어떤 청킹 전략을 적용할지를 결정할 것입니다.
  2. 어떤 임베딩 모델을 사용하고 있으며, 이 모델은 어떤 청킹 크기에서 최적으로 수행되나요? 예를 들어, 문장 변환 모델은 개별 문장에서 잘 작동하지만 text-embedding-ada-002와 같은 모델은 256 또는 512 토큰을 포함하는 청킹에서 더 잘 작동합니다.
  3. 사용자 쿼리의 길이와 복잡성에 대한 기대치는 무엇인가요? 짧고 구체적일까요, 아니면 길고 복잡할까요? 이것은 임베디드 쿼리와 임베디드 청킹 사이에 더 가까운 상관 관계가 있도록 콘텐츠를 청킹하는 방법을 결정하는 데에도 도움이 될 수 있습니다.
  4. 검색된 결과가 특정 애플리케이션 내에서 어떻게 활용되나요? 예를 들어, 의미론적 검색, 질문 응답, 요약 또는 기타 목적으로 사용되나요? 예를 들어, 결과가 토큰 제한이 있는 다른 LLM에 공급되어야 하는 경우, 이를 고려해야 하며, LLM에 요청할 청킹 수에 기반하여 청킹의 크기를 제한해야 합니다.

이러한 질문에 답하면 성능과 정확성 사이의 균형을 맞춘 청킹 전략을 개발할 수 있으며, 이는 차례로 쿼리 결과가 더 관련성 있게 됨을 보장할 것입니다.

청킹 방법(Chunking methods)

청킹을 위한 다양한 방법이 있으며, 각각이 다른 상황에 적합할 수 있습니다. 각 방법의 장단점을 검토함으로써, 우리의 목표는 그것들을 적용할 적절한 시나리오를 식별하는 것입니다.

고정 크기 청킹(Fixed-size chunking)

이것은 청킹에 대한 가장 일반적이고 간단한 접근 방식입니다: 우리는 단순히 청킹의 토큰 수를 결정하고 선택적으로 그들 사이에 어떠한 겹침이 있어야 하는지 결정합니다. 일반적으로, 우리는 청킹 사이에 의미론적 맥락이 사라지지 않도록 청킹 사이에 약간의 겹침을 유지하려고 할 것입니다. 고정 크기 청킹은 대부분의 일반적인 경우에 가장 좋은 방법일 것입니다. 다른 형태의 청킹에 비해 고정 크기 청킹은 계산상 저렴하고 사용하기 쉬우며 어떤 자연어 처리(NLP) 라이브러리도 필요로 하지 않습니다.

다음은 LangChain을 사용하여 고정 크기 청킹을 수행하는 예입니다:

text = "..." # your text
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
    separator = "\n\n",
    chunk_size = 256,
    chunk_overlap  = 20
)
docs = text_splitter.create_documents([text])

“콘텐츠 인식” 청킹 (“Content-aware” Chunking)

이것들은 우리가 청킹하는 콘텐츠의 성격을 활용하고 더 정교한 청킹을 적용하기 위한 일련의 방법입니다. 다음은 몇 가지 예입니다:

문장 분리(Sentence splitting)

앞서 언급했듯이, 많은 모델은 문장 수준의 콘텐츠를 임베딩하기에 최적화되어 있습니다. 당연히, 우리는 문장 청킹을 사용하고, 이를 수행하기 위해 여러 접근 방식과 도구를 사용할 수 있습니다. 이에는 다음과 같은 것들이 포함됩니다:

단순 분리: 가장 단순한 접근 방식은 문장을 마침표(“.”)와 줄 바꿈으로 분리하는 것입니다. 이 방법은 빠르고 간단할 수 있지만, 이러한 접근 방식은 모든 가능한 에지 케이스를 고려하지 않습니다. 다음은 매우 간단한 예입니다:

text = "..." # your text
docs = text.split(".")

NLTK: Natural Language Toolkit(NLTK)은 인간 언어 데이터를 처리하기 위한 인기 있는 Python 라이브러리입니다. 이 라이브러리는 텍스트를 문장으로 분리할 수 있는 문장 토크나이저를 제공하여 더 의미 있는 청킹을 생성하는 데 도움이 됩니다. 예를 들어, LangChain과 함께 NLTK를 사용하려면 다음과 같이 할 수 있습니다:

text = "..." # your text
from langchain.text_splitter import NLTKTextSplitter
text_splitter = NLTKTextSplitter()
docs = text_splitter.split_text(text)

Natural Language Toolkit(NLTK)는 주로 영어 텍스트 처리를 위해 설계되었습니다. 이는 영어의 심볼릭 및 통계적 자연어 처리(NLP)에 중점을 둔 라이브러리와 프로그램 세트를 통해 증명됩니다【36†(Wikipedia)】. 그러나 공식 문서나 사용 가능한 소스에서 NLTK가 한국어 처리를 상당히 지원하는지 여부가 명시적으로 언급되지 않았습니다.

Stack Overflow의 토론에서는 한국어를 포함한 언어에 대한 지원을 검색하지만, 이는 NLTK 내에서 이러한 언어에 대한 지원이 제한적일 수 있음을 제안합니다【35†(Stack Overflow)】.

한국어 텍스트를 처리하려면 KoNLPy와 같은 라이브러리나 MeCab과 같은 프레임워크를 고려할 수 있습니다. 이러한 도구들은 한국어 텍스트 처리에 더 적합할 수 있으며, 한국어 처리를 위해 특별히 설계되거나 문서화된 지원을 제공할 수 있습니다.

spaCy: spaCy는 NLP 작업을 위한 또 다른 강력한 Python 라이브러리입니다. 이 라이브러리는 텍스트를 별도의 문장으로 효율적으로 나눌 수 있는 정교한 문장 세분화 기능을 제공하여 결과 청킹에서 더 나은 맥락 보존을 가능하게 합니다. 예를 들어, LangChain과 함께 spaCy를 사용하려면 다음과 같이 할 수 있습니다:

text = "..." # your text
from langchain.text_splitter import SpacyTextSplitter
text_splitter = SpacyTextSplitter()
docs = text_splitter.split_text(text)

spaCy는 Python을 위한 자연어 처리(Natural Language Processing, NLP) 라이브러리로, 다양한 NLP 작업을 수행할 수 있는 기능을 제공합니다. 이 라이브러리는 명명된 엔터티 인식(NER), 품사 태깅(POS tagging), 의존성 구문 분석(dependency parsing), 단어 벡터(word vectors) 등을 특징으로 하고 있습니다. spaCy는 한국어에 대한 지원이 포함되어 있으며, 공식 문서에 따르면 한국어에 대한 3개의 패키지가 제공됩니다.

또한, Stack Overflow의 특정 게시물에서는 사전 훈련된 모델을 사용하여 한국어 문장을 lemmatize(원형 복원)하려는 시도가 언급되어 있으며, 이에 대해 다른 언어와 한국어 모델 문서의 예를 따라 시도했다고 설명되어 있습니다【48†(Stack Overflow)】. 또한, GitHub의 이슈에서는 spaCy에 한국어 지원을 활성화하려는 논의도 있었다고 합니다【49†(GitHub)】.

이 정보를 종합하면, spaCy는 한국어 처리를 위한 지원을 제공하며, 특정 NLP 작업을 수행하기 위해 한국어에 대한 패키지를 제공한다는 것을 알 수 있습니다. 그러나 특정 작업에 대한 성능이나 정확도는 다양할 수 있으므로, spaCy의 한국어 처리 능력을 평가하려면 추가적인 테스트와 평가가 필요할 수 있습니다.

재귀 청킹(Recursive Chunking)

재귀 청킹은 일련의 구분자를 사용하여 입력 텍스트를 계층적이고 반복적인 방식으로 작은 청킹으로 나눕니다. 초기 텍스트 분할이 원하는 청킹 크기 또는 구조를 생성하지 않으면 메서드는 원하는 청킹 크기 또는 구조가 달성될 때까지 결과 청킹에 대해 다른 구분자 또는 기준을 사용하여 자체를 재귀적으로 호출합니다. 이는 청킹이 정확하게 동일한 크기가 아니더라도 유사한 크기를 갖도록 “지향한다”는 것을 의미합니다.

다음은 LangChain을 사용하여 재귀 청킹을 사용하는 방법에 대한 예입니다:

text = "..." # your text
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
    # 정말 작은 청킹 크기를 설정하십시오, 단순히 보여주기 위해.
    chunk_size = 256,
    chunk_overlap  = 20
)
docs = text_splitter.create_documents([text])

특수 청킹(Specialized chunking)

Markdown과 LaTeX는 청킹 과정 중에 콘텐츠의 원래 구조를 유지하기 위해 특수 청킹 방법을 사용할 수 있는 구조화되고 형식화된 콘텐츠의 두 예입니다.

Markdown: Markdown은 텍스트를 형식화하는 데 일반적으로 사용되는 경량 마크업 언어입니다. Markdown 구문(예: 헤딩, 목록 및 코드 블록)을 인식함으로써, 구조와 계층에 기반하여 콘텐츠를 지능적으로 나눌 수 있으며, 이로 인해 더 의미론적으로 일관된 청킹이 생성됩니다. 예를 들면:

from langchain.text_splitter import MarkdownTextSplitter
markdown_text = "..."
markdown_splitter = MarkdownTextSplitter(chunk_size=100, chunk_overlap=0)
docs = markdown_splitter.create_documents([markdown_text])

LaTex: LaTeX는 학술 논문 및 기술 문서에 자주 사용되는 문서 준비 시스템 및 마크업 언어입니다. LaTeX 명령 및 환경을 파싱함으로써, 콘텐츠의 논리적 구성(예: 섹션, 하위 섹션 및 방정식)을 존중하는 청킹을 생성할 수 있으며, 이로 인해 더 정확하고 문맥상 관련성 있는 결과가 도출됩니다. 예를 들면:

from langchain.text_splitter import LatexTextSplitter
latex_text = "..."
latex_splitter = LatexTextSplitter(chunk_size=100, chunk_overlap=0)
docs = latex_splitter.create_documents([latex_text])

애플리케이션에 대한 최적의 청킹 크기 결정하기

일반적인 청킹 접근법, 예를 들어 고정 청킹이 사용 사례에 쉽게 적용되지 않는 경우 최적의 청킹 크기를 결정하는 데 도움이 될 몇 가지 지침을 제공합니다.

  1. 데이터 전처리: 애플리케이션에 대한 최적의 청킹 크기를 결정하기 전에 먼저 데이터를 전처리하여 품질을 보장해야 합니다. 예를 들어, 데이터가 웹에서 검색된 경우 HTML 태그 또는 노이즈를 추가하는 특정 요소를 제거해야 할 수 있습니다.
  2. 청킹 크기 범위 선택: 데이터 전처리가 완료되면 다음 단계는 테스트할 잠재적인 청킹 크기 범위를 선택하는 것입니다. 앞서 언급했듯이 선택은 콘텐츠의 성격(예: 짧은 메시지 또는 긴 문서), 사용할 임베딩 모델 및 기능(예: 토큰 제한)을 고려해야 합니다. 목표는 맥락을 보존하고 정확성을 유지하는 사이의 균형을 찾는 것입니다. 더 세밀한 의미 정보를 캡처하기 위해 더 작은 청킹(예: 128 또는 256 토큰)을 탐색하고 더 많은 맥락을 유지하기 위해 더 큰 청킹(예: 512 또는 1024 토큰)을 포함하여 다양한 청킹 크기를 탐색하십시오.
  3. 각 청킹 크기의 성능 평가: 여러 인덱스를 사용하거나 여러 네임스페이스가 있는 단일 인덱스를 사용하여 다양한 청킹 크기를 테스트할 수 있습니다. 대표 데이터 세트를 사용하여 테스트할 청킹 크기에 대한 임베딩을 생성하고 인덱스(또는 인덱스)에 저장합니다. 그런 다음 품질을 평가할 수 있는 일련의 쿼리를 실행하고 다양한 청킹 크기의 성능을 비교할 수 있습니다. 이는 콘텐츠 및 예상 쿼리에 대한 최고 성능의 청킹 크기를 결정할 수 있을 때까지 다양한 쿼리에 대해 다양한 청킹 크기를 테스트하는 반복 프로세스일 가능성이 높습니다.

결론

대부분의 경우 콘텐츠를 청킹하는 것은 꽤 간단하지만, 일반적인 경로에서 벗어날 때 일부 도전과제가 제시될 수 있습니다. 청킹에 대한 일체형 해결책은 없으므로 한 사용 사례에서 작동하는 것이 다른 사용 사례에서 작동하지 않을 수 있습니다. 이 포스트를 통해 애플리케이션에 대한 청킹 접근 방식에 대한 더 나은 직관을 얻을 수 있기를 바랍니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다