戦略について chunking
ローカルRAGでは、まず300〜500トークン、重複率0〜10%のチャンクから始めてください。文字単位ではなく段落や見出しの境界で分割し、自分で用意した質問で性能を測定してから調整します。意味に基づくチャンク分割は、そのコストに見合うと証明されていません。2024年の研究では、得られる改善は追加の計算コストを正当化しないと結論づけられています。最も重要な設定は、テキストの自然な境界で分割することです。
文書をどのようにテキストのまとまりに分割するかによって、検索で見つけられる内容が決まります。不適切な位置で分割すると、回答全体を含むまとまりがなくなり、大きすぎるまとまりでは回答がノイズに埋もれます。このガイドでは、一般的な分割方法を比較し、公表された研究に基づく初期設定のサイズを示します。また、単位(トークンか文字か)の落とし穴を説明し、お使いの文書で設定の選択が適切かを検証する手順を提案します。
#チャンク分割が埋め込みモデルと同じくらい重要な理由
チャンク分割とは、文書をインデックス化する前に、複数の部分に分割する処理です。各部分にベクトルが割り当てられ、検索で取得されて言語モデルに渡されるのは、その部分であって、文書全体ではありません。その後のすべての処理は、この分割に左右されます。質問の答えとなる文が2つの部分にまたがって分断されると、どちらにも文全体が含まれず、検索で見逃されます。また、1つの部分に3つの話題が混在すると、そのベクトルは曖昧な平均となり、どの質問とも強い類似性を示さなくなります。より高性能な埋め込みでも、不適切な分割は修正できません。与えられたものしかベクトル化できないからです。
分割戦略を評価した Chroma の研究が示すように、RecursiveCharacterTextSplitter のようなヒューリスティック手法は、パラメータを適切に設定すれば、実際の運用で良い結果を出すことが多く、選択したチャンクサイズと重複量によって結果は大きく変わります。したがって、このガイドでは、一般に当てはまる数値的な改善を約束しません。唯一確かなのは、お使いの文書で測定することです。
#チャンクのサイズはどのくらいに設定すべきですか
あなたのドキュメント、あなたのAI:あなたのPDF、メモ、メールを扱う信頼性の高いローカルRAG。何もクラウドに送信しません。
- 永久に利用できるオンラインスペース
- PDF + ファイル
- 生涯アップデート
どのコーパスにも適したサイズはありませんが、目安はあります。Chromaの研究では、重複なしで400トークン以下のサイズの場合、再帰的な分割がトークン単位の分割を上回ります。サイズが大きい場合や重複がある場合には、挙動が異なります。OpenAIのファイル検索ツールのデフォルト設定である800トークン、重複400トークンは、このベンチマークでは再現率が平均をわずかに下回り、ほかの指標では最も低いスコアとなっています。以下の目安はこの結果に基づくものであり、あなたのコーパスでも正確に当てはまると主張するものではありません。
| サイズ | 適した対象 | リスク |
|---|---|---|
| 200から300トークン | 情報量の多い文書に含まれる具体的な事実(日付、条項、値)についての質問 | 回答の前後の文脈が失われます。セクションのタイトルを添えることを検討してください。 |
| 300~500トークン | ドキュメント、手順書、記事を扱う際の基本的な目安 | リスクは低く、まず試すべき範囲です |
| 500から800トークン | 論旨が複数の段落にわたって展開される論説文 | 複数の考えが一つのベクトルに混在して、それぞれの特徴が薄まり、プロンプトも早く埋まります |
| 1,000 トークン超 | 検索にはほとんど適さないため、階層的に分割する際の親レベルに限って使う | ベクトルが汎用的すぎて、文章の各部分を選別しにくい |
#トークンか文字か:単位の誤解
ライブラリによって、数える単位は異なります。LlamaIndexのSentenceSplitterでは、chunk_sizeとchunk_overlapをトークン単位で指定します。ドキュメントによると、デフォルト値はそれぞれ1024と200です。LangChainのRecursiveCharacterTextSplitterなど、他のツールではデフォルトで文字数を数えます。お使いのバージョンのlength_functionパラメータを確認してください。同じ「500」を設定しても、どちらのツールを使うかによってチャンクの大きさには約4倍の差が生じます。2つ目の制約は、埋め込みモデル自体に最大入力長があることです。BGE-M3モデルは、モデルの説明によると最大8,192トークンの入力に対応しています。より古いモデルや軽量なモデルでは、受け入れられる入力長がはるかに短く、上限を超えると文章の末尾が無視されます。チャンクの大きさを決める前にモデルの上限を確認し、目測ではなく、そのモデルのトークナイザーで数えてください。
#重複:有用ですが、必ずしもそうではありません
オーバーラップは、あるチャンクの末尾を次のチャンクの先頭で繰り返すことで、境界で分断された文が少なくともどちらか一方では読めるようにする仕組みです。ただし、チャンク数の増加、インデックスの肥大化、検索結果の重複というコストがあります。Chromaの研究では、オーバーラップを減らすとIoUスコアが改善すると指摘されています。IoUは、冗長な情報を減点する指標です。固定サイズで分割する場合にはオーバーラップを設ける理由がありますが、すでに段落や見出しに沿って分割している場合は、その重要性は低くなります。分割位置が自然な区切りに一致するためです。
- 0トークン
- 段落や構造ごとに分割すれば十分です。最も経済的です。
- サイズの10〜15%
- 文単位または文字数で分割する場合に、バランスのよい選択です。
- 25%超
- 妥当なケースはまれです。重複が多く、結果にはほぼ同じ文章が並びます。
#分割戦略:最も簡単なものから最もコストの高いものまで
#1. 固定文字数で
テキストの内容を考慮せず、N文字またはNトークンごとに分割する方法です。最も単純で、内容を最も損ないやすい方法でもあります。単語、文、表の途中で分割してしまいます。プロトタイプに限って使うべき方法です。
#2. パラグラフごとまたは文ごと
ダブル改行や記号の削除後、ターゲットサイズまで単位をまとめていく。コストゼロで明確な境界でチャンクが開始・終了するため、大幅な改善となる。
#3. 再帰的
まず大きな区切り(段落、行、文、スペース)での分割を試し、文字単位での分割は最後の手段にします。これは主要なフレームワークのデフォルト動作であり、堅実な出発点です。Chromaの研究では、この種の分割方法は適切に設定すれば良好な結果を得られることが多いとしています。
#4. ドキュメントの構造に従って
見出し、リスト、表、コードブロックの構造を保ちます。たとえば、LlamaIndex の MarkdownNodeParser は見出しに沿って分割し、各ノードに、そのノードに至る見出しの階層を付加します。これにより、パッセージの本文だけでは得られない文脈を与えられます。技術ドキュメント、Wiki、エクスポートした HTML ページには最適な方法です。
#5. 意味に基づく分割
文ごとに埋め込みを計算し、隣接する二つの文の間で類似度が低下する箇所で分割します。LlamaIndex の SemanticSplitterNodeParser は、buffer_size(一緒に比較する文の数、デフォルトは 1)と breakpoint_percentile_threshold(デフォルトは 95。値を下げるとノードが増えます)を受け取ります。インデックス作成時に追加の埋め込み計算が必要になりますが、その効果は確立されていません。2024 年 10 月に三つの検索タスクを対象に行われた研究では、セマンティック分割による性能向上は一貫しておらず、その計算コストを正当化できないと結論づけています。採用する前に、ご自身のコーパスでテストしてください。
#6. 階層型(親と子)
検索精度を高めるために小さなチャンクをインデックス化し、文脈を提供するために、それを含むより大きな親ブロックをモデルに渡します。LlamaIndexのHierarchicalNodeParserはこのような階層を生成します。ドキュメントには、例えば2048、512、128トークンの3段階の構成が示されています。これは長い文章への対処法です。小さなチャンクだけにも、大きなチャンクだけにも頼りません。
| ドキュメントの種類 | 推奨される戦略 |
|---|---|
| ドキュメント、Wiki、Markdown、HTML | 構造(見出し)に沿って分割し、長いセクション内では再帰的に分割 |
| 契約書、法的文書 | 条または条項ごとに分割;サイズは300〜500トークン;各チャンクに条の見出しを再掲 |
| 構造化されていない自由形式の文章(メール、メモ、文字起こし) | 再帰的で、10%の重複あり、場合によっては階層的 |
| テーブル付きPDF | 事前に構造を抽出し、質問に応じて表を丸ごと1つのチャンクにするか、行ごとに分割する |
| ソースコード | 関数またはクラスごとに分割し、ブロックの途中では決して分割しない |
#LlamaIndexでの実装
#各チャンクにコンテキストを再び与える
文書から切り取られた断片は、その文脈を失います。「期限は30日です」という記述だけでは、それが何を指しているのか分かりません。この問題を解決するには、3つのシンプルな手法があります。各チャンクに文書のタイトルとセクションパスをプレフィックスとして付与すること、これらの情報をメタデータとして保存してフィルタリング(日付、ソース、文書タイプ別)に利用すること、そして代名詞や参照(「この条項」など)で始まる断片については、階層型分割の上位レベルを検討することです。多くの場合、テキストの前に1行のコンテキストを付与するだけで十分であり、モデルを変更するよりもはるかにコストが低いです。
#チャンク分割の方法を確定する前に評価する
- 01実際に想定される質問を30〜50件書くユーザーが尋ねそうな質問を用意し、それぞれに、回答の根拠として想定する原文の箇所を添えてください。結果を見る前に作成してください。
- 022つまたは3つの設定でインデックス作成たとえば250、400、700トークンで、重複率は0%と10%に設定します。他のパラメータは同じにしてください。
- 03検索結果の上位5件での再現率を測定する各質問に対して、期待される文が最初の5つの結果に含まれているかを確認します。このパーセンテージは5件の再現率を示します。
- 04適合率と重複の度合いも確認する再現率が同じなら、結果の多様性が高い設定のほうが優れています。上位5件の重複を数えてください。
- 05失敗を一つずつ検証する正しく答えられなかった質問ごとに、回答に必要な情報が含まれているはずのチャンクを開いてください。途中で切れていないか、範囲が広すぎないか、テキストの抽出が壊れていないかを確認します。原因に応じて修正方法を決めてください。
#よくある落とし穴
- 表の構造の崩れ
- PDF抽出ツールが表を平坦化すると、意味の通らないセルの並びが出力されます。文書を分割する前に、表の構造を抽出してください。
- 途中で分割されたコードブロック
- 構文を認識しない分割ツールは、ブロックを途中で分割してしまいます。構造に沿って分割してください。
- 複数の言語が混在するドキュメント
- 半分がフランス語、半分が英語のチャンクは、あまり役に立たない平均的なベクトルを生成します。コーパスに複数の言語が混在している場合は、言語ごとに分けてください。
- ほぼ空のチャンク
- 後に続く本文を伴わない見出しだけのチャンク(「3.2.1 義務」)はノイズです。短すぎるチャンクは除外するか、次のチャンクと結合してください。
- 再インデックス化せずにチャンク分割方法を変更する
- 分割方法はインデックス作成時に固定されるため、変更するたびにベクトルを再計算する必要があります。最初から再インデックス用のスクリプトを用意してください。
#チャンキングに関するよくある質問
ローカルRAGで使用するチャンクのサイズはどのくらいですか?+
セマンティックチャンキングはコストに見合いますか?+
チャンク間のオーバーラップは必要ですか?+
トークンか文字か:chunk_sizeをどう設定すべきですか?+
表を含むPDFはどのように分割すればよいですか?+
チャンクのサイズを変更した場合に再インデックスが必要ですか?+
- パイプラインにリランカーを追加する
- BM25とベクトル検索を組み合わせたハイブリッド検索
- フランス語対応の優れた埋め込みモデル
- ChromaDB と Ollama を使ったローカル RAG
- LlamaIndexの実際の使い方
- 出典:Chroma「Evaluating Chunking Strategies for Retrieval」
- 出典:Is Semantic Chunking Worth the Computational Cost ?
- 出典:LlamaIndex、ノードパーサー
- 出典:Hugging FaceのBAAI/bge-m3のドキュメント
ご意見、誤りのご指摘、補足はありますか?ぜひお知らせください。皆さんにとってより良いガイドにするために役立ちます。