はじめに
最近、RAG(Retrieval-Augmented Generation)をいろいろ試しています。RAGとは、ざっくり言うと「ドキュメントを検索して、その内容をもとにLLMに回答させる仕組み」のことです。
テキストだけのシンプルなデータなら、わりと素直に動いてくれる印象があります。ただ、ふと疑問が浮かんできました。
図や表が混ざっていて、レイアウトもページごとにバラバラなPDFでも、RAGってちゃんと動くんだろうか?
実際の業務ドキュメントって、きれいな文章ばかりじゃないですよね。図・表・矢印・JP/EN混在・スライド由来のちょっと崩れた構造…正直、なかなかカオスです。
今回は、そういうPDFを使ってPOCレベルで軽く試してみました。
なお、この記事は実験メモのような内容です。厳密な精度評価やベンチマークはしていません 🙏
そもそも「複雑なPDF」とは?
今回使ったのは、IPAの公開資料です。
工場システムにおけるサイバー・フィジカル・セキュリティ対策
https://www.ipa.go.jp/security/seminar/ssf7ph00000081a1-att/27collapla_presentation.pdf
スライド形式の資料で、ページごとにレイアウトがかなり違います。実際に開いてみると、
- 図がメインのページ
- 表が中心のページ
- テキストと図が重なっているページ
- 日本語と英語が混在しているページ
などが混在しています。吹き出しや矢印も多く、「人が目で見て理解する前提」で作られている資料、という印象です。
RAGの文脈で言うと、これが問題になります。機械はレイアウトを「見る」ことができないため、視覚的な構造に依存したドキュメントはそのまま扱いにくいのです。
テキストだけをきれいに抽出できる感じはあまりしない…でも、とりあえずやってみました。
ナイーブなRAGを試してみた
まずは、よくあるシンプルな構成で試しました。
やったこと
- OCR(PaddleOCRなど)でテキスト抽出
- ページ単位または段落単位でchunk(テキストを検索単位に分割したもの)に分割
- Embeddingベクトル生成
- ChromaDBに保存
- GPTで回答生成
いわゆる「ナイーブRAG構成」です。これが最初に自然に思いつく方法でもありました。
このくらいでそれなりに動くのでは?と思っていました。
しかし、普通に詰みました 😇
実際に動かしてみると、いくつか問題が出てきました。
① 読み順が壊れる
図+テキストが混在しているページでは、本来「図 → その説明文」という流れで読むはずなのに、OCRでは順番が前後することがあります。
これはOCRがテキストを座標順(上から下・左から右)で拾うためで、視覚的なレイアウトの意図を理解していないからです。
そのままchunkにすると、意味がうまくつながりません。
② 表構造が壊れる
チェックリスト形式の表などが特に顕著でした。
テキスト抽出すると、
- 列構造が消える
- 行の対応関係が崩れる
- ただの箇条書きのような文字列になる
Embeddingはテキストの意味を数値化しますが、行列の対応関係を前提にはしていません。表の構造が崩れると、「どの行とどの列が対応しているか」という情報がそのまま失われます。
検索すると、微妙にズレた結果が返ることが増えました。
③ 「どこに書いてあるか」が示せない
回答自体はそれっぽく返ってきます。ただ、
- どのページなのか?
- 図のどの部分なのか?
- 表のどのセルなのか?
といった根拠をうまく提示できません。
実際の業務利用を想像すると、「それっぽい回答」だけでは信頼しにくい。根拠が示せないのは、実用上かなり気になるポイントでした。
じゃあ、構造を取ればいいのでは?
そこで、「テキストだけでなく、位置情報も一緒に扱えば改善するのでは?」と考え、いくつかのアプローチを試してみました。
PyMuPDFでbbox付き抽出
PyMuPDFを使って、テキストをbbox(バウンディングボックス:ページ上の位置座標)付きで取得してみました。画像も位置情報付きで抽出できます。
ただ、
- 読み順の整理は自前で実装する必要がある
- 図と説明文の対応関係は自動では取れない
- アイコンや装飾との区別が難しい
「bboxは取れる、でも構造を意味として扱うのは結局自分で設計しないといけない」という印象でした。
OCR系モデルも試した(DeepSeek-OCR)
bbox付きでテキストは取得できます。ただ、
- レイアウトのまとまりは保証されない
- 図と装飾の区別は難しい
- chunk設計は結局自分でやる必要がある
「文字は取れるが、構造は取れていない」という状況でした。
画像ベース検索も試した(ColQwen)
思い切ってページを画像として扱い、画像埋め込みで検索する方法も試しました。
関連しそうなページは取れます。ただ、
- 図のどの部分かまでは特定できない
- 厳密な根拠提示は難しい
「ページは見つかるが、そこから先に進めない」感じでした。
3つとも試してみて、共通して感じたのは「問題はツールの選択ではないな」ということでした。
やってみて気づいたこと
問題はLLMの性能でも、ツールの選択でもなく、前処理の設計にあるということが見えてきました。
テキストだけを抽出すると、
- レイアウトが消える
- 図と説明の関係が切れる
- 表の行列構造が壊れる
結果として、「意味のまとまり」と「検索単位(chunk)のまとまり」が一致しない。
ここが一番のボトルネックでした。
LLMがどれだけ優秀でも、渡す情報の質が低ければ答えも安定しない。「モデルを変える前に、前処理の設計を見直す必要がある」というのが今回の実感です。
次回は
そこで、「テキスト抽出」ではなく 「構造ごと取り出す」 アプローチを試してみました。
具体的には、ドキュメントの要素を「これはテキストか?表か?図か?ページのどの位置か?」ごとに分類しながら取り出す方法です。
次回の記事では、そのアプローチの実装と、実際に何が変わったのかを見ていきます。