本文へスキップ
BecomeCoder

RAGコース · 第2章 文書を準備する ― チャンク分割 · レッスン5

なぜ分割するのか ― コンテキスト長と精度

ブラウザで完結

導入

文書1つをまるごと索引に入れると、検索は「その文書まるごと」しか取り出せません。質問に関係する一部分がほしいだけなのに、関係ない段落まで一緒についてきてしまいます。小さく分けておけば、質問に関係する箇所だけをピンポイントで取り出せます。

説明

さらにもう1つ理由があります。RAGでは検索で見つけた文章をLLM(大規模言語モデル)に渡して回答を作らせますが、LLMに一度に渡せる文章の量には上限があります。これをコンテキスト長と呼びます。長い文書をそのまま渡すとこの上限を超えてしまうことがあるため、あらかじめ小さく分けておく必要があります。

まずは、これから扱う文書がどれくらいの長さなのかを見てみましょう。

document = """Retrieval-Augmented Generation combines a retriever and a generator.
The retriever finds relevant passages from a knowledge base.
The generator is a large language model that writes an answer.
Splitting long documents into smaller chunks makes retrieval precise."""

print("Total characters:", len(document))
print("Total words:", len(document.split()))
print("Number of lines:", len(document.splitlines()))

# 行ごとの長さを見てみる(分割の単位を考える材料)
for i, line in enumerate(document.splitlines(), 1):
    print(f"line {i}: {len(line)} chars")

len(document) は文字数、document.split() は空白区切りで単語に分けた個数、document.splitlines() は改行で行に分けたリストです。行ごとの長さを見ると、「どこで区切るのが自然か」の材料になります。

チャンクの大きさには、はっきりしたトレードオフがあります。小さすぎると文脈が途中で切れてしまい、逆に大きすぎると質問に無関係な内容まで混ざってしまう(しかもコンテキスト長を圧迫する)。ちょうどいい大きさを探るのが、この章のテーマです。次のレッスンから、具体的な切り方を1つずつ試していきます。

やってみよう

document文字列を書き換えて、len(document)len(document.split()) がどう変わるか確かめてみましょう。行数(splitlines())が増えるように改行を足してみるのもよい練習です。

演習

document単語数document.split() の要素数)を print で表示してください。

ヒント1を見る

document.split() はスペースや改行で区切った単語のリストを返します。

ヒント2を見る

そのリストの長さを len() で数えます。

実際に動かしてみよう

下のエディタにPythonを書いて「実行」を押すと、scikit-learn(TF-IDF)とnumpyで本物の「検索」をその場で動かせます。RAGの検索フェーズ――文書のチャンク分割・ベクトル化・類似度計算・並べ替え――が実際に動きます。scikit-learn を使う回は初回の読み込みに数十秒かかることがあります。検索デモのサンプル文書は英語です(TF-IDFの既定トークナイザが日本語を分割できないため。日本語検索の対処は読み物で解説します)。LLMの生成や本物の埋め込みAPIは、APIキーが要るため読み物の回でコードを示します。

Python — ライブラリ付きで実行(Pyodide)

numpy / pandas / matplotlib が使える本物のPython(Pyodide)を読み込みます。初回のみ読み込みに少し時間がかかります(以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。