📚

RAGにおいて「チャンク」って結局何やねん。パラメータ調整で検索精度が変わる仕組み

に公開

はじめに

RAG(Retrieval-Augmented Generation)システムを構築する際、避けて通れないのがチャンキングです。この記事では、チャンクとは何か、そしてパラメータ調整が検索精度にどう影響するかを具体例とともに解説します。

チャンクとは?

チャンクとは、長い文書を検索・処理しやすい適切なサイズに分割した単位のことです。

なぜチャンキングが必要?

  1. LLMのトークン制限: 入力できる文字数に上限がある
  2. 検索精度の向上: 小さな単位で検索することで関連情報をピンポイントで特定
  3. 処理効率: 必要な部分だけを効率的に処理

3つの重要なパラメータ

チャンキングは主に以下の3つのパラメータで制御します:

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,           # チャンクの最大文字数
    chunk_overlap=50,         # チャンク間の重複文字数
    separators=["\n\n", "\n", "。", "、", " ", ""]  # 分割に使う区切り文字
)

1. chunk_size(チャンクサイズ)

チャンク1つあたりの最大文字数を指定します。

小さいchunk_size(200-300文字)

メリット:

  • 検索精度が高い(ピンポイント)
  • 処理が高速

デメリット:

  • 文脈が失われやすい
  • チャンク数が増加

具体例:

# chunk_size=200
text = """
Q: 有給休暇はいつから取得できますか?
A: 入社6ヶ月後から10日間付与されます。

Q: 通勤手当は支給されますか?
A: はい、実費支給(上限月3万円)です。
"""

# 結果: 各Q&Aが独立したチャンクになる(理想的)

大きいchunk_size(1000-2000文字)

メリット:

  • 文脈が豊富に保持される
  • チャンク数が少ない

デメリット:

  • 検索精度が下がる(ノイズが増える)
  • 関連性の低い情報も含まれる

推奨設定

  • FAQ・Q&A: 200-400文字
  • 一般文書: 500-800文字
  • 技術文書・手順書: 800-1500文字

2. chunk_overlap(チャンク重複)

隣接するチャンク間で重複させる文字数です。

なぜ重複が必要?

重複がないと、境界付近の情報が失われます:

# overlap=0の場合
text = "従業員は規則を遵守し、業務上の指示命令に従い、職務を誠実に遂行する。"

# チャンク1: "従業員は規則を遵守し、業務上の指示命令に従い、"
# チャンク2: "職務を誠実に遂行する。"
# → 「指示命令に従う」と「職務を遂行する」の関係が失われる
# overlap=50の場合(chunk_sizeの10%)
# チャンク1: "従業員は規則を遵守し、業務上の指示命令に従い、"
# チャンク2: "指示命令に従い、職務を誠実に遂行する。"
#                     ↑ この部分が重複
# → 文脈が保持される

推奨設定

  • 最小: chunk_sizeの5%
  • 推奨: chunk_sizeの10-15%
  • 最大: chunk_sizeの20%

例:

  • chunk_size=500 → overlap=50-75
  • chunk_size=1000 → overlap=100-150

3. separators(区切り文字リスト)

テキストを分割する際に優先的に使用する区切り文字を指定します。

separators = ["\n\n", "\n", "。", "、", " ", ""]

動作の流れ

具体例で理解する

text = """
第1条 (目的)
本規則は株式会社の従業員の就業に関する事項を定める。従業員は本規則を遵守する。
"""

splitter = RecursiveCharacterTextSplitter(
    chunk_size=50,
    separators=["\n\n", "\n", "。", ""]
)

処理の流れ:

  1. "\n\n"で分割を試す → 存在しない
  2. "\n"で分割を試す
    part1: "第1条 (目的)" → 8文字 ✅
    part2: "本規則は...遵守する。" → 38文字 ✅
    
    → 両方とも50文字以下なので完了!

結果:

チャンク1: "第1条 (目的)"
チャンク2: "本規則は株式会社の従業員の就業に関する事項を定める。"
チャンク3: "従業員は本規則を遵守する。"

文書タイプ別の推奨設定

# 日本語文書(標準)
separators = ["\n\n", "\n", "。", "、", " ", ""]

# Markdown文書
separators = ["\n## ", "\n### ", "\n\n", "\n", "。", ""]

# 英語文書
separators = ["\n\n", "\n", ". ", ", ", " ", ""]

パラメータ調整の実践例

ケース1: FAQ・Q&A

splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,        # 一つのQ&Aが収まるサイズ
    chunk_overlap=30,      # 10%の重複
    separators=["\n\n", "\n", "。", ""]
)

ケース2: 詳細な手順書

splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,        # 一連の手順が収まるサイズ
    chunk_overlap=120,     # 15%の重複
    separators=["\n## ", "\n### ", "\n\n", "\n", "。", ""]
)

ケース3: 社内規程(バランス重視)

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 標準サイズ
    chunk_overlap=50,      # 10%の重複
    separators=["\n\n", "\n", "。", "、", " ", ""]
)

よくある失敗パターン

NG例1: chunk_sizeが大きすぎる

chunk_size=2000  # 大きすぎる

# 問題: 
# - 「有給休暇について教えて」という質問に対して
# - 有給休暇の説明 + 特別休暇 + 病気休暇 など関係ない情報も取得
# → LLMが混乱して回答の精度が下がる

NG例2: overlapがゼロ

chunk_overlap=0  # 重複なし

# 問題:
text = "年次有給休暇は入社6ヶ月後から付与されます。詳細は就業規則第11条を参照してください。"
# チャンク1: "年次有給休暇は入社6ヶ月後から付与されます。"
# チャンク2: "詳細は就業規則第11条を参照してください。"
# → 「有給休暇」と「就業規則第11条」の関連が失われる

NG例3: separatorsの順番が不適切

separators=["", " ", "。"]  # 順番が逆

# 問題:
# - 最初に ""(空文字)で強制分割されてしまう
# - 単語や文の途中で分割される

まとめ

パラメータ 推奨値 役割
chunk_size 500文字 検索精度と文脈保持のバランス
chunk_overlap 50文字(10%) 境界情報の損失を防ぐ
separators ["\n\n", "\n", "。", "、", " ", ""] 意味のある単位で分割

調整の基本方針:

  1. まずはデフォルト値(500/50)で試す
  2. 検索結果が大雑把すぎる → chunk_sizeを小さく
  3. 文脈が切れている → chunk_sizeを大きく、またはoverlapを増やす
  4. 文書の構造に合わせてseparatorsをカスタマイズ

適切なチャンキングは、RAGシステムの検索精度を大きく左右します。文書の特性に応じてパラメータを調整し、最適な設定を見つけましょう。

Discussion