Azure AI Document IntelligenceのAPIを使ってみる

概要

AI + OCR機能で、画像やPDFなどをテキスト化してくれるAzureのサービスです。(旧:Form Recognizer)
テキスト化だけでなく、Markdown化や表の抽出など、文章の構造化もしてくれるのが魅力です。RAGを構築する際にも、文書を構造化しつつテキスト化できるので精度向上に使えますね。
モデルが複数あり、用途に応じて使い分ける必要があります。
今回試しているのはテキスト化 + 構造化をしてくれる レイアウトモデル というモデルです。

料金

料金は1000ページで1500円程度なので、1ページ1.5円程度です。
無料版だと1回の読み取りで2ページまでで、月500ページまでは無料で使えます。
今回は無料版を使用しています。

前提条件

AzureでAzureAIDocumentIntelligenceのリソースをデプロイし、
・エンドポイント
・APIキー
を.envファイルに記述しておく。(作成手順は割愛)

環境

言語:Python (v3.12.0)
SDK:azure-ai-documentintelligence (v1.0.0b3)
   azure-core (v1.30.2)
リージョン:米国西部

サンプルPDF

車のマニュアルの目次1ページ分だけのPDFを作成し、sample.pdf という名前で保存しています。

サンプルコード(テキスト化のみ)

シンプルにテキスト化した結果だけを出力するコードです。

import os

from azure.core.credentials import AzureKeyCredential
from azure.ai.documentintelligence import DocumentIntelligenceClient
from azure.ai.documentintelligence.models import AnalyzeResult
from azure.ai.documentintelligence.models import AnalyzeDocumentRequest
from dotenv import load_dotenv

def main():
    result = analyze()
    print(result.content)

def analyze():

    # 環境変数読み込み
    load_dotenv(verbose=True)
    endpoint = os.getenv("ENDPOINT")
    key = os.getenv("KEY")

    document_intelligence_client = DocumentIntelligenceClient(
        endpoint=endpoint, credential=AzureKeyCredential(key)
    )

    with open("sample.pdf", mode="rb") as f:
        poller = document_intelligence_client.begin_analyze_document(
            "prebuilt-layout",
            analyze_request=f,
            content_type="application/octet-stream",
        )

    result: AnalyzeResult = poller.result()

    return result


if __name__ == "__main__":
    main()

上記のコードを実行すると、以下の結果が出力されました。
問題なく日本語化されていますね。

2
目次
知っておいていただきたいこと 6 .......
本書の見方
10
検索のしかた
11
イラスト目次
12
1
安全·安心のために
1-1.安全にお使いいただくために
運転する前に
26
安全なドライブのために
27
シートベルト
29
SRS エアバッグ
34
排気ガスに対する注意
40
1-2. お子さまの安全
お子さまを乗せるときは
41
チャイルドシート
42
1-3. ハイブリッドシステム
ハイブリッドシステムの特徴
58
ハイブリッドシステムの注意
62
1-4. 盗難防止装置
イモビライザーシステム
67
オートアラーム
68
2 走行に関する情報表示
2-1. 計器の見方
警告灯/表示灯
72
計器類(4.2インチディスプレイ)76
計器類(7インチディスプレイ) .. 80
マルチインフォメーションディスプレ
イ
85
ヘッドアップディスプレイ
93
エネルギーモニター/燃費画面 .... 97
3 運転する前に
3-1. キー
キー 102 デジタルキー 105
3-2. ドアの開閉、ロックのしかた :unselected:
フロントドア
107
スライドドア
110
バックドア
123
スマートエントリー&スタートシステ
ム
135
3-3. シートの調整
フロントシート
140
セカンドシート
141
サードシート
146
ヘッドレスト
147
シートアレンジ
150
3-4. ハンドル位置·ミラー
ハンドル
155
インナーミラー
156
デジタルインナーミラー
157
ドアミラー
166
補助確認装置
167
3-5. ドアガラスの開閉
パワーウインドウ
169
3-6. お好み設定
マイセッティング
172
4 運転
4-1. 運転にあたって
運転にあたって
175
荷物を積むときの注意
182
4-2. 運転のしかた
パワー(イグニッション)スイッチ
183
EV ドライブモード
187
トランスミッション(アドバンスト
パーク非装着車)
189
トランスミッション(アドバンスト
パーク装着車)
191
方向指示レバー
195
パーキングブレーキ
196
ブレーキホールド
199

サンプルコード(テキスト化 + 構造化)

テキスト化だけでなく、sectionsとparagraphsの情報を組み合わせて構造化された形で出力するサンプルコードです。
下記の記事を参考にさせていただきました。
参考) https://qiita.com/nohanaga/items/1263f4a6bc909b6524c8

import os
import re

from azure.core.credentials import AzureKeyCredential
from azure.ai.documentintelligence import DocumentIntelligenceClient
from azure.ai.documentintelligence.models import AnalyzeResult
from dotenv import load_dotenv


def main():
    result = analyze()
    sections = get_sections(result)
    paragraphs = result.paragraphs
    print_hierarchy(sections, paragraphs)


def explore_sections(input_data, paragraphs, indices, depth=0):
    indent = "   " * depth
    for idx in indices:
        if idx < len(input_data):
            for path in input_data[idx]:

                if "paragraphs" in path:
                    para_idx = get_last_number_from_string(path)
                    content = paragraphs[para_idx].content
                    print(indent + f"{idx}: {path}: {content}")
                elif "sections" in path:
                    print(indent + f"{idx}: {path}")

                if "sections" in path:
                    number = int(path.split("/")[-1])
                    # 再帰的にさらにそのセクションを探索
                    explore_sections(input_data, paragraphs, [number], depth + 2)


def print_hierarchy(input_data, result):
    initial_indices = [0]
    explore_sections(input_data, result, initial_indices)


def get_last_number_from_string(s):
    match = re.search(r"/(\d+)$", s)
    if match:
        return int(match.group(1))
    else:
        return None


def get_sections(result):
    sections = []
    for section in result.sections:
        sections.append(section.elements)
    return sections


def analyze():

    # 環境変数読み込み
    load_dotenv(verbose=True)
    endpoint = os.getenv("ENDPOINT")
    key = os.getenv("KEY")

    document_intelligence_client = DocumentIntelligenceClient(
        endpoint=endpoint, credential=AzureKeyCredential(key)
    )

    with open("sample.pdf", mode="rb") as f:
        poller = document_intelligence_client.begin_analyze_document(
            "prebuilt-layout",
            analyze_request=f,
            content_type="application/octet-stream",
        )

    result: AnalyzeResult = poller.result()

    return result


if __name__ == "__main__":
    main()

上記のコードを実行すると、下記の結果が出力されました。


0: /sections/1
      1: /paragraphs/1: 目次
      1: /paragraphs/10: 1
      1: /paragraphs/11: 安全·安心のために
0: /sections/2
      2: /paragraphs/12: 1-1.安全にお使いいただくために
      2: /paragraphs/13: 運転する前に
      2: /paragraphs/14: 26
      2: /paragraphs/15: 安全なドライブのために
      2: /paragraphs/16: 27
      2: /paragraphs/17: シートベルト
      2: /paragraphs/18: 29
      2: /paragraphs/19: SRS エアバッグ
      2: /paragraphs/20: 34
      2: /paragraphs/21: 排気ガスに対する注意
      2: /paragraphs/22: 40
0: /sections/3
      3: /paragraphs/23: 1-2. お子さまの安全
      3: /paragraphs/24: お子さまを乗せるときは
      3: /paragraphs/25: 41
      3: /paragraphs/26: チャイルドシート
      3: /paragraphs/27: 42
      3: /sections/4
            4: /paragraphs/28: 1-3. ハイブリッドシステム
            4: /paragraphs/29: ハイブリッドシステムの特徴
            4: /paragraphs/30: 58
            4: /paragraphs/31: ハイブリッドシステムの注意
            4: /paragraphs/32: 62
0: /sections/5
      5: /paragraphs/33: 1-4. 盗難防止装置
      5: /paragraphs/34: イモビライザーシステム
      5: /paragraphs/35: 67
      5: /paragraphs/36: オートアラーム
      5: /paragraphs/37: 68
0: /sections/6
      6: /paragraphs/38: 2 走行に関する情報表示
      6: /sections/7
            7: /paragraphs/39: 2-1. 計器の見方
            7: /paragraphs/40: 警告灯/表示灯
            7: /paragraphs/41: 72
            7: /paragraphs/42: 計器類(4.2インチディスプレイ)76
            7: /paragraphs/43: 計器類(7インチディスプレイ) .. 80
            7: /paragraphs/44: マルチインフォメーションディスプレ
            7: /paragraphs/45: イ
            7: /paragraphs/46: 85
            7: /paragraphs/47: ヘッドアップディスプレイ
            7: /paragraphs/48: 93
            7: /paragraphs/49: エネルギーモニター/燃費画面 .... 97
0: /sections/8
      8: /paragraphs/50: 3 運転する前に
      8: /sections/9
            9: /paragraphs/51: 3-1. キー
            9: /paragraphs/52: キー 102 デジタルキー 105
            9: /paragraphs/53: 3-2. ドアの開閉、ロックのしかた
            9: /paragraphs/54: :unselected:
            9: /paragraphs/55: フロントドア
            9: /paragraphs/56: 107
            9: /paragraphs/57: スライドドア
            9: /paragraphs/58: 110
            9: /paragraphs/59: バックドア
            9: /paragraphs/60: 123
            9: /paragraphs/61: スマートエントリー&スタートシステ
            9: /paragraphs/62: ム
            9: /paragraphs/63: 135
      8: /sections/10
            10: /paragraphs/64: 3-3. シートの調整
            10: /paragraphs/65: フロントシート
            10: /paragraphs/66: 140
            10: /paragraphs/67: セカンドシート
            10: /paragraphs/68: 141
            10: /paragraphs/69: サードシート
            10: /paragraphs/70: 146
            10: /paragraphs/71: ヘッドレスト
            10: /paragraphs/72: 147
            10: /paragraphs/73: シートアレンジ
            10: /paragraphs/74: 150
            10: /paragraphs/75: 3-4. ハンドル位置·ミラー
            10: /paragraphs/76: ハンドル
            10: /paragraphs/77: 155
            10: /paragraphs/78: インナーミラー
            10: /paragraphs/79: 156
            10: /paragraphs/80: デジタルインナーミラー
            10: /paragraphs/81: 157
            10: /paragraphs/82: ドアミラー
            10: /paragraphs/83: 166
            10: /paragraphs/84: 補助確認装置
            10: /paragraphs/85: 167
      8: /sections/11
            11: /paragraphs/86: 3-5. ドアガラスの開閉
            11: /paragraphs/87: パワーウインドウ
            11: /paragraphs/88: 169
            11: /paragraphs/89: 3-6. お好み設定
            11: /paragraphs/90: マイセッティング
            11: /paragraphs/91: 172
0: /sections/12
      12: /paragraphs/92: 4 運転
      12: /sections/13
            13: /paragraphs/93: 4-1. 運転にあたって
            13: /paragraphs/94: 運転にあたって
            13: /paragraphs/95: 175
            13: /paragraphs/96: 荷物を積むときの注意
            13: /paragraphs/97: 182
      12: /sections/14
            14: /paragraphs/98: 4-2. 運転のしかた
            14: /paragraphs/99: パワー(イグニッション)スイッチ
            14: /paragraphs/100: 183
            14: /paragraphs/101: EV ドライブモード
            14: /paragraphs/102: 187
            14: /paragraphs/103: トランスミッション(アドバンスト
            14: /paragraphs/104: パーク非装着車)
            14: /paragraphs/105: 189
            14: /paragraphs/106: トランスミッション(アドバンスト
            14: /paragraphs/107: パーク装着車)
            14: /paragraphs/108: 191
            14: /paragraphs/109: 方向指示レバー
            14: /paragraphs/110: 195
            14: /paragraphs/111: パーキングブレーキ
            14: /paragraphs/112: 196
            14: /paragraphs/113: ブレーキホールド
            14: /paragraphs/114: 199

構造化はされているのですが、精度は今のところイマイチかも、、、
下図は、赤枠が親セクション(0:のセクション)、青枠が子セクションです。

正しく構造化されている箇所もありますが、1-2.と1-3.の箇所や、3-3.と3-4.の箇所など本来は別セクションとして認識してほしい箇所も一緒になってしまったり、、
構造化精度に関しては今後に期待したいです。

コメント

タイトルとURLをコピーしました