🦜

【LangChain】長文テキスト処理する機能「Text Splitters」読解メモ

2024/02/09に公開

「LangChain」のLLMで長文参照する時のテキスト処理をしてくれる「Text Splitters」機能のメモです。
https://python.langchain.com/docs/modules/data_connection/document_transformers/

Text Splittersとは

「Text Splitters」は、長すぎるテキストを指定サイズに収まるように分割して、いくつかのまとまりを作る処理です。

分割方法にはいろんな方法があり、指定文字で分割したり、Jsonやhtmlの構造で分割したりできます。

Text Splittersの種類

具体的には下記8つの方法がありました。

名前 説明
Split by character
docs
指定文字で分割してまとめる最もシンプルな方法。
(default: "")
Recursively split by character
docs
指定文字リストに従って再帰的に分割し、関連するテキストをできるだけ隣同士に保ちつつまとめる方法。
(default: ["\n\n", "\n", " ", ""])
Recursively split JSON
docs
JSONデータの階層ごとに調べながら分割し、ネストされたオブジェクトを可能な限り保持しつつまとめる方法。リストを辞書に変換する前処理オプションもあり。
HTMLHeaderTextSplitter
docs
HTML特有の文字でテキストを分割してまとめる方法。分割されたチャンクがどこから来たかの情報も追加します。
MarkdownHeaderTextSplitter
docs
Markdown特有の文字で分割してまとめる方法。分割されたチャンクがどこから来たかの情報も追加します。
Split code
docs
Python、JSなどのコーディング言語特有の文字で分割してまとめる方法。利用可能な言語は15種類です。
Split by tokens
docs
トークンで分割してまとめる方法。トークン測定方法はバリエーションあり。
Semantic Chunking
docs
文に基づいて最初に分割し、隣接する文がセマンティックに類似していれば組み合わせます。Greg Kamradt氏によるもの

1. Split by character

テキストを指定した文字列で分割して、指定チャンクサイズに収まるようにまとめる方法。最もシンプル。

コード例
# ==================================================
# 1. Split by character
# ==================================================
from langchain.text_splitter import CharacterTextSplitter


# --- 長文テキスト ---
too_long_text = """..."""

# --- インスタンス生成 ---
text_splitter = CharacterTextSplitter(
    separator="\n\n",         # チャンクの区切り文字
    chunk_size=1000,          # チャンクの最大文字数
    chunk_overlap=200,        # チャンク間の重複する文字数
    length_function=len,      # 文字数で分割
    is_separator_regex=False, # separatorを正規表現として扱う場合はTrue
)

# --- テキスト分割してリストを取得 ---
texts = text_splitter.create_documents([too_long_text])
print(texts[0])
主要メソッド
  • split_text
    • 出力: List[str]
    • 分割されたテキストのチャンクのリスト
  • create_documents
    • 出力: List[Document]
    • 分割されたテキストチャンクから生成されたDocumentオブジェクトのリスト
  • split_documents
    • 出力: List[Document]
    • 元のDocumentオブジェクト内のテキストを分割して生成された新たなDocumentオブジェクトのリスト
# Documentオブジェクト
{
    page_content: str,
    metadatas: dict
}
複数の長文テキストをまとめて分割したい場合
  • メタデータを含めて出力できる
# --- 長文テキスト ---
too_long_text_1 = """..."""
too_long_text_2 = """..."""

# --- メタデータ ---
metadatas = [{"document": 1}, {"document": 2}]

# --- インスタンス生成 ---
documents = text_splitter.create_documents(
    [too_long_text_1, too_long_text_2], # テキストリスト
    metadatas=metadatas                 # メタデータリスト
)
print(documents[0])

https://python.langchain.com/docs/modules/data_connection/document_transformers/character_text_splitter

2. Recursively split by character

テキストを指定した文字列のリストで分割して、指定チャンクサイズに収まるようにまとめる方法。

コード例
# ==================================================
# 2. Recursively split by character
# ==================================================
from langchain.text_splitter import RecursiveCharacterTextSplitter


# --- 長文テキスト ---
too_long_text = """..."""

# --- インスタンス生成 ---
text_splitter = RecursiveCharacterTextSplitter(
    # separators=["\n\n", "\n", " ", ""], # チャンクの区切り文字リスト
    chunk_size=100,           # チャンクの最大文字数
    chunk_overlap=20,         # チャンク間の重複する文字数
    length_function=len,      # 文字数で分割
    is_separator_regex=False, # separatorを正規表現として扱う場合はTrue
)

# --- テキスト分割してリストを取得 ---
texts = text_splitter.create_documents([too_long_text])
print(texts[0])
主要メソッド: RecursiveCharacterTextSplitter
  • split_text
    • 出力: List[str]
    • 分割されたテキストのチャンクのリスト
  • create_documents
    • 出力: List[Document]
    • 分割されたテキストチャンクから生成されたDocumentオブジェクトのリスト
  • split_documents
    • 出力: List[Document]
    • 元のDocumentオブジェクト内のテキストを分割して生成された新たなDocumentオブジェクトのリスト
# Documentオブジェクト
{
    page_content: str,
    metadatas: dict
}

https://python.langchain.com/docs/modules/data_connection/document_transformers/recursive_text_splitter

3. Recursively split JSON

JSONデータの階層ごとに調べながら分割し、ネストされたオブジェクトを可能な限り保持しつつまとめる方法。

コード例
# ==================================================
# 3. Recursively split JSON
# ==================================================
from langchain.text_splitter import RecursiveJsonSplitter


# --- Jsonデータ ---
json_data = {
    "text": """..."""
}

# --- インスタンス生成 ---
splitter = RecursiveJsonSplitter(
    max_chunk_size=300       # チャンクの最大文字数
)

# --- テキスト分割してリストを取得 ---
texts = splitter.split_text(json_data=json_data)
print(texts[0])
主要メソッド: RecursiveJsonSplitter
  • split_json
    • 出力: List[Dict]
    • JSONデータを再帰的に分割し、構造を保持したままサイズ制限内のJSONチャンクのリストを生成します。
  • split_text
    • 出力: List[str]
    • JSONデータを再帰的に分割し、分割された各チャンクをJSON形式の文字列リストとして返します。
  • create_documents
    • 出力: List[Document]
    • 分割されたJSONデータチャンクからDocumentオブジェクトのリストを生成します。各Documentには、分割されたJSONチャンクの内容とオプションでメタデータが含まれます。

https://python.langchain.com/docs/modules/data_connection/document_transformers/recursive_json_splitter

4. HTMLHeaderTextSplitter

HTMLのデータを、HTML特有の文字でテキストを分割してまとめる方法。またURLを指定してHTMLを取得、分割してまとめることもできます。

コード例
  • 「HTML」から分割データを取得
# ==================================================
# 4. HTMLHeaderTextSplitter
# ==================================================
from langchain.text_splitter import HTMLHeaderTextSplitter


# --- 取得元: HTMLテキスト ---
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"),
]

# --- 取得インスタンス: TextSplitter生成 ---
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)

# --- テキスト分割してリストを取得 ---
result = html_splitter.split_text(html_string)
result
  • 「URL」からHTMLを取得して分割データを取得
from langchain.text_splitter import HTMLHeaderTextSplitter, RecursiveCharacterTextSplitter


# --- 取得元: HTMLを取得するURL ---
url = "https://example.com"

# --- 取得方法: 分割するタグ指定 ---
headers_to_split_on = [
    ("h1", "Header 1"),
    ("h2", "Header 2"),
]

# --- 取得インスタンス: TextSplitter生成 ---
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)

# --- 出力 ---
result_1 = html_splitter.split_text_from_url(url)

# --- 取得インスタンス2: TextSplitter生成 ---
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=3000,   # チャンクの最大文字数
    chunk_overlap=400  # チャンク間の重複する文字数
)

# --- 出力2 ---
result_2 = text_splitter.split_documents(html_header_splits)
result_2
主要メソッド: HTMLHeaderTextSplitter
  • aggregate_elements_to_chunks
    • 出力: List[Document]
    • HTML要素を共通のメタデータに基づいてチャンクにまとめ、それぞれのチャンクからDocumentオブジェクトのリストを生成します。
  • split_text_from_url
    • 出力: List[Document]
    • 指定されたURLからHTMLを取得し、それを分割してDocumentオブジェクトのリストを生成します。
  • split_text
    • 出力: List[Document]
    • 文字列形式のHTMLテキストを分割してDocumentオブジェクトのリストを生成します。
  • split_text_from_file
    • 出力: List[Document]
    • ファイル形式のHTMLを分割してDocumentオブジェクトのリストを生成します。

https://python.langchain.com/docs/modules/data_connection/document_transformers/HTML_header_metadata

5. MarkdownHeaderTextSplitter

コード例
# ==================================================
# 5. MarkdownHeaderTextSplitter
# ==================================================
from langchain.text_splitter import MarkdownHeaderTextSplitter

# --- Markdownテキスト ---
markdown_document = "# Foo\n\n    ## Bar\n\nHi this is Jim\n\nHi this is Joe\n\n ### Boo \n\n Hi this is Lance \n\n ## Baz\n\n Hi this is Molly"

# --- 分割するタグ指定 ---
headers_to_split_on = [
    ("#", "Header 1"),   # タグ名, ヘッダー名
    ("##", "Header 2"),
    ("###", "Header 3"),
]

# --- TextSplitter生成 ---
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
md_header_splits = markdown_splitter.split_text(markdown_document)
print(md_header_splits)
主要メソッド: MarkdownHeaderTextSplitter
  • aggregate_lines_to_chunks
    • 出力: List[Document]
    • 説明: 与えられた行を共通のメタデータに基づいてチャンクに組み合わせ、それぞれのチャンクからDocumentオブジェクトのリストを生成します。
  • split_text
    • 出力: List[Document]
    • 説明: マークダウン形式のテキストを指定されたヘッダーに基づいて分割し、分割された各チャンクからDocumentオブジェクトのリストを生成します。
  • split_text_from_urlsplit_documentsメソッドは、このクラスには直接含まれていませんが、類似の機能を実装することは可能です。split_textメソッドの動作を基に、特定のURLから取得したマークダウンコンテンツの分割や、既存のDocumentオブジェクトをさらに分割する機能をカスタムメソッドとして追加することが考えられます。

https://python.langchain.com/docs/modules/data_connection/document_transformers/markdown_header_metadata

6. Split code

コード例
from langchain.text_splitter import (
    Language,
    RecursiveCharacterTextSplitter,
)

https://python.langchain.com/docs/modules/data_connection/document_transformers/code_splitter

7. Split by tokens

コード例
pip install tiktoken
# ==================================================
# 7. Split by tokens
# ==================================================
from langchain.text_splitter import CharacterTextSplitter


# --- 長文テキスト ---
too_long_text = """..."""

# --- インスタンス生成: TikTokenEncoder ---
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=100,  # チャンクの最大文字数
    chunk_overlap=0  # チャンク間の重複する文字数
)

# --- テキスト分割してリストを取得 ---
texts = text_splitter.split_text(too_long_text)
print(texts[0])

https://python.langchain.com/docs/modules/data_connection/document_transformers/split_by_token

8. Semantic Chunking

テキストを分割し、その後、埋め込み空間(embedding space)で類似している文をグループ化してマージする方法。

https://python.langchain.com/docs/modules/data_connection/document_transformers/semantic-chunker

参考記事

https://python.langchain.com/docs/modules/data_connection/document_transformers/
https://www.pinecone.io/learn/chunking-strategies/

https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/text_splitter.py

Discussion