はじめに
この記事では、Azure AI Document Intelligence(以降、Azure DI)を活用したOCRの精度検証結果を共有します。当社で実施したPoCで、賃貸契約書と支払明細書を対象に、どこまで正確に情報を抽出できるのか実際に試してみました。Azure DIによるOCRとAzure OpenAIによる補正を組み合わせることで、どの程度の精度が得られるのか、また、どのような課題が残るのかを具体的に検証していきます。
※ OCR(Optical Character Recognition)は、画像やスキャン文書から文字を認識してテキスト化する技術です。
今回実現したいこと
当社では、賃貸契約書や支払明細書などのビジネス文書から情報を抽出する作業の自動化に関する課題がありました。現在は人手による目視確認で文書のチェックを行っており、業務負担が大きくなっています。これは単なる作業量の問題だけでなく、文書自体が煩雑であることも負担の要因となっています。これらの文書はフォーマットが統一されておらず、電子媒体、紙媒体(スキャンデータ、写真撮影)など形式が多岐にわたるためです。
そこで、OCR技術を活用し、文書内の情報をテキスト化できないか検討しました。
要件
対象文書の種類と特性
- 対象は賃貸契約書および支払明細書の2種類。
- 賃貸契約書:契約者情報・物件情報・契約期間・賃料・敷金・礼金などの項目が含まれる。
- 支払明細書:支払日・支払先・金額・振込先などの項目が含まれる。
- 文書形式は多様。
- フォーマットは非定型で、帳票ごとにレイアウトが異なる。
業務要件・運用面
- 処理対象文書は約3000件規模を想定。
- 抽出結果の人手による確認・修正を最小限に抑えることが理想。
OCRの課題
OCRには以下の課題が挙げられます。
精度の限界
- 特定のフォントでのみ高精度。
- スキャン品質や照明条件によって精度が大幅に低下。
- 個人の筆跡差により手書き文字の認識率が低下。
ファイルの前処理が煩雑
- 傾き補正・ノイズ除去・コントラスト調整などの画像補正が必要。
- 複数カラムや表形式データのレイアウト解析が必要。
- 文字の境界検出と分離(セグメンテーション)が必要。
Azure DIですべて解決できるのでは
Azure DIは、マイクロソフトが提供するクラウド型OCRサービスで、請求書・契約書などのビジネス文書処理分野で広く利用されています。文書公式ドキュメントや第三者の比較記事でも高い認知度と評価を得ており、現時点で業界標準の一つとされています。Azure DIにはファイルの前処理などをAIで適切に処理する機能が備わっており、上記OCRの課題をある程度解決していると考えました。また、APIキーとエンドポイントを取得することですぐに利用できることから、PoCで実現するにあたり適切だと考え、OCR基盤としてAzure DIを選定しました。
Azure DIには、以下の特徴が挙げられます。
- AIによる文書画像の読み取り、構造理解、分類、抽出が各フェーズで活用され高性能。
- 複数の事前構築済みモデル(Prebuilt Models)が提供されており、分析対象の文書の種類に応じて使い分けが可能。
- Azure Portalでリソースを作成し、APIキーとエンドポイントを取得することで手軽に利用可能。
実装概要
内容
賃貸契約書およびそれに伴う支払明細書のPDFファイルから必要事項をテキストで抽出するプログラムをPythonで実現します。プログラムは賃貸契約書用と支払明細書用の2種類を用意しました。
抽出項目
賃貸契約書
- 契約書種類(ローン/賃貸契約)
- 家賃
- その他費用(共益費など)
- 毎月の支払額
- 契約開始日
- 契約終了日
- 支払先名
- 支払先が個人であるかどうか
支払明細書
- 支払先名
- 振込元個人名(上記契約書の契約が本人ではない場合のみ抽出。契約者本人への送金が支払明細となるため)
- 振込先個人名(上記契約書の契約が本人ではない場合のみ抽出。契約者本人への送金が支払明細となるため)
- 直近4か月の家賃支払履歴_1か月前
- 直近4か月の家賃支払履歴_2か月前
- 直近4か月の家賃支払履歴_3か月前
- 直近4か月の家賃支払履歴_4か月前
アプローチ
PoCレベルでの検証であり、実装期間やリソースの観点から複雑なチューニングは行わず、まずは既存サービスの活用で手軽に試してみることにしました。そこで、Azure DIにあらかじめ用意されている事前構築済みモデルを使用して調整していくことにしました。
事前構築済みモデルの落とし穴
事前構築済みモデルだけではOCR性能が不十分
Azure DIの事前構築済みモデルは、請求書、領収書、名刺など、よく使われる文書パターンごとにAIが学習・最適化されているため、対象となる文書から日付、金額、会社名などを自動抽出できます。しかし、実際に試してみると、今回のような支払明細書や独自フォーマットの文書では、抽出したい情報がうまく取り出せないケースがありました。支払明細書は明細ごとに独自のレイアウトや表現が多く、事前構築済みモデルでは対応しきれないことが分かりました。
事前構築済みモデルはOCRのベースとなるreadモデルに加え、請求書であれば金額や日付を抜き出してJSON形式で返却するといった追加のチューニングが施されています。一般的には、このチューニングによって精度が向上するとされています。しかし今回の場合、抽出したい情報が抜け落ちてしまうケースが判明しました。原因は主に2つあると考えており、1つ目は支払明細書のフォーマットが多岐にわたるため、すべてに対応できる事前構築済みモデルが存在しないこと、2つ目は支払明細書から複数の支払情報を抽出する必要があったため、JSON形式で構造化して返却する際に、情報がばらけていたり抜け落ちたりしたことでした。
readモデルとLLMによる独自チューニング
この課題に対し、readモデルとLLMを組み合わせるアプローチを試してみることにしました。事前構築済みモデルのOCRのベースとなっているreadモデルは他の事前構築済みモデルとは異なり、特定の文書用にチューニング等は施されていませんが、文書内の文字をすべてテキスト化して返します。そこで一旦文書内のすべてのテキストを取得し、そこから先の必要となる情報抽出をLLMに任せることで精度向上を目指しました。具体的には、readモデルでOCR処理を実施して取得したテキストと、必要となる情報の抽出要件の2つをLLMのプロンプトに与えて処理させます。
OCR処理されたテキストから情報を抽出する方法として正規表現も検討しましたが、実際に試してみると適していないことが分かりました。賃貸契約書や支払明細書には大量のテキストが含まれており、そこから正規表現だけで必要情報を抽出するのは現実的に困難だったためです。テキストをLLMのプロンプトに渡すことで、必要な情報の抽出や整形を行うと同時に、OCR処理時の誤認文字の補間も期待できると考えました。例えば英字「o」と数字「0」の誤認識などです。
利用したAPI
Azure AI Document Intelligence
- モデル:
prebuilt-read(事前構築済みの一般的な文書の読み取りモデル) - バージョン:
Document Intelligence REST API v4.0(2025/10/27時点の最新)
Azure OpenAI
- モデル:
GPT-5
Azure DIで使用できるモデルや選定方法について
以下の公式ページに、Azure DIの概要やモデル選定のフローがまとめられています。
- Azure AI ドキュメント インテリジェンスとは - Azure AI services | Microsoft Learn ⧉
- アプリケーションとワークフローに最適な Document Intelligence モデルを選択します。 - Azure AI services | Microsoft Learn ⧉
事前構築済みモデル(Prebuilt Models)
以下は代表的なモデルの一部です。
prebuilt-read:一般的な文書の読み取り(この記事の実装で使用)prebuilt-invoice:請求書の構造化抽出prebuilt-receipt:レシート・領収書の詳細解析prebuilt-businessCard:名刺情報の構造化prebuilt-idDocument:身分証明書の情報抽出
カスタムモデル構築機能
特定の業界や企業固有の文書形式に合わせたカスタムモデルの構築が可能です。学習データを用意して再トレーニングすることで、独自プロダクトに合わせてモデル精度を高められます。Document Intelligence Studioでは正確性や信頼度のスコア算出もでき、独自に学習したモデルの評価が可能です。
詳細
実装内容
今回の検証では、賃貸契約書用と支払明細書用のどちらも、次の流れでOCR処理を実装してみました。この中でOCRの精度に関わる箇所は2から4であるため、その他の箇所の説明は省略します。
- 読み取り対象のPDFを画像に変換(PyMuPDF)
- 画像からテキスト抽出(Azure AI Document Intelligence)
- テキスト整形(Azure OpenAI)
- 情報抽出(Azure OpenAI)
- JSON形式での結果出力
2. 画像からテキスト抽出
def extract_text_with_azure_ocr(image, client): """Azure AI Document Intelligenceを使用して画像からテキストを抽出する""" # PIL ImageをPNG形式でバイトストリームに変換 buffer = io.BytesIO() image.save(buffer, format="PNG") buffer.seek(0)
# Azure AI Document Intelligence APIを呼び出し poller = client.begin_analyze_document( "prebuilt-read", # 事前構築済みモデル document=buffer )
# OCR処理完了を待機 result = poller.result()
# すべての行を結合してテキストを返す return "\n".join([ line.content for page in result.pages for line in page.lines ])上記のとおり、Azure DIの事前構築済みモデルを呼び出しています。今回の検証では試していませんが、要件に合わせて他モデルへの切り替えや、カスタムモデルの設定も可能です。
# Azure AI Document Intelligence APIを呼び出しpoller = client.begin_analyze_document( "prebuilt-invoice", # 別の事前構築済みモデル document=buffer)3. テキスト整形
今回の検証で特に注力したのは、この後の処理過程です。OCRで抽出されたテキストには、誤認識や抽出漏れが発生する可能性があります。Azure DIのreadモデルは、読み取り対象のファイルからすべての文字を抽出するため、プロダクト要件に応じた適切な情報抽出と整形が必要になります。そこで、抽出後の文章をAzure OpenAI(GPT-5)に読み取らせてみることにしました。抽出後のテキストを一度LLMに通すことで、欠落文字の補間や不自然な日本語の修正ができるのではないかと考えました。
また、プロンプトに一度に大量の指示を与えると、LLMが指示内容を正確に読み取れない場合があります。これはLLMの一般的な特性ですが、今回はある程度正確に指示を理解してもらう必要があるため、指示内容を分割し、複数回に分けてプロンプトを渡すことにしました。実際に試してみると、処理時間はかかりますが、より忠実に従うようになりました。
1回目では、抽出したテキスト内の年度や金額の表示方法のばらつきを修正するよう指示しています。
プロンプト設計の工夫
検証を進める中で、以下のようなプロンプト設計の工夫を試しました。
- 役割ごとにプロンプトを分割する(抽出・校正・構造化など)。
- 正確な指示と具体例を提示する(過不足のない指示)。
- 抽出対象の定義を明確化し、識別ルール(キーワード・文脈)をプロンプトに組み込む。
- OCRで抽出したテキストと整形後のテキストを比較し、差分に基づいてプロンプトを継続的に調整する。
- 想定される読み取り対象フォーマットに合わせた調整をする。
4. 情報抽出
次に2回目のプロンプトで、プロダクトで必要な情報を抽出し、JSON形式で返却します。以下は、賃貸契約書用の検証コードです。
def extract_invoice_fields_from_json_output(refined_text, deployment_id): """整形済みテキストから契約書情報をJSON形式で抽出""" if not refined_text: return {}
# Azure OpenAI クライアント初期化 client = AzureOpenAI( azure_endpoint=AZURE_OPENAI_ENDPOINT, api_key=AZURE_OPENAI_API_KEY, api_version=AZURE_OPENAI_API_VERSION )
# システムプロンプト(抽出ルール) system_prompt = textwrap.dedent("""\ 以下の契約書記載内容から、契約書の種類を判定し、 適切な項目を抽出してください。
【契約書種類の判定】 文書内に「ローン」「融資」「借入」「金銭消費貸借」などの 文言が含まれている場合は「ローン契約書」として処理してください。 それ以外の場合は「その他契約書」として処理してください。
【共通抽出項目】 - 名前:借主、賃借人、契約者の氏名 - 連帯債務者:連帯債務者、連帯保証人(配列形式) - 連帯契約者:その他契約書の場合の追加契約者(配列形式) - 支払額:家賃 + その他費用の合計 - 契約期間:契約開始日から終了日までの期間 - 契約開始日:ISO形式(YYYY-MM-DD) - 契約終了日:ISO形式(YYYY-MM-DD) - 支払先:不動産会社名や管理会社名 - 支払先が個人であるかどうか
出力形式は以下のようなJSON形式でお願いします:
{ "契約書種類": "ローン契約書", "名前": "...", "連帯債務者": ["氏名1", "氏名2"], "家賃": "...", "その他費用": "...", "支払額": "...", "契約期間": "...", "契約開始日": "YYYY-MM-DD", "契約終了日": "YYYY-MM-DD", "支払先": "...", "支払先が個人であるかどうか": "..." } """)
user_prompt = f"契約書テキスト:\n{refined_text}\n\n上記の形式でJSONを出力してください。"
# GPTに送信 response = client.chat.completions.create( model=deployment_id, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], max_tokens=4096, temperature=0.2 )
content = response.choices[0].message.content.strip()
# JSON部分のみを正規表現で抽出 match = re.search(r'\{.*\}', content, re.DOTALL) if match: json_str = match.group(0) invoice_data = json.loads(json_str) return invoice_data else: return {}処理の流れは次のとおりです。
- 整形済みテキストをAzure OpenAIに送信する。
- プロンプトで抽出項目を詳細に指定する。
- JSON形式で構造化データを取得する。
OCRの精度について
実際に作成したOCRプログラムについて、以下の観点で評価しました。結論として、賃貸契約書はほぼ100%の正答率でした。一方、支払明細書は約85%の正答率で、課題が残る結果となりました。文書から読み取りたい項目ごとに正答率を算出し、精度を評価しました。
テストデータ内容
賃貸契約書および支払明細書のサンプル(拡張子はすべてPDF)を使用しました。各文書は、電子ファイル、紙媒体のスキャン、紙媒体の写真の3種類の形式が存在し、各種OCR処理の精度が異なります。同一形式でも鮮明さに違いがあるため、すべてのテストデータを読み取り難易度別にレベル分けしました。
難易度順
- 電子ファイル
- スキャン
- 写真
- スキャン(不鮮明)
- 写真(不鮮明)
※電子ファイルが最も処理が容易、写真(不鮮明)が最難関
用語の定義:
- 電子ファイル:電子明細の文書
- スキャン:紙の明細書をスキャナーで取り込んだ画像
- 写真:スマートフォンなどで紙の明細書を撮影した画像
末尾の(不鮮明)は鮮明さの観点で分類したものです。
- 光の当たり方が均一かどうか(暗い・明るい・影があるなど)
- スキャンや写真の鮮明さやピントの合い具合
- 紙の汚れやシワ、印刷のかすれなどの有無
上記に当てはまる場合は(不鮮明)ラベルを付けました。
今回テストで使用したデータの分布は以下のとおりです。
| ファイル形式 | 賃貸契約書(19件) | 支払明細書(19件) |
|---|---|---|
| 電子ファイル | 4 | 10 |
| スキャン | 8 | 4 |
| 写真 | 5 | 1 |
| スキャン(不鮮明) | 2 | 2 |
| 写真(不鮮明) | 0 | 2 |
正答率
賃貸契約書

項目別のNG件数と正答率(全19ファイル・141項目)
全体正答率:99.29%
NGだった1ケースについて
1件だけ抽出ミスがあり、「支払先」の項目です。データの読み取り難易度レベルはスキャン(不鮮明)であり、該当箇所の文字が薄く読みづらい状態でした。プロンプトを確認したところ、Azure DIでOCRする段階で抽出できていませんでした。
支払明細書

項目別のNG件数と正答率(全19ファイル・133項目)
全体正答率:85.71%
項目別NG件数
| 項目 | NG件数 |
|---|---|
| 振込元個人名 | 0 |
| 振込先個人名 | 0 |
| 5月金額 | 5 |
| 6月金額 | 3 |
| 7月金額 | 2 |
| 8月金額 | 3 |
| 支払先 | 6 |
NG原因の種類と書類の読み取り難易度レベルの関係

NG原因の種類の説明
- OCR:OCRの段階でテキスト抽出不可
- プロンプト:プロンプトでの抽出ミス
- マスキング:読み取り対象の書類に不適切なマスキングが施されており適切な読み取り不可
- 未記載:読み取り対象の書類に必要情報が未記載のため、AIが別のテキストを誤抽出
表の見方:電子ファイルの場合、全体でNGが10件あり、NGの種類はプロンプトが8件、未記載が2件です。また、縦の合計は読み取り難易度ごとの件数、横の合計はNG原因ごとの件数です。
表1の概要
- NG19件中13件はプロンプトによる抽出ミスでした。
- OCRでテキスト抽出できていない件数は1件で、データの読み取り難易度レベルは写真(不鮮明)であるため、OCR自体はおおむね問題ありません。
- マスキング、未記載はOCR技術の問題ではなく、読み取り対象側に起因するものです。
NG原因の種類分布
NGは主に金額の項目で発生しており、原因はプロンプトでの抽出ミスが大部分を占めます。
項目ごとのNG件数(NG原因の種類ごと)

考察
賃貸契約書および支払明細書ごとに正答率を算出した結果、全体を通してOCR段階でテキストを正確に抽出できていないパターンは2件のみでした。適切に結果が得られていない主な原因は、プロンプトでの抽出ミスであることが分かりました。賃貸契約書に関してはほぼ100%の正答率で、NGだった部分も読み取り対象の書類の質に問題があったため、特段の課題は残りませんでした。
支払明細書で精度が出なかった理由
NG原因の種類別分析では、図3のグラフにあるように4種類の原因があり、プロンプト以外は件数が少ない、または読み取り対象側に大きな問題があるため割愛します。
プロンプトが原因のNGに関しては、LLMによる誤抽出が直接の原因ですが、検証を進める中で、その背景には支払明細書の複雑さがあることが分かってきました。直近4か月の家賃支払い履歴を参照するには、次のような問題点があることが判明しました。
- 同月に支払履歴が多数存在すると、該当の家賃支払い履歴の判断が困難で、支払履歴名もさまざまであるため範囲指定が難しい
- 支払いではなく出金や差額残高などを参照する場合がある
- 家賃支払いは必ずしも対象月に行われるわけではない(例:7月分を8月1日に、8月分を8月31日に支払うなど)
上記の問題に対して、プロンプトで必要情報を正しく抽出するためにさまざまな工夫を試してみましたが、結果としては正答率が85%程度に留まりました。
今回の検証を通して、一定の効果は得られました。Azure DIの事前構築済みモデルだけでは適切にテキスト抽出できないケースがあったため、readモデルとLLMの合わせ技で処理するアプローチを試してみました。このアプローチ自体は有効で、事前構築済みモデルだけでは抽出できていなかったテキストも抽出できることが確認できました。ただし、まだ精度が安定しておらず、さまざまな形式が存在する支払明細書の処理には課題が残りました。
プロンプト設計による改善の限界
抽出精度向上のためにキーワードの網羅や文脈の明示を試してみましたが、抽出対象の文書の質や内容に大きく左右されることが分かりました。支払明細書のように記載内容のパターンが一律ではない場合、プロンプトだけで的確に抽出することには限界がありました。
賃貸契約書の抽出精度が高かった理由として、国土交通省が提供する標準的なテンプレートが存在するため、文書構造がある程度統一されていることが挙げられます。また、支払明細書のように類似情報が多くなく、プロンプトの指示が通りやすくなっていました。一方、支払明細書は金融機関ごとにフォーマットが異なり、類似情報が多く存在するため、指示が複雑化して調整が難航しました。
プロンプトの分割で一定の改善は見られましたが、今後GPT-5より性能がよいモデルが登場すれば改善余地はあるはずです。こうしたパターンでは、カスタムモデルを使用し、学習データを用意して適宜チューニングするアプローチも試してみる価値があると考えます。
また、Azure DIの事前構築済みモデルには米国のみ対応のものもありますが、今後、日本での利用が可能になったり、新規モデルが追加されたりすることで、精度が改善される可能性もあります。
おわりに
Azure DIとAzure OpenAIの組み合わせは、PoC段階でも高い精度を発揮できるため、プロダクトによっては大幅な時短が期待できます。特にAzure DIは既存モデルの性能がよく、今回もOCRでテキスト抽出そのものが困難だったケースはごく少数でした。そのため、読み取り対象ファイルの品質管理とコンテキストエンジニアリングを磨くことで、さらに高精度な運用が可能になります。
ただし、読み取り対象ファイルの構造によっては単純な読み取りモデルでは厳しい場面もあります。適宜カスタムモデルを利用し、独自の文書構造に合わせてトレーニングを行い、OCR結果に加えて、文脈に基づいたフィールド抽出を行うアプローチも有効だと考えます。