RAGの鬼門＝PDFの表を突破できるか？ PDF を マークダウンに変換するツール "Marker" を使ってみた

## TL;DR:

- LLM で PDF から情報を抽出する際、Marker を使ってマークダウンに変換してLLMのコンテキスト情報として渡す方法と pdfminer.six でテキストを抽出して LLM へ渡す方法を比較
- 表のデータ抽出においては、Marker を使ったマークダウン変換の方が優位性あり
- ただし、Marker による変換は完璧ではなく、表の構造によっては正しく変換できないケースも
- マークダウンの表のヘッダー崩れにはパターンがあり、In-Context Learning で改善の可能性あるかも
- Marker は日本語に対応しているが今回は未検証
## はじめに
大規模言語モデル（LLM）を使ってPDFファイルから情報を抽出したり、データベースを用いたRAG（Retrieval-Augmented Generation）を構築する際、PDFファイルからのデータ抽出が課題となります。ヘッダーやフッターなどの不要な情報の除去や、表の構造をLLMに伝えることは特に難しい問題です。

このような目的に役立ちそうなツールとして、「Marker」の存在を知りました。Markerを使ってPDFをマークダウンに変換し、それをCohere Command R+に読み込ませることで、pdfminer.sixでテキストを抽出した場合との違いを検証しています。

近い将来にCommand R+がOCI（Oracle Cloud Infrastructure）で使えるようになることを願って！
## Marker とは
Markerは、PDFをマークダウン形式に素早く正確に変換するツールです。書籍や学術論文に最適化されていますが、幅広い種類のドキュメントに対応しており、あらゆる言語（と公式ドキュメントに書かれています）をサポートしています。

変換の際には、ヘッダー、フッター、その他の不要な要素を削除し、テーブルやコードブロックを適切に整形します。また、画像を抽出してマークダウンと一緒に保存し、ほとんどの数式をLaTeX形式に変換します。

Markerは、GPU、CPU、またはMPS（Apple Silicon）上で動作するため、様々な環境で利用できます。これにより、PDFからマークダウンへの変換を効率的かつ正確に行うことができます。

GitHub リポジトリは[こちら](https://github.com/VikParuchuri/marker)
### 商用利用について
Markerは、研究や個人利用においては常に無料で利用可能ですが、商用利用に関しては一部制限があります。

モデルの重みは「cc-by-nc-sa-4.0」ライセンスの下で提供されていますが、直近12ヶ月間の売上高が500万米ドル未満、かつ、ベンチャーキャピタルやエンジェル投資家から調達した資金の累計が500万米ドル未満の組織に対しては、この制限が免除されます。

GPLライセンスの要件を取り除く（デュアルライセンス）、または収益制限を超えて商用利用したい場合は、別途 [Datalab 社](https://www.datalab.to/)が提供するオプションを確認する必要があります。詳しくは[こちら](https://github.com/VikParuchuri/marker/blob/master/README.md#commercial-usage)に書かれていますので商用利用をお考えの場合には確認してください。

## インストール
### 前提条件
#### Python
version 3.9 以上（私は、3.11.9 で試しています）
#### PyTorch
PyTorch のインストール方法は[PyTorch Get-Startedページ](https://pytorch.org/get-started/locally/)でOS情報などを選択するとインストールに使用するコマンドを確認できます。CPU版でもGPU(CUDA)版でも良いようですが私はGPU版を使用しています（私のPCに搭載されている GPU は、NVIDIA RTX 2060 6GB という トップガン マーヴェリック のルースターなら「太古の遺物」といいそうなグラボです ）。GPU版の PyTorch をインストールする場合は、インストールされている CUDA のバージョンを確認する必要があります。以下のコマンドで確認できます。
```
nvcc --version
```
Cursor Editor の TERMINAL の bash での出力例です。
```
$ nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2022 NVIDIA Corporation
Built on Wed_Sep_21_10:41:10_Pacific_Daylight_Time_2022
Cuda compilation tools, release 11.8, V11.8.89
Build cuda_11.8.r11.8/compiler.31833905_0
```
私の場合は、11.8 であることがわかりました（長いことバージョンアップを怠けています）。
CUDA対応の GPU は搭載している環境で CUDA 未インストールの場合は、[PyTorch Get-Startedページ](https://pytorch.org/get-started/locally/)で PyTorch がサポートする CUDA バージョンを確認して、CUDA をインストールします。CUDA の最新バージョンは、[こちら](https://developer.nvidia.com/cuda-downloads)、以前のバージョンは[こちら](https://developer.nvidia.com/cuda-toolkit-archive)です。
CUDA のバージョン確認ができたら[PyTorch Get-Startedページ](https://pytorch.org/get-started/locally/)でOS や CUDA バージョンを指定してインストールコマンドを確認します。私の場合は下のスクショのような組み合わせでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/5070bdfe-30a5-bbeb-2fb0-d8d3b5a46db3.png)
"Run This Command"に表示されたコマンドを実行します（私の例↓）。
```
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
```
実行例
```
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
Looking in indexes: https://download.pytorch.org/whl/cu118
Collecting torch
...
...
  Using cached https://download.pytorch.org/whl/mpmath-1.3.0-py3-none-any.whl (536 kB)
Installing collected packages: tbb, mpmath, intel-openmp, typing-extensions, sympy, pillow, numpy, networkx, mkl, MarkupSafe, fsspec, filelock, jinja2, torch, torchvision, torchaudio
Successfully installed MarkupSafe-2.1.5 filelock-3.13.1 fsspec-2024.2.0 intel-openmp-2021.4.0 jinja2-3.1.3 mkl-2021.4.0 mpmath-1.3.0 networkx-3.2.1 numpy-1.26.3 pillow-10.2.0 sympy-1.12 tbb-2021.11.0 torch-2.3.1+cu118 torchaudio-2.3.1+cu118 torchvision-0.18.1+cu118 typing-extensions-4.9.0
```
PyTorch が正常にインストールされているか確認します。
```
pip show torch
```
出力例
```
$ pip show torch
Name: torch
Version: 2.3.1+cu118
Summary: Tensors and Dynamic neural networks in Python with strong GPU acceleration
Home-page: https://pytorch.org/
Author: PyTorch Team
Author-email: packages@pytorch.org
License: BSD-3
Location: d:\lab\Marker\.venv\Lib\site-packages
Requires: filelock, fsspec, jinja2, mkl, networkx, sympy, typing-extensions
Required-by: torchaudio, torchvision
```
python スクリプトからもちゃんと見えているか確認します。
```
python -c "import torch; print(torch.__version__)"
```
出力例
```
2.3.1+cu118
```
無事、準備が整いました！
### Marker のインストール
```
pip install marker-pdf
```
Marker が正常にインストールされているか確認します。
```
pip show marker-pdf
```
出力例
```
$ pip show marker-pdf
Name: marker-pdf
Version: 0.2.13
Summary: Convert PDF to markdown with high speed and accuracy.
Home-page: https://github.com/VikParuchuri/marker
Author: Vik Paruchuri
Author-email: github@vikas.sh
License: GPL-3.0-or-later
Location: d:\lab\Marker\.venv\Lib\site-packages
Requires: filetype, ftfy, grpcio, numpy, pdftext, Pillow, pydantic, pydantic-settings, python-dotenv, rapidfuzz, regex, scikit-learn, surya-ocr, tabulate, texify, torch, tqdm, transformers
Required-by
```
Marker は、デフォルトでは OCR に surya を使用するため marker-pdf をインストールすると surya-ocr もインストールされます。
```
pip show surya-ocr
```
出力例
```
$ pip show surya-ocr
Name: surya-ocr
Version: 0.4.12
Summary: OCR, layout, reading order, and line detection in 90+ languages
Home-page: https://github.com/VikParuchuri/surya
Author: Vik Paruchuri
Author-email: vik.paruchuri@gmail.com
License: GPL-3.0-or-later
Location: d:\lab\Marker\.venv\Lib\site-packages
Requires: filetype, ftfy, opencv-python, pillow, pydantic, pydantic-settings, pypdfium2, python-dotenv, tabulate, torch, transformers
Required-by: marker-pdf
```
なお、surya による OCR がサポートする言語のリストは[こちら](https://github.com/VikParuchuri/surya/blob/master/surya/languages.py)です。日本語も入ってますね！
## PDF からマークダウンへの変換
PDFをマークダウンへ変換する Marker のコマンドは、単一ファイルの場合、つまり1つの PDF ファイルをマークダウン形式に変換する marker_single と複数のファイルを一気に変換する marker の２つがあります。
ここでは、単一ファイルの場合の marker_single を試してみました。
```
marker_single /path/to/file.pdf /path/to/output/folder --batch_multiplier 2 --max_pages 10 --langs English
```
- 最初のパラメータは、変換したい PDF ファイルのパス
- 2番目のパラメータは、変換した マークダウンファイルなどを出力するディレクトリのパス
- --batch_multiplier は、余分なVRAMがある場合にデフォルトのバッチサイズにかける倍数です。数値が大きいほどより多くのVRAMを使用しますが、処理は高速になります。デフォルトでは2に設定されています。デフォルトのバッチサイズは約3GBのVRAMを使用しますので、デフォルト、もしくは上記の例の場合は 3GB x 2 = 6GB 程度の GPU VRAM を使用することになります
- --max_pages は処理するページの最大数です。ドキュメント全体を変換するには、このオプションを省略します
- --langs は、OCRのために、ドキュメントに含まれる言語をカンマ区切りで指定するオプションです。サポートする言語のリストは[こちら](https://github.com/VikParuchuri/surya/blob/master/surya/languages.py)です

#### Lost in the Middle を変換してみた
LLM のコンテキストウィンドウに関する有名な論文 "Lost in the Middle: How Language Models Use Long Contexts" の PDF を変換してみました。PDF ファイルは [arxive](https://arxiv.org/abs/2307.03172) からダウンロードしました。

```
marker_single 2307.03172v3_Lost_in_the_Middle.pdf LostInTheMiddleOut --batch_multiplier 2 --langs English
```
下記は実行中の出力ですが様々なモデルが使用されている様子が見て取れます。
```
$ marker_single 2307.03172v3_Lost_in_the_Middle.pdf LostInTheMiddleOut --batch_multiplier 2 --langs English
config.json: 100%|████████████████████████████████████████████████████████████████████| 1.18k/1.18k [00:00<?, ?B/s] 
model.safetensors: 100%|████████████████████████████████████████████████████████| 120M/120M [00:02<00:00, 52.6MB/s] 
Loaded detection model vikp/surya_det2 on device cuda with dtype torch.float16
preprocessor_config.json: 100%|███████████████████████████████████████████████████████████| 430/430 [00:00<?, ?B/s] 
config.json: 100%|████████████████████████████████████████████████████████████████████| 1.57k/1.57k [00:00<?, ?B/s] 
model.safetensors: 100%|████████████████████████████████████████████████████████| 120M/120M [00:02<00:00, 53.2MB/s] 
Loaded detection model vikp/surya_layout2 on device cuda with dtype torch.float16
preprocessor_config.json: 100%|███████████████████████████████████████████████████████████| 430/430 [00:00<?, ?B/s] 
config.json: 100%|████████████████████████████████████████████████████████████| 5.04k/5.04k [00:00<00:00, 5.02MB/s] 
model.safetensors: 100%|████████████████████████████████████████████████████████| 550M/550M [00:10<00:00, 53.0MB/s] 
generation_config.json: 100%|█████████████████████████████████████████████████████████████| 160/160 [00:00<?, ?B/s]
Loaded reading order model vikp/surya_order on device cuda with dtype torch.float16
preprocessor_config.json: 100%|███████████████████████████████████████████████████████████| 684/684 [00:00<?, ?B/s] 
config.json: 100%|████████████████████████████████████████████████████████████████████| 6.91k/6.91k [00:00<?, ?B/s] 
model.safetensors: 100%|██████████████████████████████████████████████████████| 1.05G/1.05G [00:19<00:00, 53.1MB/s] 
generation_config.json: 100%|█████████████████████████████████████████████████████████████| 181/181 [00:00<?, ?B/s] 
Loaded recognition model vikp/surya_rec on device cuda with dtype torch.float16
preprocessor_config.json: 100%|███████████████████████████████████████████████████████████| 608/608 [00:00<?, ?B/s] 
config.json: 100%|████████████████████████████████████████████████████████████████████| 4.92k/4.92k [00:00<?, ?B/s] 
model.safetensors: 100%|████████████████████████████████████████████████████████| 625M/625M [00:11<00:00, 52.6MB/s] 
generation_config.json: 100%|██████████████████████████████████████████████████████| 191/191 [00:00<00:00, 190kB/s] 
Loaded texify model to cuda with torch.float16 dtype
preprocessor_config.json: 100%|███████████████████████████████████████████████████████████| 617/617 [00:00<?, ?B/s] 
tokenizer_config.json: 100%|██████████████████████████████████████████████████| 4.49k/4.49k [00:00<00:00, 4.49MB/s] 
tokenizer.json: 100%|█████████████████████████████████████████████████████████| 2.14M/2.14M [00:00<00:00, 2.87MB/s] 
added_tokens.json: 100%|██████████████████████████████████████████████████████████████| 18.3k/18.3k [00:00<?, ?B/s] 
special_tokens_map.json: 100%|████████████████████████████████████████████████████████████| 552/552 [00:00<?, ?B/s] 
Detecting bboxes: 100%|██████████████████████████████████████████████████████████████| 3/3 [00:23<00:00,  7.70s/it] 
Detecting bboxes: 100%|██████████████████████████████████████████████████████████████| 2/2 [00:08<00:00,  4.10s/it] 
Finding reading order: 100%|█████████████████████████████████████████████████████████| 2/2 [00:05<00:00,  2.57s/it] 
Saved markdown to the LostInTheMiddleOut\2307 folder
```
マークダウンファイルなどの出力先として指定したディレクトリ（"LostInTHeMiddleOut"）にはこのようなファイル群が作られていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/6e38237b-6d05-64fe-1f5d-7de38ec24083.png)
"0_image_0.png"などの png ファイルは、論文のPDFから抽出された画像ファイルです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/71fb3586-9f34-9be1-63dc-d4ec21b85896.png)
json ファイルには、全体のページ数や実際にOCR処理されたページ数などが記録されているようです。今回のPDFファイルはテキストが埋め込まれていてOCR無しでテキスト抽出が可能ですので OCR の統計情報はいずれも "0" になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/c7958ddb-90fe-4914-847f-7914f3725a2e.png)
マークダウンファイルはこのような形でとても綺麗に出ていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/56e25b44-2997-eefc-fdd5-807e01cf60f2.png)
Corsor Editor のプレビューで表示したところです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/488450bb-3230-0c1c-884e-84ce40326ca4.png)

### 表の変換について
PDF からのテキスト抽出で鬼門となる表です。
論文中の表を変換前のPDFと変換後のマークダウンで較べてみます。
- "Table 1"

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/fd2b7721-ba5b-b892-daf7-9b9d03db938f.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/bf6ea1d5-0e9c-2708-8995-efde427658e3.png)|
|---|---|
|変換前のPDF|変換後のマークダウン|

綺麗に変換できているようです。
- "Table 2"

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/19d7dead-fa6b-1a2e-9e20-cba5306b420c.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/e4cb0a5a-6d1e-40cf-57b7-b388f89d9d0e.png)|
|---|---|
|変換前のPDF|変換後のマークダウン|

今度は「なんだかなぁ」ですね。ヘッダーの"Closed-Block"と"Oracle"が左寄せになってしまっています。ちなみにこの"Oracle"は私の勤務先とは全く関係ないです。

- "Table 3"

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/d9bc1c9b-0cc2-c70f-c198-2b5146767dc7.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/70df43e0-192b-70c3-e4a1-50858d5cd416.png)|
|---|---|
|変換前のPDF|変換後のマークダウン|

"Table 3"は、「なんだかなぁ」どころではなく表として認識されていないようです。
それでは、"Table 3"と構造がほぼ同じ"Table 4"はどうでしょう？

- "Table 4"


|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/9817ee85-63af-d347-dc63-27180a9d5bbc.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/d7e26a4c-e077-9e14-99af-0e9299132864.png)|
|---|---|
|変換前のPDF|変換後のマークダウン|

"Table 4"は、先程表と認識されていなかった "Table 3"とほぼ同じ構造ですが表と認識されたようです。でも、最初の2行（表のヘッダー）のカラム位置は間違っています。奥が深いですね。

### OCR を強制してみる
表の変換がおしいところまで来ました。このままでも大規模言語モデル（LLM）への入力にするなら後はLLMがなんとかしてくれるのではと淡い期待を抱きたいところですが、その前に Marker で他にできることがないかとドキュメントを見ていると "[Troubleshooting](https://github.com/VikParuchuri/marker?tab=readme-ov-file#troubleshooting)"にこんな記述がありました。
> OCR_ALL_PAGES - set this to true to force OCR all pages. This can be very useful if the table layouts aren't recognized properly by default, or if there is garbled text.

日本語に翻訳すると
> OCR_ALL_PAGES - すべてのページで強制的にOCRを実行するには、これをtrueに設定します。これは、デフォルトでテーブルのレイアウトが正しく認識されない場合や、文字化けしたテキストがある場合に非常に便利です。

ということですので、期待してしまいますね。環境変数で OCR_ALL_PAGES を True に設定すればよいようです。

環境変数を設定します
```
export OCR_ALL_PAGES=True
```
もう一度、この環境変数と出力先ディレクトリ指定以外は全く同じパラメータで "marker_single" コマンドを実行します。
```
marker_single 2307.03172v3_Lost_in_the_Middle.pdf LostInTheMiddleOut_OCR --batch_multiplier 2 --langs English
```
表はどうなったでしょうか？
- "Table 1" ： OCR無しでも正しく認識されていたケースです

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/4cfda312-89b3-fdf5-5c39-9c0abba01121.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/f1c9fa79-3538-07c2-0cad-fc9304b6ecb8.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/1e0c3b3b-eaac-20fb-46c9-8a165ef24b20.png)|
|---|---|---|
|変換前のPDF|OCR無しのマークダウン|OCR強制のマークダウン|

"Table 1"は今回も正常に認識されています。
- "Table 2" ： OCR無しではヘッダーのカラム位置が間違っていたケースです

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/19d7dead-fa6b-1a2e-9e20-cba5306b420c.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/e4cb0a5a-6d1e-40cf-57b7-b388f89d9d0e.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/ba1f69ae-bfce-3115-9d91-085d802e222d.png)|
|---|---|---|
|変換前のPDF|OCR無しのマークダウン|OCR強制のマークダウン|

これは今回も「なんだかなぁ」となりました。ヘッダーの"Closed-Block"と"Oracle"が左寄せになってしまっています。

- "Table 3" ： OCR無しではテーブルとして認識されなかったケースです

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/d9bc1c9b-0cc2-c70f-c198-2b5146767dc7.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/70df43e0-192b-70c3-e4a1-50858d5cd416.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/8523f95f-ebfd-7e62-67ef-33c75ed3935f.png)|
|---|---|---|
|変換前のPDF|OCR無しのマークダウン|OCR強制のマークダウン|

OCR強制では、表としては認識されましたが3つの異なる表としてマークダウンフォーマットされてしまいました。「なんだかなぁ」ですね。奥が深いです。

- "Table 4" ：  OCR無しではヘッダーのカラム位置が間違っていたケースです

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/9817ee85-63af-d347-dc63-27180a9d5bbc.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/d7e26a4c-e077-9e14-99af-0e9299132864.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/7f5668ed-453e-1701-dc3c-2d948ff1ee76.png)|
|---|---|---|
|変換前のPDF|OCR無しのマークダウン|OCR強制のマークダウン|

OCR強制でも表として認識されましたが、ヘッダーが間違っています。OCR 無しよりも少し余計に崩れています。難しいですね。
## LLM への入力としてテキストの場合とマークダウンの場合を較べてみる
表の変換は完全ではありませんでしたが、LLMへコンテキスト情報として与える場合には、表構造に関する情報が完全に失われているテキストよりは良い結果を得られるのではないかという期待で比較してみました。
### PDF からテキストの抽出
テキストデータは、pdfminer.six を使って PDF から抽出しました。
抽出プログラムコードはこちらです。

```pdf-extracter.py
from pdfminer.high_level import extract_text

def extract_text_from_pdf(pdf_path):
    return extract_text(pdf_path)

def write_text_to_file(text, output_path):
    with open(output_path, 'w', encoding='utf-8') as file:
        file.write(text)

def main(pdf_path):
    text = extract_text_from_pdf(pdf_path)
    output_path = pdf_path.replace('.pdf', '.txt')
    write_text_to_file(text, output_path)
    print(f'Text extracted and written to {output_path}')

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2:
        print("Usage: python pdf-extracter.py <pdf_path>")
    else:
        main(sys.argv[1])
```
実行方法はこちらです。
```
python pdf-extracter.py <pdf_path>
```
pdf_path に指定した PDF ファイルの拡張子を "txt" に変えたファイルに抽出したテキストが出力されます。

### Cohere Command R+ による推論コード
Cohere Command R+ を使ってテキストファイルの情報を元に質問に答えさせた場合と、マークダウンファイルに基づいて質問に答えさせた場合を比較しています。
推論コードはこちらです。
```Cohere_Chat_QA.py
import cohere
from dotenv import load_dotenv, find_dotenv
import os
import json

MODEL = "command-r-plus"
APPLICATION_TYPE = "Cohere_Chat_QA"
DOCUMENT_NAME = "2307.03172v3_Lost_in_the_Middle.txt"
#DOCUMENT_NAME = "2307.03172v3_Lost_in_the_Middle_Marker.md"
#DOCUMENT_NAME = "2307.03172v3_Lost_in_the_Middle_Marker_OCR.md"

_ = load_dotenv(find_dotenv())

def read_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return file.read()
    
def get_system_prompt():
    return read_file(f"{APPLICATION_TYPE}_system_prompt.txt")

def get_prompt_template():
    return read_file(f"{APPLICATION_TYPE}_user_prompt_template.txt")

with open(DOCUMENT_NAME, "r", encoding="utf-8") as f:
    text = f.read()

with open(f"{APPLICATION_TYPE}_Prompts.json", "r", encoding="utf-8") as f:
    PROMPT = json.load(f)

user_prompt_template = get_prompt_template()
chat_history = [{"role": "SYSTEM", "message": get_system_prompt()}]

with open(f"report/Report_{os.path.splitext(DOCUMENT_NAME)[0]}.md", "w", encoding="utf-8") as f:
    # テーブルのヘッダーを書き込む
    f.write("|id|question|モデルの応答|Ground truth|正誤|\n")
    f.write("|--|--------|------------|------------|----|\n")
    for prompt in PROMPT:
        question_id = prompt["id"]
        question = prompt["question"]
        ground_truth = prompt["ground_truth"]
        message = user_prompt_template.replace("{context}", text).replace("{prompt}", question)

        co = cohere.Client(api_key=os.environ['COHERE_API_KEY'])
        model_response = co.chat( 
            model=MODEL,

            message=message,

            search_queries_only=False,
            temperature=0.0,
            max_tokens = 1400,
            frequency_penalty = 0.1,
            chat_history=chat_history,
            prompt_truncation='OFF', # AUTO or OFF
            connectors=[],
            documents=[]
        ) 
        print("-----------------")
        print(f"Q{question_id}:{question}")
        print(f"A{question_id}:{model_response.text}")
        print(f"Ground Truth{question_id}:{ground_truth}")
        f.write(f"|{question_id}|{question}|{model_response.text}|{ground_truth}||\n")
```
システムメッセージ（Preambleではありません）は次のファイルに定義しています。

```Cohere_Chat_QA_system_prompt.txt
あなたは学術論文に精通したサイエンスライターです。
```
また、ユーザーメッセージのテンプレートは次のファイルです。

```Cohere_Chat_QA_user_prompt_template.txt
context の情報を元に prompt で与えられるユーザーの質問に答えてください。context の情報だけではユーザーの質問に回答できない場合には必ず「その質問には提供された情報からは回答できません。」と答えてください。
**prompt**
{prompt}

**context**
{context}
```

このコードを実行するために Cohere の API キーが必要です。Cohere のダッシュボードで取得することができます。トライアルキーで大丈夫です。取得した API キーを ".env" ファイルに以下の書式で記載しておきます。


```.env ファイル
COHERE_API_KEY=XXXXXXXXXX
```

コード中の "DOCUMENT_NAME" を変更することで pdfminer.six で抽出したテキストと Marker で変換したマークダウンを切り替えます。
実行結果を標準出力と Python スクリプトと同じフォルダーにある report フォルダー配下のファイルに出力されます。

### テスト結果
#### テキストの場合（Table 1 に関する質問）
|id|question|モデルの応答|Ground truth|正誤|
|--|--------|------------|------------|----|
|1|この論文のタイトルは？|Lost in the Middle: How Language Models Use Long Contexts|Lost in the Middle: How Language Models Use Long Contexts|正|
|2|Closed-Book と Oracle のそれぞれのケースにおいて LongChat-13B (16K)  の accuracy はいくつでしたか？|Closed-Bookの場合、LongChat-13B (16K)のaccuracyは31.5%でした。Oracleの場合、LongChat-13B (16K)のaccuracyは81.9%でした。|Closed-Book：35.0%、Oracle：83.4%|誤|
|3|Closed-Book と Oracle のそれぞれのケースにおいて MPT-30B-Instruct の accuracy はいくつでしたか？|MPT-30B-InstructのClosed-Bookのaccuracyは31.5%で、Oracleのaccuracyは81.9%でした。|Closed-Book：31.5%、Oracle：81.9%|正|
|4|Closed-Book と Oracle のそれぞれのケースにおいて GPT-3.5-Turbo の accuracy はいくつでしたか？|Closed-Bookの場合のGPT-3.5-Turboのaccuracyは56.1%でした。Oracleの場合のaccuracyは88.3%でした。|Closed-Book：56.1%、Oracle：88.3%|正|
|5|Closed-Book と Oracle のそれぞれのケースにおいて GPT-3.5-Turbo (16K)  の accuracy はいくつでしたか？|Closed-Bookの設定では、GPT-3.5-Turboのaccuracyは56.1%でした。Oracleの設定では、GPT-3.5-Turboのaccuracyは88.3%でした。|Closed-Book：56.0%、Oracle：88.6%|誤|
|6|Closed-Book と Oracle のそれぞれのケースにおいて Claude-1.3 の accuracy はいくつでしたか？|Closed-Bookのケースでは48.3％、Oracleのケースでは76.1％でした。|Closed-Book：48.3%、Oracle：76.1%|正|
|7|Closed-Book と Oracle のそれぞれのケースにおいて Claude-1.3 (100K)  の accuracy はいくつでしたか？|Closed-Bookのaccuracyは56.1％、Oracleのaccuracyは76.1％でした。|Closed-Book： 48.2%、Oracle：76.4%|誤|

**正解率：50%**
id=1 は、論文タイトルを聞いていますので除外すると Table 1 に関しては 6問中3問正解で正解率は 50% でした。
#### テキストの場合（Table 3 に関する質問）

|id|question|モデルの応答|Ground truth|正誤|
|--|--------|------------|------------|----|
|8|10 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値、標準偏差、最大値はそれぞれ 3464.6 ± 202.3、4955、5181.9 ± 294.7 です。|平均：1749.9 、標準偏差：112.4、最大値：2511|誤|
|9|10 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値、標準偏差、最大値は、10個のドキュメントを含むプロンプトに対してそれぞれ1499.7、88.5、1907でした。|平均：1499.7、標準偏差：88.5、最大値：1907|誤|
|10|10 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|GPT-3.5-Turboのトークン数の平均値、標準偏差、最大値はそれぞれ2946.2 ± 155.1、3920、4419.2 ± 226.5です。|平均：1475.6、標準偏差：86.5、最大値：1960|誤|
|11|10 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：1475.6、標準偏差：86.5、最大値：1960|誤|
|12|20 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|平均値：1,475.6トークン、標準偏差：86.5トークン、最大値：1,960トークン|平均：3464.6、標準偏差：202.3、最大値：4955|誤|
|13|20 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30B のトークン数の平均値、標準偏差、最大値は、それぞれ 1499.7 ± 88.5、1907、2048 でした。|平均：2962.4、標準偏差：158.4、最大値：3730|誤|
|14|20 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|平均トークン数は 1475.6 で、標準偏差は 86.5、最大値は 1960 でした。|平均：2946.2、標準偏差：155.1、最大値：3920|誤|
|15|20 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：2946.2、標準偏差：155.1、最大値：3920|誤|
|16|30 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|平均値：1,749.9トークン、標準偏差：112.4トークン、最大値：2,011トークン|平均：5181.9、標準偏差：294.7、最大値：7729|誤|
|17|30 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値、標準偏差、最大値はそれぞれ1499.7 ± 88.5、1907、2048でした。|平均：4426.9、標準偏差：230.5、最大値：5475|誤|
|18|30 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|トークン数の平均値は 3,464.6 で、標準偏差は 202.3、最大値は 7,729 でした。|平均：4419.2、標準偏差：226.5、最大値：6101|誤|
|19|30 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：4419.2、標準偏差：226.5、最大値：6101|誤|

**正解率：0%**
Table 3 に関しては 12問中正解なしで正解率は 0% でした。

#### テキストの場合（Table 4 に関する質問）

|id|question|モデルの応答|Ground truth|正誤|
|--|--------|------------|------------|----|
|20|75 KV pairs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：5444.5、標準偏差：19.1、最大値：5500|誤|
|21|75 KV pairs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値、標準偏差、最大値はそれぞれ1499.7 ± 88.5、1907、2048でした。|平均：4110.5、標準偏差：23.8、最大値：4187|誤|
|22|75 KV pairs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：3768.7、標準偏差：25.6、最大値：3844|誤|
|23|75 KV pairs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：3768.7、標準偏差：25.6、最大値：3844|誤|
|24|140 KV pairs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：10072.4、標準偏差：24.1、最大値：10139|誤|
|25|140 KV pairs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値、標準偏差、最大値はそれぞれ1499.7 ± 88.5、1907、2048でした。|平均：7600.9、標準偏差：31.1、最大値：7687|誤|
|26|140 KV pairs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：6992.8、標準偏差：34.1、最大値：7088|誤|
|27|140 KV pairs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：6992.8、標準偏差：34.1、最大値：7088|誤|
|28|300 KV pairs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：21467.3、標準偏差：35.9、最大値：21582|誤|
|29|300 KV pairs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値、標準偏差、最大値はそれぞれ1499.7 ± 88.5、1907、2048でした。|平均：16192.4、標準偏差：46.6、最大値：16319|誤|
|30|300 KV pairs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：14929.4、標準偏差：50.7、最大値：15048|誤|
|31|300 KV pairs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：14929.4、標準偏差：50.7、最大値：15048|誤|

**正解率：0%**
Table 4 に関しては 12問中0問正解で正解率は 0% でした。

#### マークダウンの場合（Table 1 に関する質問）
**※Marker は、OCRを強制する環境変数は設定せず OCR を使わないパターン**
**※ Table 1 は、正しく表に変換されたケースです**
|id|question|モデルの応答|Ground truth|正誤|
|--|--------|------------|------------|----|
|1|この論文のタイトルは？|Lost In The Middle: How Language Models Use Long Contexts|Lost in the Middle: How Language Models Use Long Contexts|正|
|2|Closed-Book と Oracle のそれぞれのケースにおいて LongChat-13B (16K)  の accuracy はいくつでしたか？|Closed-Bookの設定では35.0%、Oracleの設定では83.4%でした。|Closed-Book：35.0%、Oracle：83.4%|正|
|3|Closed-Book と Oracle のそれぞれのケースにおいて MPT-30B-Instruct の accuracy はいくつでしたか？|Closed-Bookの設定におけるMPT-30B-Instructのaccuracyは31.5％、Oracleの設定では81.9％でした。|Closed-Book：31.5%、Oracle：81.9%|正|
|4|Closed-Book と Oracle のそれぞれのケースにおいて GPT-3.5-Turbo の accuracy はいくつでしたか？|Closed-Book の設定における GPT-3.5-Turbo の accuracy は 56.1% でした。Oracle の設定における accuracy は 88.3% でした。|Closed-Book：56.1%、Oracle：88.3%|正|
|5|Closed-Book と Oracle のそれぞれのケースにおいて GPT-3.5-Turbo (16K)  の accuracy はいくつでしたか？|Closed-Bookの場合のGPT-3.5-Turboのaccuracyは56.1%でした。Oracleの場合のaccuracyは88.3%です。|Closed-Book：56.0%、Oracle：88.6%|誤|
|6|Closed-Book と Oracle のそれぞれのケースにおいて Claude-1.3 の accuracy はいくつでしたか？|Closed-Bookの設定におけるClaude-1.3のaccuracyは48.3%でした。Oracleの設定におけるaccuracyは76.1%です。|Closed-Book：48.3%、Oracle：76.1%|正|
|7|Closed-Book と Oracle のそれぞれのケースにおいて Claude-1.3 (100K)  の accuracy はいくつでしたか？|Closed-Bookのaccuracyは48.2%で、Oracleのaccuracyは76.4%でした。|Closed-Book： 48.2%、Oracle：76.4%|正|

**正解率：83.3%**
Table 1 に関しては 6問中5問正解で正解率は 83.3% でした。

#### マークダウンの場合（Table 3 に関する質問）
**※Marker は、OCRを強制する環境変数は設定せず OCR を使わないパターン**
**※ Table 3 は、表として認識されなかったケースです**

|id|question|モデルの応答|Ground truth|正誤|
|--|--------|------------|------------|----|
|8|10 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値は1749.9、標準偏差は112.4、最大値は2511 でした。|平均：1749.9 、標準偏差：112.4、最大値：2511|正|
|9|10 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は1499.7、標準偏差は88.5、最大値は1907でした。|平均：1499.7、標準偏差：88.5、最大値：1907|正|
|10|10 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|GPT-3.5-Turboのトークン数の平均値は4419.2、標準偏差は226.5、最大値は6101です。|平均：1475.6、標準偏差：86.5、最大値：1960|誤|
|11|10 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値は4419.2、標準偏差は226.5、最大値は6101です。|平均：1475.6、標準偏差：86.5、最大値：1960|誤|
|12|20 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値は1749.9、標準偏差は112.4、最大値は2511 でした。|平均：3464.6、標準偏差：202.3、最大値：4955|誤|
|13|20 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は1499.7で、標準偏差は88.5、最大値は1907でした。|平均：2962.4、標準偏差：158.4、最大値：3730|誤|
|14|20 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|GPT-3.5-Turboのトークン数の平均値は4419.2、標準偏差は226.5、最大値は6101でした。|平均：2946.2、標準偏差：155.1、最大値：3920|誤|
|15|20 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値は4419.2、標準偏差は226.5、最大値は6101です。|平均：2946.2、標準偏差：155.1、最大値：3920|誤|
|16|30 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値は 1749.9 で、標準偏差は 112.4、最大値は 2511 でした。|平均：5181.9、標準偏差：294.7、最大値：7729|誤|
|17|30 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は4419.2、標準偏差は226.5、最大値は6101でした。|平均：4426.9、標準偏差：230.5、最大値：5475|誤|
|18|30 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：4419.2、標準偏差：226.5、最大値：6101|誤|
|19|30 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値は4419.2、標準偏差は226.5、最大値は6101です。|平均：4419.2、標準偏差：226.5、最大値：6101|正|

**正解率：25%**
Table 3 に関しては 12問中3問正解で正解率は 25% でした。

#### マークダウンの場合（Table 4 に関する質問）
**※Marker は、OCRを強制する環境変数は設定せず OCR を使わないパターン**
**※ Table 4 は、表として認識されたがヘッダーが間違っていたケースです**

|id|question|モデルの応答|Ground truth|正誤|
|--|--------|------------|------------|----|
|20|75 KV pairs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値、標準偏差、最大値はそれぞれ 5444.5 ± 19.1、5500、10072.4 ± 24.1 でした。|平均：5444.5、標準偏差：19.1、最大値：5500|誤|
|21|75 KV pairs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は4110.5、標準偏差は23.8、最大値は4187でした。|平均：4110.5、標準偏差：23.8、最大値：4187|正|
|22|75 KV pairs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：3768.7、標準偏差：25.6、最大値：3844|誤|
|23|75 KV pairs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値は3768.7、標準偏差は25.6、最大値は3844でした。|平均：3768.7、標準偏差：25.6、最大値：3844|正|
|24|140 KV pairs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値は 5444.5 で、標準偏差は 19.1、最大値は 10072.4 でした。|平均：10072.4、標準偏差：24.1、最大値：10139|誤|
|25|140 KV pairs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は4110.5、標準偏差は23.8、最大値は4187でした。|平均：7600.9、標準偏差：31.1、最大値：7687|誤|
|26|140 KV pairs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：6992.8、標準偏差：34.1、最大値：7088|誤|
|27|140 KV pairs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値は4419.2、標準偏差は226.5、最大値は6101でした。|平均：6992.8、標準偏差：34.1、最大値：7088|誤|
|28|300 KV pairs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値、標準偏差、最大値はそれぞれ 1749.9、112.4、2511 でした。|平均：21467.3、標準偏差：35.9、最大値：21582|誤|
|29|300 KV pairs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は4426.9、標準偏差は230.5、最大値は5475でした。|平均：16192.4、標準偏差：46.6、最大値：16319|誤|
|30|300 KV pairs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：14929.4、標準偏差：50.7、最大値：15048|誤|
|31|300 KV pairs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値、標準偏差、最大値はそれぞれ1475.6、86.5、1960です。|平均：14929.4、標準偏差：50.7、最大値：15048|誤|

**正解率：16.6%**
Table 4 に関しては 12問中2問正解で正解率は 16.6% でした。

#### OCR強制マークダウンの場合（Table 3 に関する質問）
**※Marker は、OCRを強制する環境変数は設定して OCR を使ったパターン**
**※ Table 3 は、表として認識されたがヘッダーが間違っていたケースです**

|id|question|モデルの応答|Ground truth|正誤|
|--|--------|------------|------------|----|
|8|10 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値は 1749.9 で、標準偏差は 112.4 でした。最大値は 5500 でした。|平均：1749.9 、標準偏差：112.4、最大値：2511|誤|
|9|10 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は1499.7、標準偏差は88.5、最大値は482です。|平均：1499.7、標準偏差：88.5、最大値：1907|誤|
|10|10 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：1475.6、標準偏差：86.5、最大値：1960|誤|
|11|10 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値と標準偏差は、それぞれ1475.6 ± 86.5、最大値は156.0です。|平均：1475.6、標準偏差：86.5、最大値：1960|誤|
|12|20 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値は 1749.9 で、標準偏差は 112.4 でした。最大値は 5500 でした。|平均：3464.6、標準偏差：202.3、最大値：4955|誤|
|13|20 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は1499.7、標準偏差は88.5、最大値は482でした。|平均：2962.4、標準偏差：158.4、最大値：3730|誤|
|14|20 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：2946.2、標準偏差：155.1、最大値：3920|誤|
|15|20 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値は15.3、標準偏差は2.2、最大値は29です。|平均：2946.2、標準偏差：155.1、最大値：3920|誤|
|16|30 docs のテストケースのおいて LongChat-13B (16K) のトークン数の平均と標準偏差、最大値はいくつでしたか？|LongChat-13B (16K) のトークン数の平均値は 1749.9 で、標準偏差は 112.4 でした。トークン数の最大値は 5500 でした。|平均：5181.9、標準偏差：294.7、最大値：7729|誤|
|17|30 docs のテストケースのおいて MPT-30B のトークン数の平均と標準偏差、最大値はいくつでしたか？|MPT-30Bのトークン数の平均値は1499.7、標準偏差は88.5、最大値は482です。|平均：4426.9、標準偏差：230.5、最大値：5475|誤|
|18|30 docs のテストケースのおいて GPT-3.5-Turbo のトークン数の平均と標準偏差、最大値はいくつでしたか？|その質問には提供された情報からは回答できません。|平均：4419.2、標準偏差：226.5、最大値：6101|誤|
|19|30 docs のテストケースのおいて Claude-1.3 のトークン数の平均と標準偏差、最大値はいくつでしたか？|Claude-1.3のトークン数の平均値と標準偏差は、それぞれ419.2と226.5です。トークン数の最大値は6101です。|平均：4419.2、標準偏差：226.5、最大値：6101|正|

**正解率：8.3%**
OCR強制の Table 3 に関しては 12問中1問正解で正解率は 8.3% でした。

### テスト結果のまとめ
#### Marker について
- シンプルな表であればマークダウンに正しく変換できる

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/fd2b7721-ba5b-b892-daf7-9b9d03db938f.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/bf6ea1d5-0e9c-2708-8995-efde427658e3.png)|
|---|---|
|変換前のPDF|変換後のマークダウン|

- ヘッダーが複雑な表は OCR なしの場合、同じ構造の表でも表と認識される場合、されない場合がある。認識されてもヘッダーが正しく変換されなかった。また、OCRを強制してもヘッダーは直らなかった

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/19d7dead-fa6b-1a2e-9e20-cba5306b420c.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/e4cb0a5a-6d1e-40cf-57b7-b388f89d9d0e.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/ba1f69ae-bfce-3115-9d91-085d802e222d.png)|
|---|---|---|
|変換前のPDF|OCR無しのマークダウン|OCR強制のマークダウン|

- OCRなしでは表として認識されないケースではOCRを強制することで表として認識されるが正しいマークダウンには変換されなかった

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/d9bc1c9b-0cc2-c70f-c198-2b5146767dc7.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/70df43e0-192b-70c3-e4a1-50858d5cd416.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/8523f95f-ebfd-7e62-67ef-33c75ed3935f.png)|
|---|---|---|
|変換前のPDF|OCR無しのマークダウン|OCR強制のマークダウン|

#### LLM のコンテキスト情報としてのマークダウンの効果（表中のデータの抽出に関して）
- 正しいマークダウンの表は LLM の正解率を向上させる。ただし、完全ではない
- ヘッダーが間違っているマークダウンの表は、LLM の正解率を上げる効果はほぼない
- 表として認識されなかった場合はテキストと違いはほぼない

## まとめ

LLMにPDFの情報を読み込ませる際、以下の2つの方法を比較しました。

1. Markerを使ってPDFをマークダウンに変換したデータを使う方法
2. pdfminer.sixで抽出したテキストを直接読み込ませる方法

比較の結果、表のデータを抽出するという点においては、方法1のMarkerを使ったマークダウンの方がやや優位性があることがわかりました。ただし、決定的な解とはならないようです。

また、今回のテストでは、プロンプトに特別な工夫はしていませんでした。平均や標準偏差など、元のPDFやマークダウンには直接現れない表現で質問しています。これは少し意地悪な質問ですが、LLMは完璧ではないものの、意図を汲み取って平均値と標準偏差の値を取り出そうとしてくれました。

今回のテストでは、正解できないケースが多かったのですが、マークダウンの表の部分を見ると、ヘッダーの崩れ方にはパターンがあるようです。そこで、In-Context Learningを用いて、間違ったヘッダーと正しいヘッダーの例をいくつか与えることで、改善する可能性がありそうです。

~~また、今回はマークダウンをそのままLLMのプロンプトにセットしていますが、表構造はHTMLなどに変換した方が良い結果が得られる可能性もあります。~~ Table １ の表を HTML 化してみましたが正解率に変化はありませんでした。言語についてはドキュメント上は日本語に対応しているようですが今回は確かめていません。これらは今後の課題として検討していきたいと思います。 

## えっと、あとがきです
Claude Chat（LLM の Claude 3 Opus/Sonnet/Haiku 単体ではなく PDF の解析なども含めたアプリケーションとしての [Claude](https://claude.ai/chats)） はPDFをポンと放り込んだだけでこのくらいのタスクはこなしてくれますね、どうやっているのでしょう？Claude の頭が良いということだけではないですよね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/b21eaf91-79cb-ba57-cdd5-8e63eb8bea03.png)
Claude が注目されはじめた頃は、LLMとしての能力そのものよりもこの PDF 解析能力（おそらく LLM のコンテキストに渡す前の処理）の部分の精度と使い勝手の良さが人気でしたね。もちろん、大きなコンテキストウィンドウもですが。
【更新】OCRの surya がアップデートされて高速になったようです。Mkarker への merge はまもなくとのこと。

https://x.com/VikParuchuri/status/1811798636759793726

"Table parsing and OCR heuristic improvements in marker" というコメントも見えるので期待したいですね。

https://x.com/VikParuchuri/status/1811798641600041362

## おまけ
他にもいろいろ記事を書いていますので良かったらお立ち寄りください。

https://qiita.com/yuji-arakawa/items/28f30a5434ba429f3f16

https://qiita.com/yuji-arakawa/items/8d065ae3df536f5cd559

https://qiita.com/yuji-arakawa/items/fd4fd0c026ecfa664d97

https://qiita.com/yuji-arakawa/items/1a8cfeff8f81ba808389

https://qiita.com/yuji-arakawa/items/654d936ca616334286b0

https://qiita.com/yuji-arakawa/items/1a8cfeff8f81ba808389

https://qiita.com/yuji-arakawa/items/9cd485debd5b0d18aca2

https://qiita.com/yuji-arakawa/items/042937eaf16fa00cf491

https://qiita.com/yuji-arakawa/items/05e3455572d3b09a53dc

https://qiita.com/yuji-arakawa/items/2d4f6eff17a5410dba2d

https://qiita.com/yuji-arakawa/items/597c4bd9f3d5b4212b51

https://aws.amazon.com/jp/blogs/news/leveraging-pinecone-on-aws-marketplace-as-a-knowledge-base-for-amazon-bedrock/