はじめに
こんにちは!
株式会社ユーザベース スピーダ事業の飯田です。 普段はベクトル検索用の埋め込みモデルの学習・提供するAPIの構築およびローカルLLMの推進を行っています。
今回は、画像やPdfの表から情報抽出に関するTipsを紹介します。 主にGeminiを活用して行ったのですが、行名・列名の揺れが発生したため精度検証で困難に直面しました。その際、行名・列名を先に抽出し、その後に値を抽出するという方法を用いることで、精度検証をコスパよく行えました。現場レベルで簡便な精度検証が必要という場合によいと感じたため紹介します。
タスク概要
表からの情報抽出は、Pdfや画像の表に記載されている情報を、コンピュータが扱いやすい形式に変換することを目的としたタスクです。 今回は、可能な限り表の情報をそのままjsonに変換することに取り組みました。変換先をjsonにしたのは、構造化出力モードで取り扱いやすいためです。
課題
取り組み始めた当初は、表からの情報抽出の評価をすべて人手で行っていました。しかし、この方法では、評価のコストが高く、入力形式(画像にするかpdfのままにするか)や前処理、使用するモデルについて、どのような影響があるのかの評価が困難でした。そのため、いろいろな表を試して、なんとなく良さそうということで、意思決定をするしかない状況に陥ってしまいました。この状況を解決する方法の1つとして、評価データを用意し、機械的に評価できるようにする方法があります。しかし、評価データを用意するにも課題がありました。
まず、研究で行われるようなbonding boxを用いるような方法は、今回のケースでは表の全項目をbonding boxで囲む必要があるため、作成コストが高くなります。 さらに、Geminiが出力するBounding Box自体が、実行のたびに変わるため、採用できませんでした。
また、情報抽出後の形式(例えば、json)を用意して、正解率を算出するような方法にも課題がありました。それは、表の行・列名自体が、何かしらの変更実行するたびに変化してしまうという点です。完全一致による精度の検証では、行・列名が一致するかどうかで、精度が大幅に変わってしまうため、これが精度の数値に大きく影響を与えます。そのため、行・列名の揺れが人目には許容範囲であっても、その揺れで精度が大きく落ちてしまうという課題がありました。
解決方法
これを解決するために、今回は予め行と列のみを抽出し、抽出した行と列を出力形式の指定事例としてプロンプトに入力するという方法を取りました。
Step1: 行・列名のみの抽出
この方法では、まず行と列のみを入力した表から抽出します。具体的には、システムプロンプトとして、以下のように出力フォーマットを指定しています。
あなたは、複雑な表形式ドキュメントを解析し、構造化されたJSONに変換する専門家です。
表とそれを解析して列名と行名を抽出したJSONが与えられます。
表とJSONから、指定されたフォーマットのJSONを生成してください。
# JSON生成のルール
## 1. 全体構造
- 画像に含まれる**すべてのデータ列**を処理してください。
## 2. 列名の処理方法
(考慮したい特有のルールを記載する)
## 3. 行名の処理方法
(考慮したい特有のルールを記載する)
...
# 出力フォーマット
- 生成するJSONは、必ず ```json ... ``` のコードブロック内に記述してください。
コードブロック```json(ここはコードブロックを使用。ブログの都合上消してます)
{
"columns":["列名1", "列名2", "列名3"],
"rows":["行名1", "行名2", "行名3"]
}
ここまで```
(上記コードブロックの注釈は、ブログ用に修正しています)
その後人手で評価を行い、もっとも精度が高かった出力を採用します。
Step2: 表の値の抽出
次に、ここで得られた列名・行名をつかって、モデルに値を抽出させます。具体的には以下のシステムプロンプトを使用しています。
# 命令
あなたは、複雑な表形式ドキュメントを解析し、構造化されたJSONに変換する専門家です。
与えられた表の画像から、記載されている情報を網羅的かつ忠実に抽出し、指定されたフォーマットのJSONを生成してください。
# JSON生成のルール
(もろもろの考慮したい要件)
# 出力フォーマット
- 生成するJSONは、必ず ```json ... ``` のコードブロック内に記述してください。
- 以下は、**ある諸元表を処理した場合の出力例**です。このキーと値の構造、フォーマットを参考に、今回与えられた画像の完全なJSONを生成してください。
コードブロック```json
{
"列キー1": {
"項目A": "値1",
"項目B": "値2"
},
"列キー2": {
"項目A": "値3",
"項目B": "値4"
}
}
ここまで```
今回の表では以下の形式にしてください。
コードブロック```json
(Step1で得られれた列名、行名をそれぞれ列キー、項目として上記形式に変換したjson)
ここまで```
(画像などを入れる)
(上記コードブロックの注釈は、ブログ用に修正しています)
この方法を取ることで、プロンプトやモデル、入力するデータの形式を変えても、表中の行・列名が比較的安定しました。 加えて、Geminiの出力を一部修正することで、各表における検証データを用意する工数を大幅に削減できました。 これによって、入力形式やモデルの違いによる精度の評価をさまざまな表で行うことができました。
なお、安定したとはいえ、これでも行名・列名が揺れるケースはありました。これは、モデルの指示追従性も合わせて評価しているとみなし、完全一致で評価を行っています。
まとめと課題
今回は画像やpdfの表から情報抽出を行う際に、現場で簡便に評価を行う方法を紹介しました。まず列名・行名を抽出し、抽出した列名・行名を用いて値を取得するという2段階の方法です。 列名・行名を予め抽出することで、安定した精度評価になったと考えています。また、列名・行名に人手評価を通していますが、これもLLMで生成されたものなので、概ね情報抽出全体を通してLLMで行う際の代替指標としてある程度考慮できると考えています。さらに、列名・行名の一致に曖昧性を許す方法(例えば編集距離やcos類似度)と比べて、業務固有の知識必要性や許容範囲の調整を少なくできていると考えています。
しかしながら、この方法を実際にプロダクションで情報抽出をする際に用いると、2段階でLLMの処理を行うため、コストを増加させるという課題があります。そのため、表からの情報抽出を検証する場合に有効だと考えます。
謝辞
今回の取り組みでは、株式会社プロトソリューションの川村礼人様と金城理奈様に大変お世話になりました。この場を借りて御礼申し上げます。