LayerX エンジニアブログ

LayerX の エンジニアブログです。

Document AI を使った請求書読み取り機能の検証

LayerX で機械学習エンジニアとして働いている松村 @yu-ya4 です。現在はAI-OCRチームにて、バクラクシリーズのOCR機能の開発を主に行なっています。この記事は LayerX Tech Advent Calendar 2022 の18日目の記事です。

OCR機能とは、アップロードされた請求書や領収書などの帳票の画像データを読み取り、人間が手入力せずとも必要な項目を自動で抽出してデータ化する機能のことです。以下は請求書OCR機能のデモ動画です。


www.youtube.com

このブログは、このようなOCR機能を誰でも簡単に実現してしまおうとしている Document AI というサービスを触って検証した際のメモ書きとなります。API を扱う Client ライブラリもいくつかの言語で公開されており、今回は Python を使いました。もしかしたら私の仕事がなくなるかもしれません。

Document AI とは

Document AI は2020年にリリースされた Google Cloud のサービスのひとつです。非構造化データである契約書や請求書などのドキュメントデータを対象に、テキストなどの情報を抽出したりそれらを構造化することで、データを理解、分析、利用しやすくすることを目的とします。

cloud.google.com

Document AI の種類(Processor と呼ぶ)は大きく以下の3つに分かれています。

  • General
    • 大方のドキュメントに対応した汎用的な機械学習モデルを利用
  • Specialized
    • 請求書や免許証、契約書など特定のドキュメントごとに特化して作成された機械学習モデルを利用
  • Custom
    • ユーザーが自前で用意したドキュメントを使って訓練した機械学習モデルを利用

Web 上でも好きな Processor を利用して任意のドキュメント(なければサンプルもあります)をアップロードすることで簡単に試せるので是非試してみてください。

Document AI デモ

事前準備

Cloud API を活用する際のいつものやつです。こちらに従って進めてください。

  1. Cloud Document AI API の有効化
    • 今回利用するプロジェクトを選択した上で Document APIを有効化
  2. 認証設定
    • 適切な権限を付与したサービスアカウントを作成して、アカウントキーを発行
    • 発行したキーを実行環境にダウンロードし、パスを環境変数にセットする。 export GOOGLE_APPLICATION_CREDENTIALS="KEY_PATH"
  3. プロセッサインスタンスの作成
    • 今回はドキュメントの中でも請求書を取り上げるので、Specialized Processor の中でも請求書に特化した Invoice Parser を選択
    • 作成したインスタンスの ID は後ほど利用

Python Client を利用して Document AI API を試す

Client のインストール

今回の検証では Python Client を利用します。手元の M1 Mac において Python3.9 で検証しました。Python Client ライブラリは pip でインストール可能です。

github.com

$ python --version
Python 3.9.15
$ pip install --upgrade google-cloud-documentai

利用する請求書データ

今回は以下のようなサンプルの請求書を使ってみます。「株式会社LayerZ」から「株式会社LayerX」に対して「2022/12/18」に発行された請求書です。合計請求金額は「66,000」で支払い期限は「2022/12/30」となっています。

API のリクエスト

あとは ドキュメントを参考に請求書ファイルを指定してAPIを叩きます。

from google.api_core.client_options import ClientOptions
from google.cloud import documentai


def process_document(
    project_id: str, location: str, processor_id: str, file_path: str, mime_type: str
):

    # Initialize
    opts = ClientOptions(api_endpoint=f"{location}-documentai.googleapis.com")
    client = documentai.DocumentProcessorServiceClient(client_options=opts)

    # The full resource name of the processor, e.g.:
    # projects/project_id/locations/location/processor/processor_id
    name = client.processor_path(project_id, location, processor_id)

    with open(file_path, "rb") as image:
        image_content = image.read()

    # Load Binary Data into Document AI RawDocument Object
    raw_document = documentai.RawDocument(content=image_content, mime_type=mime_type)

    # Configure the process request
    request = documentai.ProcessRequest(name=name, raw_document=raw_document)

    result = client.process_document(request=request)

    document = result.document

    return document


# project_id = 'YOUR_PROJECT_ID'
# location = 'YOUR_PROCESSOR_LOCATION' # Format is 'us' or 'eu'
# processor_id = 'YOUR_PROCESSOR_ID' # Create processor before running sample
# file_path = '/path/to/local/pdf'
# mime_type = 'application/pdf' # Refer to https://cloud.google.com/document-ai/docs/file-types for supported file types


document = process_document(
    project_id=project_id,
    location=location,
    processor_id=processor_id,
    file_path=file_path,
    mime_type=mime_type,
)

請求書データの解析結果

読み取ったドキュメント内のテキスト情報には text フィールドでアクセスします。なんとなく意味のある単位で単語が読み取れており、請求書として読み取りたい情報も含まれている気配がします。

>>> print(document.text)
2022/12/18
: lx-001
*
LayerX
LayerZ
LEEF
T012-3456
|||||1-1-1
CHRAN
¥ 66,000 -
TEL: 09012341234
test@gmail.com
2022/12/30#
#m
7077AXE
50 h
1,000
50,000
705
1
10,000
10,000
60,000
(10%)
6,000
Alt
66,000
=()0012345 LTÞ▬ť» |

Invoice Parser では、読み取ったテキストから請求書として読み取りたい項目ごとの値(entity)を識別します。その結果は entities フィールドで取得できます。取得できた entitiy のうち適当なものを表示してみます。

>>> document.entities[3]
text_anchor {
  text_segments {
    start_index: 111
    end_index: 121
  }
  content: "2022/12/30"
}
type_: "invoice_date"
mention_text: "2022/12/30"
confidence: 0.870477498
page_anchor {
  page_refs {
    bounding_poly {
      normalized_vertices {
        x: 0.190362886
        y: 0.251892358
      }
      normalized_vertices {
        x: 0.271267116
        y: 0.251892358
      }
      normalized_vertices {
        x: 0.271267116
        y: 0.262825906
      }
      normalized_vertices {
        x: 0.190362886
        y: 0.262825906
      }
    }
  }
}
id: "3"
normalized_value {
  date_value {
    year: 2022
    month: 12
    day: 30
  }
  text: "2022-12-30"
}

こちらは type_: "invoice_date" とあるように、請求書の発行日に該当する entity のようです。発行日として取得した値を見てみると mention_text: "2022/12/30" となっています。請求書内の日付に関する情報をうまく取得できてはいるのですが、サンプルの請求書を見返すとこちらは「支払い期限」に当たる日付ですので、発行日をうまく識別できてはいなさそうです。

normalized_value 内で、「2022/12/30」-> 「2022-12-30」のように正規化した日付表現を返しているのは地味に便利ですね。

取得できたすべての entity の情報を表示します。参考までに元の請求書ファイルに取得した entity の位置を(手動で)マッピングしたものも用意しました。

>>> for e in document.entities:
...     print(f"{e.type_}: {e.mention_text}")
...
net_amount: 60,000
total_amount: 66,000
supplier_email: test@gmail.com
invoice_date: 2022/12/30
total_tax_amount: 6,000
invoice_type:
supplier_phone: 09012341234
supplier_name: LayerZ
line_item: 50 h 1,000 50,000
line_item: 1 10,000 10,000

invoice_date 以外で取得できた entity については正しい情報となっていそうなことが分かります。表のそれぞれの情報を意味する line_item については、品番・品名に該当する「プロダクト開発業務」なども本来含めたかったようには思います。このようにドキュメント内で位置が離れているものをひとつの意味ある塊として認識するのは難しいと普段の業務でも実感しているため、とても気持ちが分かります。

また、本来 Invoice Parser では他にも、due_date(支払い期限)や receiver_name(請求先)など請求書読み取り機能としては重要な項目も entity として取得できるはずですが今回のケースではうまくいっていませんでした。

他に読み取れる項目など API の詳細については Invoice Parser のドキュメントを参照ください。

所感

他にもいくつかのパターンを試してみたのですが、少なくとも現時点では LayerX バクラク請求書の OCR 機能のほうが必要な情報を読み取れているようでした(仕事を失わずに済みそうでよかった)。特に、やはり日本語への対応は難しそうで、日付の表現を「2022年12月30日」と変えてみたり、請求書の発行者名を漢字の社名にしたりすると entity としては全然読み取れなくなってしまいました。

ただ、大前提として現在の Document AI の日本語対応は不十分な状態です。自然言語処理については OCR で読み取った日本語のテキスト情報を Translation AI にかけて翻訳することで対応しているとのことでした(【Google Cloud Day: Digital ’22】機械学習 セッションより)。また、海外で使われている請求書フォーマットと日本のフォーマットは同じ項目でも記載されている位置が異なっていたりするため、日本語をうまく扱えたとしても該当項目を識別することは困難な可能性が高いです。

一方で、日本語への対応は計画しているとのことなので、どこまでうまく読み取ることができるようになるのかは楽しみです。ただ、実務で大量の生の請求書データを扱っている身としては、あれだけ多種多様なフォーマットや表現を有する請求書を汎用的に読み取ることができる機械学習モデルを作成するのはなかなかに難しいのではないかと感じています。もうしばらく私の仕事はなくならなさそうです。

終わりに

今回は LayerX のバクラクシリーズにおける OCR 機能のように、ドキュメントを解析して必要な項目を読み取ることができる Google Cloud の Document AI、その中でも請求書に特化した Invoice Parser API の検証をしてみた結果を簡単に紹介しました。

LayerX ではOCR機能をいくつかの処理に分割し、一部はドメイン知識に基づくルール/ロジックを明示的に記述することで、そして一部は機械学習を用いることで実現しています。機械学習を用いる部分についても、外部のAPIを利用する部分、学習済みの公開モデルを利用する部分、そして独自の機械学習モデルを作成する部分、といったように分かれています。このように処理ごとに現在の状況に応じた適切な手段を採用することで、小さなチームでも最大限の価値をお客様に提供するべく効率的に開発を進めています。

独自の機械学習モデルの開発については、今年の3月に一人目の機械学習エンジニアが入社してから始まったものでありまだまだ発展途上にありますが、開発の取り組みについての発信も最近は増えてきており、ぜひチェックしてみてください。

tech.layerx.co.jp

tech.layerx.co.jp

LayerX における機械学習活用はまだまだ始まったばかりですが、機械学習エンジニアとして成果を出し成長していくための環境としては他にないくらいいいものだと自負しております。この辺りの話は私の LayerX への入社ブログに記していますので、こちらも是非ご覧ください。

note.com

Document AI に負けないためにも一緒にハタラク仲間を募集しています!まずはカジュアルにお話しできればと思いますので、お気軽に twitter のDMや以下のリンクから声をかけてください。

open.talentio.com