こんにちは、マネーフォワードの 田中です。 この記事は、Money Forward Engineering Advent Calendar 2021 12日目の投稿です。
ところで、昨日のken11さんの記事は皆様お読みになられましたか?
「アドベントカレンダー」ですけど、僕は幼少期に「毎日1個チョコが出てくるイベント」と認識していた
素晴らしいご家庭ですね。自分はずっと「IT系の人たちが何か一斉にブログを書き出す謎の祭り」の事だと思っていたので、お店に「アドベントカレンダー」が売っているのを見てパニックになったことがあります。
今回はいま日本でアツイ、「書類読み取り機能」を実現する技術の1つを紹介したいと思います。
はじめに
世の中には構造化された文書が多く存在します。構造化された文書というのはここでは、ある程度のフォーマットが決まっていて、定められた項目が記述してある文書のことを指すこととします。文書というと分かりにくいですが、PDFファイルや単なる画像をイメージしてください。
構造化された文書として、例えばレシート・領収書・請求書のPDFや画像が挙げられます。会社員をやっていると、経費申請や払込申請を行う際に、このような構造化された文書を目で見て、金額などの項目を手でシステムに入力することが少なくないと思います。
今回はそのような文書から、「金額」や「振込先口座名」などのお目当ての項目を機械的に抽出する技術について紹介したいと思います。もしも精度高く項目抽出できれば、数値入力の手間を減らすことができ、私たちはその時間を他のことに当てることができるのでとても嬉しいです。
構造化された文書から項目の値を抽出するために、これまでに様々な手法が提案されてきていますが、今回はその中の1つ Representation Learning for Information Extraction from Form-like Documents というタイトルの論文について紹介します。最近Attention(注意機構)が様々な分野で注目されていますが、この論文ではAttentionの主な性質である「集合を入力出来るニューラルネットワーク」を活かした手法が提案されています。
この論文はGoogle AIの公式ブログでも紹介されているので、ぜひご覧になってください:https://ai.googleblog.com/2020/06/extracting-structured-data-from.html
Representation Learning for Information Extraction from Form-like Documents
概要
大まかな流れとしては
- ステップ1:文書から文字列とその座標を抽出する
- ステップ2:抽出した文字列から関心項目の値を選択する
といったもので、ステップ2で今回提案されているモデルによる推論が行われます。
問題設定
ここでは、構造化された文書から、その文書に記述されている文字列と、その文字列の座標(例えば文字列を含むbounding box)が取得できているとします。
実際には、文書が文字の情報を内包するようなPDFデータであれば、PDF解析ライブラリ(pythonのライブラリではpdfminer.six が有名)を用いることで、そのような情報を得ることができます。また、文書が単なる画像データで与えられた場合にも汎用OCRエンジンを利用することでそのような情報を得ることができます。論文中では Google Cloud Vision API を利用したそうです。
入力
そこで今回の入力情報は、2種類:
- 文書に含まれる文字列に関する情報:
[("口座番号", 100, 120, 300, 310), ("1234567", 130, 140, 300, 310), ...]
- 各要素は、例えば
(文字列、bounding boxの左上x座標、bounding boxの右下x座標、bounding boxの左上y座標、bounding boxの右下y座標)
のように与えられます。
- 各要素は、例えば
- 抽出したい項目(以下では関心項目と呼びます):
["支払金額", "支払期日", ... ]
とすることができます。また、アノテーションデータとして、各関心項目に対応する文字列が得られているとします。(どの文字列がどの項目に該当するか、というデータ)
出力
最終的に得たい出力は、各関心項目に対応する文字列です。
例えば、ある文書について、関心項目 支払金額
と支払期日
がある時、
{"支払金額": "10000", "支払期日": "2021/12/12"}
のような情報を得たいです。
手法
候補語の選定
抽出した文字列から関心項目の値を選択したいわけですが、全ての文字列をターゲットにするのは効率が悪そうです。例えば、日付に関する関心項目では 1000円
といった文字列は明らかに候補から除外して良いでしょう。
そこで、関心項目によってはあり得ない文字列をあらかじめフィルタする処理を挟みます。論文中では正規表現を使うことや、系列ラベリングを行うモデルを使うことなどが挙げられていますが、論文では https://cloud.google.com/natural-language という固有表現抽出サービスを利用し、日付・金額などを判定したようです。
このステップで、各関心項目に対して、候補となる文字列の集合を得ることができました。
関心項目の値のスコアリング
各関心項目について、候補文字列に対して[0, 1]のスコアをつけることを考えます。スコアが高ければ高いほど、関心項目の値らしいとしたいです。
ある候補文字列に対してスコアをつける際に、以下の情報を利用します:
- 関心項目は何か?(「支払金額」に対するスコアなのか、「支払期日」に対するスコアなのか...etc.)
- 候補文字列の座標
- 候補文字列の近傍にある、各文字列のテキスト情報と、候補文字列からの相対的な位置情報
cited from https://ai.googleblog.com/2020/06/extracting-structured-data-from.html
ここで、「候補文字列の近傍にある、各文字列の候補文字列からの相対的な位置情報」とは、上記の図で言うと候補文字列 02/26/2015
を原点とした時の近傍文字列 Invoice
の相対位置です。ですので、相対的な位置情報は例えばx軸方向の相対距離とy軸方向の相対距離の2次元実ベクトルとして与えられます。
人間の目で文書を見るときにも、近くにどんな文字が書いてあるか、というのは(少なくとも私にとっては)重要な情報なので、近傍文字列の情報をモデルに入力するのは筋がよく思えます。
候補文字列のテキストの情報は、過適合を防ぐためモデルの入力としては使わなかったそうです。例えば、2019年に作成された文書をトレーニング/評価データとして利用したモデルで、2019年以前/以降に作成された文書を推論対象とするとき、2019
という文字列を手がかりに推論を行ってしまうと精度が劣化してしまうことが考えられるためです。個人的にはこれはやりすぎなんじゃないかと思っていて、例えば過適合の原因になりそうな数字文字列を全て0
などの固定の数字に変換した上で、候補文字列のテキスト情報をモデルに入力してもいいのではないかと思います。
モデル構造
cited from: https://ai.googleblog.com/2020/06/extracting-structured-data-from.html
上記の図は論文中にも掲載されているもので、モデルアーキテクチャの概観を示しています。
- (j) field embedding:Field idには関心項目をlabel encodingした整数を入力します
{支払金額": 0, "支払期日": 1, ...}
といったようにidに変換するだけです
- (g) Cand. pos.:には候補文字列の、文書中での座標を入力します
Neighbors:候補文字列の近隣にある文字列の集合を入力します。最終的には近隣文字列を1つのベクトルに埋め込みたいですが、ここで近隣文字列の順番に意味はないので、埋め込みにはpermutation invariance性があると良さそうです。そこで、self-attentionが利用されています。以下の(a)と(b)の情報をconcatしたものの集合に対してself-attentionを行い、最終的に1つのベクトルに集約します。
- (a) text :近隣文字列のテキスト情報。単語埋め込みテーブルによってベクトルに変換されます。
- (b) Pos:候補文字列に対する近隣文字列の相対座標。こちらも全結合層によってベクトルに変換されます。
候補文字列に関する特徴を埋め込んだベクトルと、近隣文字列の集合に関する特徴を埋め込んだベクトルを結合させて(i): Candidate Encodingとし、関心項目のidを埋め込んだベクトルとのcosine 類似度をとります。そして、[-1, 1]のcosine類似度を[0, 1]にスケールします。
モデルの学習はこのスケールした類似度と、0(関心項目の文字列ではない) or 1(関心項目の文字列である)の教師データとのbinary cross entrpoyを最小化するように行われます。色々な処理がありましたが、最終的には各候補文字列に対する2値分類器を作る、というだけです。
cosine類似度を使っていることからrepresentation learningというタイトルを付けているのだと思いますが、単に全結合層とsigmoid関数等で[0,1]のスカラを出力するようにしてもいいと思います。論文中では関心項目の埋め込み表現と、候補文字列+近隣文字列集合の埋め込み表現をTSNEで2次元に圧縮したものを可視化として載せています。(ただ結局何が起きてるのかはよく分からん...というやつです)
実験
2つのデータセット(請求書/レシート)に対して実験を行なっています。
いずれも未知のフォーマットに対する汎化性能を評価するため、train-validation-test分割は文書のフォーマットに被りがないように行われています。
比較手法
- BoW:近隣文字列のテキスト情報をBag of wordsベクトルで入力し、かつ近隣文字列の相対座標は利用しない
- MLP:入力情報は提案手法と同じ(近隣文字列の相対座標も利用する)だが、埋め込みにself-attentionではなく全結合層を利用する
結果
Scorer ROC AUC
が、抽出した文字列の関心項目か否か、の2値分類精度を表していて、End-to-End Max F1
が文字抽出部分(OCR)も含めた関心項目抽出精度です。
- 提案手法がBoWに優っている事から、近隣文字列の相対座標が予測に貢献していること
- 提案手法がMLPに優っている事から、近隣文字列の埋め込みにself-attantionが適していること
を主張しています。
おわりに
今回は構造化された文書から情報を抽出する技術の1つについて紹介しました。
個人的にはattentionをうまく使っているなあというのと、普段私たちが目で見て判断するのと同じような情報を使った推論プロセスだなあと、スッと入ってきたので紹介させていただきました。
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【サイトのご案内】 ■マネーフォワード採用サイト ■Wantedly ■京都開発拠点
【プロダクトのご紹介】 ■お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android
■ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』
■だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』