LangChainのPDF Loaderを試してみる
概要
LangChainにはいろいろDocument Loaderが用意されているが、今回はPDFをターゲットにしてみる。
- PyPDF
- MathPix
- Unstructured
- PDFMiner
- PyMuPDF
まあこの記事の劣化版です;)
環境
当初Unstruturedをいろいろ触ってたのだけど、
- Colaboratoryだとうまくいかなかった(カーネル再起動してもモジュール見つけられなかった)
- ローカルのDockerだとメモリが足りない
とうことで自作PC上のDockerでやることにした。
- 自作PC上のDocker
- スペック等についてはここ
- VSCode devcontainer
- イメージ: mcr.microsoft.com/devcontainers/python:0-3.10
サンプルデータ
PDFのサンプルは以下を使う。
- クリエイティブ・コモンズの「パワー・オブ・オープン」日本版
- https://creativecommons.jp/tag/ブックレット/
- ライセンス的にも問題なさそう
- 文字の段組みとかもあるので確認するのに良さそう
- 約3.2MB
試してみた
Unstructured
We're delivering the first ever open-source toolkit designed to make it easy to prepare unstructured data like PDFs, HTML and Word Documents for downstream data science tasks.
つまり、パースが難しそうなタイプのいろんなファイルがUnstructuredの統一されたインタフェース経由でパースできるというのがウリ。
レポジトリはいくつかに分かれている。以下DeepL訳。
- unstructured - 非構造化データ用の前処理コンポーネントを含むコアライブラリ。
- unstructured-api - 多くの種類の生ドキュメントを処理できる、unstructuredのコアパーティショニング機能をAPIとして提供するプロジェクト。
- unstructured-api-tools - データサイエンスや機械学習のワークフローで簡単に利用できるようにパイプラインノートブックをREST APIに変換するライブラリ。
- unstructured-inference - 推論コードを含むライブラリで、unstructuredのローカルまたはホストされたサービスとして使用することができる。
で、通常はunstructuredだけでよいけど、PDFを扱う場合にはunstructured-inferenceを使う様子。
インストール
ざっと見た感じ以下が必要そう。
- libmagic
- ファイル種別の判別
- poppler
- PDFのレンダリング
- tesseract
- OCRライブラリ。たぶん画像とかはOCRで読み込むんだと思う。
- libreoffice
- MS Officeのドキュメント向け。あんまり調べてないので今回はパス。
- detectron
- 物体検出ライブラリ。たぶんtesseractと同じような使い方っぽい。
- libxml2/libxslt
- HTMLとかXMLとかのパース。
依存するOSパッケージを先にaptでインストール。
libmagic-dev
poppler-utils
libpoppler-dev
tesseract-ocr
libtesseract-dev
tesseract-ocr-jpn
tesseract-ocr-jpn-vert
tesseract-ocr-script-jpan
tesseract-ocr-script-jpan-vert
libxml2-dev
libxslt1-dev
libgl1-mesa-dev
tesseractは日本語向けパッケージがあるようなので入れておいた。libgl1はどうやらunstructured-inferenceの依存関係にOpenCVが含まれていてそっちで必要らしい(入れないと"libGL.so.1 not found"になる)
$ sudo apt update
$ sudo xargs apt install -y < packages.txt
次にPythonパッケージ。
- langchain
- openai
- faiss-cpu
この辺はLangChain+VectorDBで基本となるものなので説明は割愛。
langchain
openai
faiss-cpu
$ pip install -r requirements1.txt
- unstructured[local-inference]
- "detectron2@git+https://github.com/facebookresearch/detectron2.git@e2ce8dc#egg=detectron2"
- layoutparser[layoutmodels,tesseract]
unstructuredはPDFを扱う場合は"unstructured[local-inference]"というパッケージになる。さらにdetectronやlayoutparserをインストールすると、レイアウトを考慮するために物体検出やOCRなどの画像処理が行われるようになる=PDF内の画像からも文字列をパースできるということになるのだと思う。
まずはunstructured[local-inference]だけ入れてやってみる。
$ pip install unstructured[local-inference]
スクリプト
from langchain.document_loaders import UnstructuredFileLoader
loader = UnstructuredFileLoader("pdf/tpoo_jap.pdf")
docs = loader.load()
print(f"number of docs: {len(docs)}")
print("--------------------------------------------------")
print(docs[0].page_content)
実行する。
$ python sample.py
初回のみ以下が表示される。どうやら内部的にNLTKを使っているらしく、トークナイザーがダウンロードされる。
[nltk_data] Downloading package punkt to /home/vscode/nltk_data...
[nltk_data] Unzipping tokenizers/punkt.zip.
[nltk_data] Downloading package averaged_perceptron_tagger to
[nltk_data] /home/vscode/nltk_data...
[nltk_data] Unzipping taggers/averaged_perceptron_tagger.zip.
そしてdetectron2がない場合はこういうメッセージが表示される。
detectron2 is not installed. Cannot use the hi_res partitioning strategy. Falling back to partitioning with the fast strategy.
結果。まずこの部分。
print(f"number of docs: {len(docs)}")
number of docs: 1
元々のUnstrucuredではテキストは「要素」ごとに分けられる。LangChainではなくUnstruturedで同じことをやってみる。
from unstructured.partition.auto import partition
elements = partition("pdf/tpoo_jap.pdf")
print(f"number of docs: {len(elements)}")
print("\n----------\n".join([str(e) for e in elements]))
実行すると先ほどと同じメッセージが表示される。つまり、detectron2を必要としているのはUnstructuredであることがわかる。
detectron2 is not installed. Cannot use the hi_res partitioning strategy. Falling back to partitioning with the fast strategy.
上記の該当部分はこうなる。
555
実際にパースしたデータ。比較するために「パワー・オブ・オープン」日本版のP-10を見てみる。実際はこんな感じ。
段組みやレイアウトなどもあるので良さそう。
でパースしたデータはこうなっている。
(snip)
----------
ジューン・コーエン
----------
TED トーク「この驚異的な普及は、TED
----------
自由にアイデアを広げる
----------
ニューヨーク
----------
いまやウェブに欠かせない存在となったオンライン動画配信プロジェクトのTEDトークは、当初は一部の 限定された人たちを対象としたセミナーとして始まりました。TEDトークの講演者たちはそれぞれの革新 的なテーマについて講演し、TEDトークがすべてクリエイティブ・コモンズのライセンスによって配信され てから5年間で、2億人以上の聴衆により視聴されています。
----------
「この驚異的な普及は、TEDトークがオープンで無償の配信によって公開されたことによるところが大きく 、CCライセンスは私たちだけの力では及ばないレベルまでTEDトークの普及を推し進めてくれました。」と TED Media 制作責任者のジュネ・コーエンさんは言います。
----------
そしてコーエンさんは続けます。「私たちが、自らのコンテンツを公開すると決めたのは、アイデアを広める という目的があったからです。私たちの取った決断はすべてその目的に基づいています。その意味でもク リエイティブ・コモンズは最も効率良くTEDトークの普及を促進し、かつ私たちの動画コンテンツがどのよ うに使われるべきかという議論を行う必要性から解放してくれました。」
----------
さらに、コーエンさんは続けます。「オンライン配信の決定は、とても物議を醸しました。それによって、人々 がカンファレンス費用の支払いを拒んだり、講演者に拒否されたりして、このプロジェクト自体が破綻して しまうのではないかとも懸念されました。」
----------
「初めてTEDトークのコンテンツを無料でリリースしてから一年後にカンファレンス料金を50%値上げし ましたが、それにもかかわらず参加チケットは1週間で完売し、さらに1,000名の順番待ちが発生しました 。講演者たちがトーク内容が早急に掲載されることを求めただけでなく、カンファレンス料金を払って参加 していただいた人々は会場で聞いたばかりの講演を自分の家族、友人や同僚に伝えたいと切望していま した。」と、コーエンさんは言います。
----------
TEDトークがスウェーデンの医師で統計学者のハンス・ロスリング(Hans Rosling) さんを特集した際の彼 の発展途上国についての発表は、CCライセンスがある課題を世に広める手助けになるという良い例を示 しています。「自分のTEDトークがオンラインで公開されたことは、それまで行ってきたどの活動よりも自分 のキャリアに影響を与えたとハンスは私に言いました。」とコーエンさんは言います。「そのことが彼にとっ てまったく新たな世界を開いたのです。」
----------
「私たちが意図していた以外の結果も非常にポジティブなものでした。」とコーエンさんは言います。「TED の成長だけでなく、世界中の視聴者がひとつのグローバルなチームとなり、TEDのブランドを受け入れ、 さらなる革新を促しているのです。クリエイティブ・コモンズ・ライセンスを使うことによって、私たちがアイ デアを普及させることについて真剣であることを明確に視聴者に伝えられたのです。」
----------
トークがオープンで無償の 配信によって公開されたこ とによるところが大きく、CC ライセンスは私たちだけの 力では及ばないレベルまで TEDトークの普及を推し進 めてくれました。」
----------
詳細 http://www.ted.com/talks
----------
r e i c u L n e B
----------
: o t o h P
----------
(snip)
要素ごとに分割されているのがわかる。
LangChainのUnstructuredに戻る。
number of docs: 1
実際のデータはこうなっている。
number of docs: 1
--------------------------------------------------
The
POWER OF
The
POWER OF
ACKNOWLEDGEMENTS
(snip)
ジューン・コーエン
TED トーク「この驚異的な普及は、TED
自由にアイデアを広げる
ニューヨーク
いまやウェブに欠かせない存在となったオンライン動画配信プロジェクトのTEDトークは、当初は一部の 限定された人たちを対象としたセミナーとして始まりました。TEDトークの講演者たちはそれぞれの革新 的なテーマについて講演し、TEDトークがすべてクリエイティブ・コモンズのライセンスによって配信され てから5年間で、2億人以上の聴衆により視聴されています。
「この驚異的な普及は、TEDトークがオープンで無償の配信によって公開されたことによるところが大きく 、CCライセンスは私たちだけの力では及ばないレベルまでTEDトークの普及を推し進めてくれました。」と TED Media 制作責任者のジュネ・コーエンさんは言います。
そしてコーエンさんは続けます。「私たちが、自らのコンテンツを公開すると決めたのは、アイデアを広める という目的があったからです。私たちの取った決断はすべてその目的に基づいています。その意味でもク リエイティブ・コモンズは最も効率良くTEDトークの普及を促進し、かつ私たちの動画コンテンツがどのよ うに使われるべきかという議論を行う必要性から解放してくれました。」
さらに、コーエンさんは続けます。「オンライン配信の決定は、とても物議を醸しました。それによって、人々 がカンファレンス費用の支払いを拒んだり、講演者に拒否されたりして、このプロジェクト自体が破綻して しまうのではないかとも懸念されました。」
「初めてTEDトークのコンテンツを無料でリリースしてから一年後にカンファレンス料金を50%値上げし ましたが、それにもかかわらず参加チケットは1週間で完売し、さらに1,000名の順番待ちが発生しました 。講演者たちがトーク内容が早急に掲載されることを求めただけでなく、カンファレンス料金を払って参加 していただいた人々は会場で聞いたばかりの講演を自分の家族、友人や同僚に伝えたいと切望していま した。」と、コーエンさんは言います。
TEDトークがスウェーデンの医師で統計学者のハンス・ロスリング(Hans Rosling) さんを特集した際の彼 の発展途上国についての発表は、CCライセンスがある課題を世に広める手助けになるという良い例を示 しています。「自分のTEDトークがオンラインで公開されたことは、それまで行ってきたどの活動よりも自分 のキャリアに影響を与えたとハンスは私に言いました。」とコーエンさんは言います。「そのことが彼にとっ てまったく新たな世界を開いたのです。」
「私たちが意図していた以外の結果も非常にポジティブなものでした。」とコーエンさんは言います。「TED の成長だけでなく、世界中の視聴者がひとつのグローバルなチームとなり、TEDのブランドを受け入れ、 さらなる革新を促しているのです。クリエイティブ・コモンズ・ライセンスを使うことによって、私たちがアイ デアを普及させることについて真剣であることを明確に視聴者に伝えられたのです。」
トークがオープンで無償の 配信によって公開されたこ とによるところが大きく、CC ライセンスは私たちだけの 力では及ばないレベルまで TEDトークの普及を推し進 めてくれました。」
詳細 http://www.ted.com/talks
r e i c u L n e B
: o t o h P
(snip)
要素に分割されずに一つのドキュメントになっていることがわかる。
LangChainのUnstructuredFileLoaderはデフォルトだと要素を一つにまとめてしまう。そもそもテキストの分割については分割の方法なども含めてtext splitterで行う、ということだからだと思う。
Unstructuredと同じように分割するにはmode="elements"
を指定する。
from langchain.document_loaders import UnstructuredFileLoader
loader = UnstructuredFileLoader("pdf/tpoo_jap.pdf")
docs = loader.load()
print(f"number of docs: {len(docs)}")
print("--------------------------------------------------")
print(docs[95].page_content)
number of docs: 555
--------------------------------------------------
いまやウェブに欠かせない存在となったオンライン動画配信プロジェクトのTEDトークは、当初は一部の 限定された人たちを対象としたセミナーとして始まりました。TEDトークの講演者たちはそれぞれの革新 的なテーマについて講演し、TEDトークがすべてクリエイティブ・コモンズのライセンスによって配信され てから5年間で、2億人以上の聴衆により視聴されています。
きちんと分割されていることがわかる。
で、この分割については以下のメッセージも絡んでいる。
detectron2 is not installed. Cannot use the hi_res partitioning strategy. Falling back to partitioning with the fast strategy.
Unstructuredにおける要素単位での「分割」=partitioningは2つのストラテジーがある。
- "hi_res"
- レイアウトなども踏まえてパースするので、精度は高いが時間がかかる。
- detectron2が必要
- detectronがない場合は"fast"ストラテジーにフォールバック
- "fast"
- テキストのみを処理するのでパースは速いが精度が落ちる
- 内部的にはpdfminer.sixが使われる様子
- 上記の通りdetectron2がない場合にこちらにフォールバックする
- detectron2があった場合でも明示的に"fast"が指定されていればこちら
と考えて良い様子。
LangChain側でもストラテジーを設定できるが、これは結局のところUnstructuredに渡しているだけ。
ということで、detectron2を有効にしてやってみる。layoutparserは指定しなくても依存関係で入ってるようにみえるので以下だけで良さそう。
$ pip install detectron2@git+https://github.com/facebookresearch/detectron2.git@e2ce8dc#egg=detectron2
ではUnstructured単体でやってみる。先ほどと同じスクリプト。
from unstructured.partition.auto import partition
elements = partition("pdf/tpoo_jap.pdf")
print(len(elements))
print("\n----------\n".join([str(e) for e in elements]))
detectron2 is not installed. 〜
のメッセージが表示されず、先程よりも時間が少しかかることになった。結果は以下。
283
The POWER OF
----------
(snip)
----------
ジューン・コーエン TED トーク 自由にアイデアを広げる 「この驚異的な普及は、TED ニューヨーク トークがオープンで無償の いまやウェブに欠かせない存在となったオンライン動画配信プロジェクトのTEDトークは、当初は一部の
----------
限定された人たちを対象としたセミナーとして始まりました。TEDトークの講演者たちはそれぞれの革新 てから5年間で、2億人以上の聴衆により視聴されています。
----------
、CCライセンスは私たちだけの力では及ばないレベルまでTEDトークの普及を推し進めてくれました。」と TED Media 制作責任者のジュネ・コーエンさんは言います。
----------
そしてコーエンさんは続けます。「私たちが、自らのコンテンツを公開すると決めたのは、アイデアを広める という目的があったからです。私たちの取った決断はすべてその目的に基づいています。その意味でもク リエイティブ・コモンズは最も効率良くTEDトークの普及を促進し、かつ私たちの動画コンテンツがどのよ うに使われるべきかという議論を行う必要性から解放してくれました。」
----------
さらに、コーエンさんは続けます。「オンライン配信の決定は、とても物議を醸しました。それによって、人々 がカンファレンス費用の支払いを拒んだり、講演者に拒否されたりして、このプロジェクト自体が破綻して しまうのではないかとも懸念されました。」
----------
http://www.ted.com/talks
----------
ましたが、それにもかかわらず参加チケットは1週間で完売し、さらに1,000名の順番待ちが発生しました していただいた人々は会場で聞いたばかりの講演を自分の家族、友人や同僚に伝えたいと切望していま した。」と、コーエンさんは言います。
----------
TEDトークがスウェーデンの医師で統計学者のハンス・ロスリング(Hans Rosling) さんを特集した際の彼 の発展途上国についての発表は、CCライセンスがある課題を世に広める手助けになるという良い例を示 しています。「自分のTEDトークがオンラインで公開されたことは、それまで行ってきたどの活動よりも自分 のキャリアに影響を与えたとハンスは私に言いました。」とコーエンさんは言います。「そのことが彼にとっ
----------
の成長だけでなく、世界中の視聴者がひとつのグローバルなチームとなり、TEDのブランドを受け入れ、 さらなる革新を促しているのです。クリエイティブ・コモンズ・ライセンスを使うことによって、私たちがアイ デアを普及させることについて真剣であることを明確に視聴者に伝えられたのです。」
----------
(snip)
detectron2がない場合に比べると要素数が減っているのがわかる。また、少し各要素の並びも違っているのがわかる。
ただ、"fast"で上手くできていた部分が逆にできなくなっていたり、抜け落ちてた利する部分もある。ドキュメントの区切りもやや微妙。推測だけど、detectronを使うとテキストとして抽出できていた部分も画像解析になってしまうのではないだろうか?
今回の場合は普通に"fast"でやったほうが品質的にはよい印象。ここはたぶんPDFの作りのよって変わってきそう。
detectron2がインストールしてあれば、LangChainでも書き方は変わらないので割愛。唯一書き方が変わるのは、detectron2がインストールされていて、その上で明示的にfastにするようなケースぐらいだと思う。
from langchain.document_loaders import UnstructuredFileLoader
loader = UnstructuredFileLoader("pdf/tpoo_jap.pdf", mode="elements", strategy="fast")
docs = loader.load()
print(f"number of docs: {len(docs)}")
print("--------------------------------------------------")
print(docs[95].page_content)
まとめ
- 準備さえしておけば同じインタフェースで複数のファイルタイプに対応できるUnstructuredは便利かも。
- とはいえ、パース結果は元のPDFファイルの内容やレイアウトにも影響されそう。実際にやってみないとわからない感がある。
- 上の例のように"hi_res"にせずに"fast"でテキスト部分だけ抽出するほうがクオリティが高い場合もありそう。
- PDFのテキストが少なくて、画像内に説明が含まれてる、というようなドキュメント(例えば画面キャプチャに説明を入れてある、等の技術マニュアルなど)等の場合には"hi_res"は便利かもしれない。
PDFMiner
こちらが本家だけどもうメンテされてない。
実際に使用されているのはこちら。上のfork。
上で書いたとおり、普通にdetectron2抜きでUnstructuredを使うとPDFMinerが使用される模様、ということでたぶん品質的にも同じになりそうなので割愛。
PyPDF
ちょっとややこしいのだけど、PyPDF2というのがあるが、こちらではない
ただしくは"pypdf"
元々"pypdf"だったのが”PyPDF2”になったけど、"PyPDF2"の開発は終わってて"pypdf"に戻ってきた、ということらしい。