RAG/LLMとPDF：PyMuPDFを使用したMarkdownテキストへの変換

![py-blog-rag-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573216/cbfda36b-ecd6-4422-f567-33381ac7ada6.jpeg)


# はじめに
**大規模言語モデル（LLMs） と検索増強生成（RAG）** 環境の文脈において、**Markdownテキスト形式**でのデータ供給は**重要な意味**を持ちます。以下に詳細な考慮事項を示します。

**LLMs**は、一貫していて文脈に沿ったテキストを生成できる強力な言語モデルです。しかし、時には事実の正確さや文脈を欠いた応答を生成することがあります。検索ベースの手法（**RAG**など）を取り入れることで、生成されたテキストの品質を向上させることができます。

**RAG**は、**LLM**のトレーニングデータに以前存在しなかった**外部データ**をテキスト生成プロセスに統合することを可能にします。これを取り込むことで、「幻覚（ハルシネーション）」の問題を緩和し、テキスト応答の関連性を向上させることができます。

# なぜLLMにMarkdownを使用するのか？
**Markdown**は、シンプルな構文を使ってプレーンテキストをフォーマットすることができる軽量なマークアップ言語です。**GitHub**や**Jupyter**ノートブック、さまざまなコンテンツ管理システムなど、構造化されたドキュメントを作成するために広く使用されています。**LLM**や**RAG**システムにデータを供給する際に、**Markdown**形式を使用することにはいくつかの利点があります。

1. **構造化されたコンテンツ：Markdown**を使用すると、見出し、リスト、表などの構造化された要素に情報を整理できます。この構造は、理解を助け、コンテキストを保存するのに役立ちます。
1. **リッチテキスト：Markdown**は太字、斜体、リンク、コードブロックなどの基本的なフォーマットをサポートしています。入力データにリッチテキストを含めることで、言語モデルに対するコンテキストが強化されます。
1. **リンクや参照の埋め込み：Markdown**では、ハイパーリンク、脚注、参照を埋め込むことができます。**RAG**のシナリオでは、外部ソースへの参照や追加のコンテキスト提供にこれが重要となる場合があります。
1. **執筆の容易さ：Markdown**は人間が読みやすく、書きやすい形式です。複雑なフォーマットツールなしで、著者は効率的にコンテンツを作成できます。
1. **チャンキング：RAG**システムにとって不可欠なチャンキング（通称「分割」）は、大規模なドキュメントをより簡単に処理するために分解します。**PyMuPDF**データ抽出が**MD**形式で利用可能なため、共通のコンテキストを持つテキストをまとめるためにチャンキングをサポートしています。**重要なのは、PyMuPDFによるMD形式での抽出が[レベル3のチャンキング](https://medium.com/@anuragmishra_27746/five-levels-of-chunking-strategies-in-rag-notes-from-gregs-video-7b735895694d#b123)を可能にすることです**。

要約すると、**LLM**および**RAG**環境での**Markdown**テキスト形式の使用は、より正確かつ関連性の高い結果を保証します。なぜなら、より豊富なデータ構造と、**LLM**によるより関連性の高いデータチャンクのロードを提供するからです。

# PDFのMarkdown変換のためのPyMuPDFサポート
**PyMuPDF**は創設以来、**PDF**ページからテキスト、画像、ベクトルグラフィックを抽出できるようになりました。2023年8月以降、テーブルも抽出可能です。これらのオブジェクトタイプごとに抽出方法があります。テキスト用のもの、およびテーブル、画像、ベクトルグラフィック用のそれぞれ異なるものがあります。**RAG**の要件を満たすために、これらの異なる抽出を統合し、ページのコンテンツ全体を一貫して表す共通の統一された**Markdown**文字列を生成しました。

これはすべて1つの**Python**スクリプトとして実装されています。他のスクリプトからモジュールとしてインポートしたり、次のようにターミナルウィンドウでコマンドラインとして呼び出すことができます：

`$ python pymupdf_rag.py input.pdf [-pages PAGES]`

**Markdown**形式のテキストファイル（`input.md`と呼ばれる）が生成されます。オプションのパラメータ`PAGES`を使用すると、**PDF**の総ページ数の一部に変換を制限できます。省略すると、全体の**PDF**が処理されます。

# Markdownの作成詳細
## 考慮するページの選択
"-pages"パラメータは、**Markdown**変換に考慮するページ番号（1から始まる）からなる文字列です。複数のページ番号仕様をカンマで区切って指定できます。各仕様は、1つの整数または"-"ハイフンで区切られた2つの整数のいずれかであり、ページの範囲を指定します。以下に例を示します：

**“-pages 1-10,15,20-N”**

これには、1ページから10ページ、15ページ、および20ページからファイルの最後までのページ（大文字の「N」は最後のページの番号として扱われます）が含まれます。

## ヘッダーの特定
起動時、プログラムは指定されたページのすべてのテキストを調べ、最も頻繁に使用されるフォントサイズを見つけます。この値（およびそれより小さいフォントサイズすべて）は**本文テキス**トを表すと見なされます。より大きなフォントサイズは**ヘッダーテキスト**を表すと見なされます。

フォントサイズの階層での相対的な位置に応じて、ヘッダーテキストは1つ以上の**Markdown**ヘッダー `#-tag` 文字で接頭辞が付けられます。

## ページの各領域ごとの処理モードの識別
各ページのすべてのテキストは、まず標準テキストかテーブルテキストかに分類されます。その後、ページの内容が上から下に向かってマークダウン形式に変換されます。

これは例を示すのが最も理解しやすいです：

![page-md.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573216/c622c888-ac33-b7f7-9056-58dc022f315e.png)



このページには、典型的な状況を表すコンテンツが表示されます：

- 一部が重なっている2つのテーブル。1つのテーブルにはヘッダーがありませんが、もう1つには外部列ヘッダーがあります。
- **タイトル**行と複数レベルの**ヘッダー**があります。
- **本文**には、**太字**、*斜体*、インラインコード などのさまざまなスタイルの詳細が含まれています。
- 順序付きおよび順序なしリスト
- コードスニペット

レイアウト解析では、3つのエリアが特定され、適切な処理モードが選択されます：<font color="cc0000">（1）</font>テキスト、<<font color="cc0000">（2）</font>テーブル、<font color="cc0000">（3）</font>テキスト。

生成された**Markdown**テキストは、できる限りそのままに反映されます。

例として、外部ヘッダーを持つテーブルの出力を見てみましょう：

```
|Column1|Column2|

|---|---|

|Cell (0, 0)|Cell (0, 1)|

|Cell (1, 0)|Cell (1, 1)|

|Cell (2, 0)|Cell (2, 1)|
```

これは、可能な限りトークンのサイズを最小限に抑えた、**GitHub**互換の形式です。これは、**RAG**システムへのフィードを小さく保つために重要な側面です。

**列の境界**は「|」文字で示されます。テキスト行は、「|---|---| …」の形式の行が続く場合、**テーブルのヘッダー**であると見なされます。完全な**テーブル定義**は、少なくとも1つの空行で囲まれる必要があります。

技術的な理由から、**Markdown**テーブルにはヘッダーが必要であり、外部ヘッダーが利用できない場合は最初のテーブル行が選択されます。

全体の忠実度を確認するために、ここにいくつかの**Markdown**パーサーがページ全体を処理する方法が示されています：

![page-md-output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573216/a2240852-4f21-76d4-6498-8ec4b424653a.png)


# PyMuPDF4LLM

## マークダウンコンバーターをプログラムから呼び出すことができます
コマンドラインでプログラムを実行する代わりに、プログラムからもマークダウン変換を要求できます。

`pip install pymupdf4llm`

```python
import pymupdf4llm
md_text = pymupdf4llm.to_markdown("input.pdf")

# write markdown string to some file
output = open(“out-markdown.md”, “w”)
output.write(md_text)
output.close()
```

# 結論
**PyMuPDF**の抽出方法を統合することで、**PDF**ページの内容は忠実に**Markdown**テキストに変換され、**RAG**チャットボットの入力として使用できます。

成功する**RAG**チャットボットの鍵は、アクセスできる情報の質と完全性にあります。

**PyMuPDF**による**Markdown**抽出は、**PDF**からの情報が可能であるだけでなく、簡単であることを保証し、ライブラリの強みと開発者向けの利便性を示しています。幸せなコーディングを！


# ソースコード
[RAG/helpers/pymupdf_rag.py](https://github.com/pymupdf/RAG/blob/main/helpers/pymupdf_rag.py)

# 参考文献
[5つのテキスト分割のレベル](https://github.com/FullStackRetrieval-com/RetrievalTutorials/blob/main/tutorials/LevelsOfTextSplitting/5_Levels_Of_Text_Splitting.ipynb)


# 関連ブログ


[ChatGPT APIとPyMuPDFを使用してRAGチャットボットGUIを構築する](https://qiita.com/jamie-lemon/items/f075ae56cf56723c9409)

[ChatGPTとPyMuPDFを使用したRAGチャットボットの作成](https://qiita.com/jamie-lemon/items/df6703906392ae8295f3)

[RAG/LLMとPDF: テキスト抽出の強化](https://qiita.com/jamie-lemon/items/455e14f83b4f5c81034b)