21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-06-16

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 リポジトリはこちら

商用利用について

Markerは、研究や個人利用においては常に無料で利用可能ですが、商用利用に関しては一部制限があります。

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

GPLライセンスの要件を取り除く(デュアルライセンス)、または収益制限を超えて商用利用したい場合は、別途 Datalab 社が提供するオプションを確認する必要があります。詳しくはこちらに書かれていますので商用利用をお考えの場合には確認してください。

インストール

前提条件

Python

version 3.9 以上(私は、3.11.9 で試しています)

PyTorch

PyTorch のインストール方法はPyTorch Get-Startedページで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ページで PyTorch がサポートする CUDA バージョンを確認して、CUDA をインストールします。CUDA の最新バージョンは、こちら、以前のバージョンはこちらです。
CUDA のバージョン確認ができたらPyTorch Get-StartedページでOS や CUDA バージョンを指定してインストールコマンドを確認します。私の場合は下のスクショのような組み合わせでした。
image.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 がサポートする言語のリストはこちらです。日本語も入ってますね!

PDF からマークダウンへの変換

PDFをマークダウンへ変換する Marker のコマンドは、単一ファイルの場合、つまり1つの PDF ファイルをマークダウン形式に変換する marker_single と複数のファイルを一気に変換する marker の2つがあります。
ここでは、単一ファイルの場合の 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のために、ドキュメントに含まれる言語をカンマ区切りで指定するオプションです。サポートする言語のリストはこちらです

Lost in the Middle を変換してみた

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

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
"0_image_0.png"などの png ファイルは、論文のPDFから抽出された画像ファイルです。
image.png
json ファイルには、全体のページ数や実際にOCR処理されたページ数などが記録されているようです。今回のPDFファイルはテキストが埋め込まれていてOCR無しでテキスト抽出が可能ですので OCR の統計情報はいずれも "0" になっています。
image.png
マークダウンファイルはこのような形でとても綺麗に出ていますね。
image.png
Corsor Editor のプレビューで表示したところです。
image.png

表の変換について

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

  • "Table 1"
image.png image.png
変換前のPDF 変換後のマークダウン

綺麗に変換できているようです。

  • "Table 2"
image.png image.png
変換前のPDF 変換後のマークダウン

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

  • "Table 3"
image.png image.png
変換前のPDF 変換後のマークダウン

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

  • "Table 4"
image.png image.png
変換前のPDF 変換後のマークダウン

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

OCR を強制してみる

表の変換がおしいところまで来ました。このままでも大規模言語モデル(LLM)への入力にするなら後はLLMがなんとかしてくれるのではと淡い期待を抱きたいところですが、その前に Marker で他にできることがないかとドキュメントを見ていると "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 image.png image.png
変換前のPDF OCR無しのマークダウン OCR強制のマークダウン

"Table 1"は今回も正常に認識されています。

  • "Table 2" : OCR無しではヘッダーのカラム位置が間違っていたケースです
image.png image.png image.png
変換前のPDF OCR無しのマークダウン OCR強制のマークダウン

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

  • "Table 3" : OCR無しではテーブルとして認識されなかったケースです
image.png image.png image.png
変換前のPDF OCR無しのマークダウン OCR強制のマークダウン

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

  • "Table 4" : OCR無しではヘッダーのカラム位置が間違っていたケースです
image.png image.png image.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 image.png
変換前のPDF 変換後のマークダウン
  • ヘッダーが複雑な表は OCR なしの場合、同じ構造の表でも表と認識される場合、されない場合がある。認識されてもヘッダーが正しく変換されなかった。また、OCRを強制してもヘッダーは直らなかった
image.png image.png image.png
変換前のPDF OCR無しのマークダウン OCR強制のマークダウン
  • OCRなしでは表として認識されないケースではOCRを強制することで表として認識されるが正しいマークダウンには変換されなかった
image.png image.png image.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 1 の表を HTML 化してみましたが正解率に変化はありませんでした。言語についてはドキュメント上は日本語に対応しているようですが今回は確かめていません。これらは今後の課題として検討していきたいと思います。

えっと、あとがきです

Claude Chat(LLM の Claude 3 Opus/Sonnet/Haiku 単体ではなく PDF の解析なども含めたアプリケーションとしての Claude) はPDFをポンと放り込んだだけでこのくらいのタスクはこなしてくれますね、どうやっているのでしょう?Claude の頭が良いということだけではないですよね。
image.png
Claude が注目されはじめた頃は、LLMとしての能力そのものよりもこの PDF 解析能力(おそらく LLM のコンテキストに渡す前の処理)の部分の精度と使い勝手の良さが人気でしたね。もちろん、大きなコンテキストウィンドウもですが。
【更新】OCRの surya がアップデートされて高速になったようです。Mkarker への merge はまもなくとのこと。

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

おまけ

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

21
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?