1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

YomiTokuの解析結果をOpenAI APIで検証する

Posted at

YomiTokuは日本語に特化して学習したAI OCRです。Pythonパッケージとして公開されており、インストールすればCLIで簡単にOCR解析が可能です。APIもありますので、Pythonからの操作も可能です。

YomiTokuのOCR精度は十分に高いですが、自動化する際には異なるOCR処理と組み合わせることで、検証が行えます。今回はOpenAIのAPIを使って、YomiTokuのOCR結果を検証する流れを解説します。

今回検証する帳票

今回は、以下のような帳票を対象としています。

template_02.png

YomiTokuのインストール

YomiTokuのインストールは、 pip コマンドで行います。

pip install yomitoku

APIを利用する

今回は自動処理なので、YomiTokuのAPIを利用します。CLIも提供しているので、解析結果だけが欲しい場合には、そちらを利用してください。

yomitoku -o /path/to/results -f md /path/to/dir

インポートする

まずは、YomiTokuをインポートします。

from yomitoku import DocumentAnalyzer
from yomitoku.data.functions import load_pdf

DocumentAnalyzerを初期化する

DocumentAnalyzerを初期化します。このとき、GPUを使う場合は device="cuda" を指定します。

analyzer = DocumentAnalyzer(visualize=True, device="cuda")

ドキュメントを読み込む

ドキュメントを読み込みます。PDFファイルを読み込む場合は、 load_pdf を使います。

imgs = load_pdf("/path/to/invoice.pdf")

今回利用したドキュメントは1ページしかないので、 imgs には1つの要素が入ります。複数ある場合にはループ処理を行ってください。今回は1ページ目を取り出しています。

img = imgs[0]

OCRを実行する

OCR処理を行う analyzer は非同期処理なので、 await を使います。

results, ocr_vis, layout_vis = await analyzer.run(img=img)

結果を出力する

解析結果を取得します。

yomitoku_results = results.to_json("", img=img)

この時、 json にOCR処理の結果が入ります。以下はその一部です。たとえば検証として、請求金額の合計を使います。明細やフッターなども取得できていますが、今回は省略しています。

{
    :,
    "paragraphs": [
        :
        {
            "box": [
                449,
                500,
                707,
                542
            ],
            "contents": "¥2,855,600-",
            "direction": "horizontal",
            "order": 5,
            "role": null
        },
        :
    ],
    :
}

OpenAIのAPIを利用する

次に、OpenAIのAPIを利用して処理をします。OpenAIのAPIは、APIキーを取得して利用します。APIキーは、OpenAIのサイトから取得できます。なお、OpenAIでは画像ファイルのみ対象なので、PDFは画像(PNGなど)にしてから処理します。

import argparse
import os
import sys
import json
import openai
import base64
from typing import Optional, Dict, Any

def main():
    """メイン関数"""
    
    # OpenAI APIクライアントの初期化
    client = initialize_openai_client(os.environ.get("OPENAI_API_KEY"))
    
    # ファイルの内容に対して、解析を行う
    openai_results = analyse_file(client, "/path/to/invoice.png")
    
    if openai_results:
        print("\n=== ファイル解析結果 ===")
        print(json.dumps(openai_results, indent=2, ensure_ascii=False))
        print("=== 解析完了 ===")
    else:
        print("ファイルの解析に失敗しました。")

if __name__ == "__main__":
    main()

OpenAIのクライアントを初期化する関数です。キーは環境変数で設定しておくと便利です。

def initialize_openai_client(api_key: str) -> openai.OpenAI:
    """OpenAI APIクライアントを初期化する関数"""
    return openai.OpenAI(api_key=api_key)

analyse_file 関数は、ファイルを読み込んでOpenAIに送信し、結果を取得する関数です。

def analyse_file(client: openai.OpenAI, file_path: str) -> Dict[str, Any]:
    """OpenAIを使って、ファイルを解析する
    
    Args:
        client: OpenAI APIクライアント
        file_path: 解析対象のファイルパス
        
    Returns:
        Dict[str, Any]: ヘッダー、明細、フッターを含むJSON形式のデータ
    """
    try:
        # ファイルが存在するか確認
        if not os.path.exists(file_path):
            print(f"エラー: ファイル '{file_path}' が見つかりません。", file=sys.stderr)
            return None
        
        # ファイル名から拡張子を取得
        _, file_extension = os.path.splitext(file_path)
        file_extension = file_extension.lower()
        
        # ファイル解析のためのプロンプト
        prompt = """
        このファイルを解析して、以下の3つの部分に分けてください:
        1. ヘッダー部分
        2. 明細部分
        3. フッター部分
        
        それぞれの部分について、構造と内容を詳細に説明してください。
        結果はJSON形式で返してください。
        """
        
        # ファイルの種類に応じた処理
        image_content = None
        
        if file_extension in ['.jpg', '.jpeg', '.png']:
            # 画像ファイルを直接読み込む
            with open(file_path, 'rb') as file:
                image_content = file.read()
                mime_type = f"image/{file_extension[1:]}"
        else:
            print(f"サポートされていないファイル形式です: {file_extension}", file=sys.stderr)
            return None
        
        if not image_content:
            print("画像データの取得に失敗しました。", file=sys.stderr)
            return None
        
        # OpenAI APIを使用してファイルを解析
        print("OpenAI APIを使用してファイルを解析しています...")
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {
                    "role": "system",
                    "content": "あなたはファイル解析の専門家です。アップロードされたファイルを解析し、ヘッダー、明細、フッターに分けて情報を抽出してください。"
                },
                {
                    "role": "user",
                    "content": [
                        {"type": "text", "text": prompt},
                        {
                            "type": "image_url",
                            "image_url": {
                                "url": f"data:{mime_type};base64,{base64.b64encode(image_content).decode('utf-8')}"
                            }
                        }
                    ]
                }
            ],
            response_format={"type": "json_object"}
        )
        
        # 解析結果を取得
        raw_content = response.choices[0].message.content
        
        try:
            analysis_result = json.loads(raw_content)
            
            # 結果を整形
            result = {
                "header": analysis_result.get("ヘッダー部分", analysis_result.get("header", {})),
                "details": analysis_result.get("明細部分", analysis_result.get("details", {})),
                "footer": analysis_result.get("フッター部分", analysis_result.get("footer", {}))
            }
            
            # 結果が空の場合は、元のレスポンスをそのまま返す
            if all(not v for v in result.values()):
                print("解析結果が空のため、元のレスポンスを使用します。")
                openai_results = analysis_result
        except json.JSONDecodeError as e:
            print(f"JSONの解析に失敗しました: {e}")
            return None
        
        return openai_results
        
    except Exception as e:
        print(f"エラー: ファイルの解析中に問題が発生しました: {e}", file=sys.stderr)
        return None

結果として、以下のようなJSON形式のデータが得られます。なお、この結果を確実に受け取るには、プロンプトにて明示的に指定する必要があります。

{
  "header": {
    "title": "請求書",
    "recipient_info": {
      "name": "合名会社小田部石材商会 東京支社 御中",
      "address": "〒919-5359 福井県南条群日野2-4-4",
      "department": "営業部",
      "contact_person": "担当者:北岡 崇典 様"
    },
    "issuer_info": {
      "name": "株式会社ナカヤマ",
      "address": {
        "postal_code": "〒456-0029",
        "prefecture_area": "愛知県日井市胡瓜町",
        "office": "4-1-10 東城ビレ1F"
      },
      "phone": [
        "0531-50-0067",
        "0531-50-0058"
      ],
      "email": "sato_415@example.org"
    },
    "invoice_info": {
      "number": "123456-123",
      "date": "令和13年12月21日"
    },
    "seal": "株式会社ナカヤマ"
  },
  "details": {
    "request_statement": "下記の通りご請求申し上げます。",
    "total_amount_due": "¥2,855,600",
    "items": [
      {
        "no": 1,
        "name": "ノートパソコン",
        "quantity": "5台",
        "unit_price": "120,000",
        "amount": "600,000"
      },
      :
      {
        "no": 9,
        "name": "モニター",
        "quantity": "4台",
        "unit_price": "30,000",
        "amount": "120,000"
      }
    ],
    "sub_total": "¥2,596,000",
    "tax": {
      "rate": "10%",
      "amount": "¥259,600"
    },
    "grand_total": "¥2,855,600"
  },
  "footer": {
    "notes": "備考欄:",
    "bank_info": {
      "bank_name": "あおぞら銀行",
      "branch": "夕立支店",
      "account_type": "普通口座",
      "account_number": "No 0123456",
      "account_holder": "カ)ナカヤマ"
    },
    "payment_due_date": "お支払い期限:令和13年12月31日",
    "payment_note": "(※お振込み手数料は御社ご負担にてお願い致します)"
  }
}

この結果から、 results["details"]["grand_total"] に請求金額が入っています。これを使って、YomiTokuの結果と比較します。

# 正規表現で、数値でないものは除去
yomitoku_total = re.sub(r"[^0-9]", "", yomitoku_results["paragraphs"][5]["contents"]) # 2855600
openai_total = re.sub(r"[^0-9]", "", openai_results["details"]["grand_total"]) # 2855600
assert int(yomitoku_total) == int(openai_total)

このようにして、YomiTokuの結果とOpenAI APIによる結果を検証できます。

検証のメリット・デメリット

OpenAIのAPIは、有料です。また、ネットワークを使うので、結果が返ってくるのに時間を要する場合があります。そのため、常時検証するのではなく、定期的にテストするなどでも十分でしょう。

YomiTokuは商用では有償ですが、実行回数などによる課金は発生しません。また、データを自社内に留めて利用できますので、セキュリティ的にも安心して利用できます。さらに、同様の帳票であれば同じように結果が得られるので、システム自動化しやすいのがメリットです。

こうした複数のAIによる検証により、信頼性や精度の向上を図れるでしょう。AIによる業務フロー自動化を行う際には、こうした検証・テスト工程をフローに含めることをお勧めします。

ライセンス

YomiTokuのライセンスはコモンズ証 - 表示 - 非営利 - 継承 4.0 国際 - Creative Commonsです。非商用での個人利用、研究目的においては、ご自由に利用できます。商用目的での利用に関しては、商用ライセンスが必要です。

まとめ

YomiTokuの精度を検証する際には、他のOCR処理が可能なAIと比較してみると良いでしょう。OpenAI APIは有名なLLMであり、検証に用いるのにちょうど良いかと思います。

YomiTokuは日本語に特化しており、日本語のOCR処理には最適です。また、Pythonパッケージとして提供されているため、簡単に利用できます。ぜひ、AI OCRを使って業務効率化を実現してください。

kotaro-kinoshita/yomitoku: Yomitoku is an AI-powered document image analysis package designed specifically for the Japanese language.

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?