1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LangChainのText Splittersでドキュメントを分割しよう(CharacterTextSplitter)

Posted at

はじめに

RAG(Retrieval-Augmented Generation)は、情報を効率的に取得し、それを基に応答を生成する手法です。このプロセスにおいて、大きなドキュメントを適切に分割し、関連する情報を迅速に取り出すことが非常に重要です。特に、テキストの分割方法は、検索効率や回答精度の向上に寄与するため、適切なツールの選択がカギとなります。
ここでは、LangChainライブラリに用意されているテキストを簡単に分割するための「Text Splitters」の中から、CharacterTextSplitterの基本的な使い方とその実際の動作について詳しく見ていきます。

こちらも参考にして下さい

筆者のレベル

  • RAGについて基本的な理解を持っている
  • RAGを実装することで、さらに深く学びたいと考えている
  • プログラミング経験はあまりないが、RAGを実装してみたい
  • 実際にコードを作成して動かすことで、理解を深めていきたい

記載していること

  • CharacterTextSplitterの基本的な説明
  • いくつかの設定パターンで動かしてみた結果
  • できなかったこと

CharacterTextSplitterの特徴

CharacterTextSplitterは、文字単位でテキストを分割する機能を持つText Splitterです。このツールを使用することで、文字列処理において、より効果的に情報を扱うことができます。以下に、CharacterTextSplitterの主要なパラメータを説明します。

パラメータの説明

  • chunk_size: チャンクの最大サイズを指定します。このサイズに基づいてテキストが分割されます。
  • separator: テキストを分割するためのセパレータを指定します。通常、句点や改行などが使われます。
  • is_separator_regex: セパレータが正規表現かどうかを指定します。Trueにすると、指定したセパレータが正規表現として解釈されます。
  • chunk_overlap: チャンク間のオーバーラップを指定します。これにより、前のチャンクと次のチャンクの一部が重複するようになります。

‎RAGの基本的な仕組み.‎002.jpeg

実装例

以下は、CharacterTextSplitterの基本的な使い方の例です。

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter

source_text = "あいうえお、かきくけこさしすせそ。"

chunk_size = 10
chunk_overlap = 2

char_text_splitter = CharacterTextSplitter(
    chunk_size=chunk_size,
    chunk_overlap=chunk_overlap,
    separator=''
)

documents = char_text_splitter.split_text(source_text)

動かしてみる

ドキュメントの準備

このようなテキストファイルを用意します。

sample.txt
あいうえお。
かきくけこ。さしすせそ。
たちつてと。なにぬねの。はひふへほ。
まみむめも。やゆよ。らりるれろわをん。

①チャンクサイズのみを指定してみる

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter

# テキストをロード
source_document = TextLoader('sample.txt').load()
source_text = source_document[0].page_content

# CharacterTextSplitterを設定
char_text_splitter = CharacterTextSplitter(
    chunk_size=5,
    chunk_overlap=0
)

# 分割を実行
char_documents = char_text_splitter.split_text(source_text)

# 分割結果を表示
print('\nCharacterTextSplitterによる分割結果: \n')
for i, chunk in enumerate(char_documents):
    print(f"Chunk {i+1} (Length: {len(chunk)}):\n{chunk}\n")

exit(0)

CharacterTextSplitterによる分割結果:
あれ?思ったように分割されていない・・・

Chunk 1 (Length: 58):
あいうえお。
かきくけこ。さしすせそ。
たちつてと。なにぬねの。はひふへほ。
まみむめも。やゆよ。らりるれろわをん。

②チャンクサイズとセパレータを設定してみる

セパレータに空白文字('')を設定することで、chunk_sizeで分割されました。

# CharacterTextSplitterを設定
char_text_splitter = CharacterTextSplitter(
    chunk_size=5,
    chunk_overlap=0,
    separator=''
)

CharacterTextSplitterによる分割結果:
改行は1文字とカウントされています。

Chunk 1 (Length: 5):
あいうえお

Chunk 2 (Length: 5):
。
かきく

Chunk 3 (Length: 5):
けこ。さし

Chunk 4 (Length: 4):
すせそ。

Chunk 5 (Length: 5):
たちつてと

Chunk 6 (Length: 5):
。なにぬね

Chunk 7 (Length: 5):
の。はひふ

Chunk 8 (Length: 5):
へほ。
ま

Chunk 9 (Length: 5):
みむめも。

Chunk 10 (Length: 5):
やゆよ。ら

Chunk 11 (Length: 5):
りるれろわ

Chunk 12 (Length: 3):
をん。

③オーバーラップを設定してみる

chunk_sizeは10、chunk_overlapは2を設定してみます。

# CharacterTextSplitterを設定
char_text_splitter = CharacterTextSplitter(
    chunk_size=10,
    chunk_overlap=2,
    separator=''
)

CharacterTextSplitterによる分割結果:
2文字オーバラップしているのが分かります。

Chunk 1 (Length: 10):
あいうえお。
かきく

Chunk 2 (Length: 10):
きくけこ。さしすせそ

Chunk 3 (Length: 10):
せそ。
たちつてと。

Chunk 4 (Length: 10):
と。なにぬねの。はひ

Chunk 5 (Length: 10):
はひふへほ。
まみむ

Chunk 6 (Length: 10):
みむめも。やゆよ。ら

Chunk 7 (Length: 10):
。らりるれろわをん。

④セパレータを正規表現にしてみる

セパレータに句点(。)と改行(\n)を設定します。
また、分かりやすくするために、テキストファイルの内容を変更します。

sample.txt
あいうえお。かきくけこさしすせそたちつてと。
なにぬねの。はひふへほ、まみむめも、やゆよ。らりるれろわをん。
# CharacterTextSplitterを設定
char_text_splitter = CharacterTextSplitter(
    chunk_size=10,
    chunk_overlap=2,
    is_separator_regex=True,
    separator='[。|\\n]'
)

CharacterTextSplitterによる分割結果:
2つのセパレータで分割していることが分かります。
ですが、チャンクサイズが10を超えています、また、オーバーラップしていません。

Chunk 1 (Length: 5):
あいうえお

Chunk 2 (Length: 15):
かきくけこさしすせそたちつてと

Chunk 3 (Length: 5):
なにぬねの

Chunk 4 (Length: 15):
はひふへほ、まみむめも、やゆよ

Chunk 5 (Length: 8):
らりるれろわをん

仕様を調べてみる(ChatGPT)

チャンクサイズ、オーバーラップ、セパレータ の優先度は、テキストの分割において以下のように処理されるとのこと。

1. セパレータによる分割

  • 最初に、指定されたセパレータ(文字や正規表現)でテキスト全体を分割します。
  • ここでのセパレータは、テキストをどこで区切るかを決定するもので、チャンクサイズに関係なく先に処理されます。
  • 例えば、セパレータとして句点(。)や改行(\n)を指定した場合、それを基にテキストを区切ってリストにします。

2. チャンクサイズの調整

  • セパレータで分割した後、各チャンクが設定された chunk_size(最大チャンクサイズ)を超える場合、それを更に細かく分割します。
  • 逆に、chunk_size よりも小さなチャンクであれば、そのまま次の処理へと進みます。

3. オーバーラップの適用

  • 最終的に、チャンク間で重複させる文字数を chunk_overlap で指定します。
  • 例えば、前のチャンクの最後の 2 文字が次のチャンクの最初に重なるように設定する場合、chunk_overlap=2 とします。

できなかったこと(考察)

④セパレータを正規表現にしてみる
では、想定していた動作の確認を取ることができませんでした。

②チャンクサイズとセパレータを設定してみる
では、セパレータに空白文字('')を設定することで、chunk_sizeで分割されるようになりました。

同じような設定をすればいいのかもしれませんが、正規表現で空白文字('')を設定ができないことから、この設定は難しいと考えています。

まとめ

RAG(Retrieval-Augmented Generation)においては、文脈や意味の単位でテキストを分割することが、精度向上につながります。そのため、特定の文字数で区切る方法は、利用シーンが少ないのかもしれません。

参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?