재귀 문자 텍스트 분할

재귀 문자 텍스트 분할 (RecursiveCharacterTextSplitter)

텍스트를 청크로 쪼갤 때 가장 많이 쓰이는 분할기가 바로 재귀 문자 텍스트 분할기예요. 이름에서 느껴지듯 구분자 목록을 순서대로 시도하면서 청크가 충분히 작아질 때까지 재귀적으로 나누는 방식이에요. 일반 텍스트에는 이 분할기가 공식 권장 사항이라서, 어떤 분할기를 쓸지 고민될 때는 일단 이걸 선택하면 돼요.

출처: 공식문서

분할 방식

이 분할기의 동작은 두 가지로 요약할 수 있어요.

  1. 텍스트를 어떻게 나누나: 문자 구분자 목록에 따라요.
  2. 청크 크기를 어떻게 재나: 문자 수로 측정해요.

구분자 목록은 문자 배열로 설정하며, 기본값은 ["\n\n", "\n", " ", ""]예요. 이 순서 덕분에 가능한 한 문단(그리고 문장, 단어)을 한 덩어리로 유지하려고 해요. 문단 경계가 없으면 문장으로, 문장도 없으면 단어로 내려가면서 나누기 때문에, 의미적으로 가장 강하게 연결된 텍스트 조각끼리 뭉치게 되는 거죠.

설치

Python

pip install -qU langchain-text-splitters

JavaScript

npm install @langchain/textsplitters
pnpm install @langchain/textsplitters
yarn add @langchain/textsplitters
bun add @langchain/textsplitters

사용 방법

문자열 그대로의 결과가 필요하면 .split_text를, 이후 작업에 쓸 Document 객체가 필요하면 .create_documents를 사용해요.

from langchain_text_splitters import RecursiveCharacterTextSplitter

# Load example document
with open("state_of_the_union.txt") as f:
    state_of_the_union = f.read()

text_splitter = RecursiveCharacterTextSplitter(
    # Set a really small chunk size, just to show.
    chunk_size=100,
    chunk_overlap=20,
    length_function=len,
    is_separator_regex=False,
)
texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])
print(texts[1])
page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and'
page_content='of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.'
print(text_splitter.split_text(state_of_the_union)[:2])
['Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and',
 'of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.']

JavaScript 예시

문자열을 직접 얻으려면 .splitText, Document 객체가 필요하면 .createDocuments를 쓰면 돼요.

import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";

const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 100, chunkOverlap: 0 })
const texts = splitter.createDocuments([{ pageContent: "..." }])
[
  { pageContent: "...", metadata: {} },
]

주요 매개변수

RecursiveCharacterTextSplitter에서 위 예시에 쓴 매개변수들을 차례로 살펴볼게요.

  • chunk_size: 청크 하나의 최대 크기예요. 실제 크기는 length_function이 결정해요.
  • chunk_overlap: 청크 사이의 목표 중첩량이에요. 문맥이 두 청크로 나뉘어 정보가 유실되는 걸 완화해 줘요.
  • length_function: 청크 크기를 결정하는 함수예요.
  • is_separator_regex: 구분자 목록(기본값 ["\n\n", "\n", " ", ""])을 정규식으로 해석할지 여부예요.

단어 경계가 없는 언어의 텍스트 분할

중국어, 일본어, 태국어처럼 단어 경계가 없는 문자 체계도 있어요. 기본 구분자 목록인 ["\n\n", "\n", " ", ""]으로 이 텍스트를 나누면 단어가 청크 사이에서 잘릴 수 있죠. 단어를 온전히 유지하려면 구분자 목록에 추가 구두점을 넣어 재정의하면 돼요.

text_splitter = RecursiveCharacterTextSplitter(
    separators=[
        "\n\n",
        "\n",
        " ",
        ".",
        ",",
        "\u200b",  # Zero-width space
        "\uff0c",  # Fullwidth comma
        "\u3001",  # Ideographic comma
        "\uff0e",  # Fullwidth full stop
        "\u3002",  # Ideographic full stop
        "",
    ],
    # Existing args
)
const splitter = new RecursiveCharacterTextSplitter({
  separators: [
    "\n\n",
    "\n",
    " ",
    ".",
    ",",
    "\u200b",  // Zero-width space
    "\uff0c",  // Fullwidth comma
    "\u3001",  // Ideographic comma
    "\uff0e",  // Fullwidth full stop
    "\u3002",  // Ideographic stop
    "",
  ],
});

더 알아보기 (Learn more)