Skip to content

文書とPDFにアノテーションを付ける方法

契約書、報告書、論文を平坦化したテキストではなく文書として注釈し、レイアウト、ページ番号、ページをまたぐ参照をそのまま保つ。

文書は抽出したテキストではなくレンダリングされたページの上で注釈する。レイアウトは抽出が捨ててしまう意味を担っており、確認する側はラベルをページ上で見つける必要があるからだ。 契約書を一本の文字列に平坦化すると、ページ番号、段の順序、表の構造、脚注と本文の区別が失われる。

文書アノテーションは、通常のテキストアノテーションの構成が通用しない場面だ。PDFは文字の並びではなくページ上の印の記述なので、同じファイルから二つの異なる抽出器は二つの異なる文字列を返す。多段組みのページは互い違いに混ざり、ヘッダーとフッターは流れの途中に現れ、表は空白の連なりになる。その文字列にラベルを付ける作業者は抽出器の産物にラベルを付けているのであり、後から原本を開いた確認者は、あるスパンがどの箇所を指していたのかを必ずしも判別できない。

位置、ページ番号、参照構造

文書の三つの性質は、ページ上には残るが抽出テキストには残らない。位置は、ある条項が見出しなのか脚注なのか本文なのかを確認者に伝えるが、抽出は三つとも同じ区別のない文字の連なりに潰してしまう。ページ番号は、後続のあらゆる読み手がその箇所を引用するときの手がかりであり、ページが存在するあいだしか存在しない。参照構造はある箇所が何を指しているかを記録するもので、あるページの主張が九ページ先の表に依拠しているときに効いてくる。

道具の変更をいちばん頻繁に迫るのは三つ目の性質だ。文書内部の参照構造は二つの位置のあいだの関係であり、文書を一本の平坦な文字列としてモデル化する道具には二つ目の位置を置く場所がない。法務レビュー、科学的主張の検証、財務報告の分析は、いずれもその関係を軸にしている。

三種類のアンカー

文書上の注釈は三通りのいずれかで固定され、どれを選ぶかは好みではなく問いから決まる。

  • テキストスパンは選択した語に固定され、文言に関する問いに向く。スパンには文書がテキストレイヤーを持っていることが要る。デジタル生成のPDFは持っており、スキャンは持っていない。
  • 領域ボックスはページ座標に固定され、図、表、印影、署名、そしてテキストレイヤーが捉えたことのないあらゆるものに向く。テキスト抽出がまったくないスキャンでも機能する。
  • リンクは二つのアンカーを結び、参照構造に向く。リンクは文書内の二か所のあいだの、型付きで、必要なら向きを持つ関係を記録する。

一つのタスクでアンカーの種類を混ぜるのは普通のことだ。主張検証タスクは主張をテキストスパンとして、表を領域ボックスとして印を付け、それらをリンクで結ぶ。

Potatoでの設定

PotatoのpdfディスプレイはファイルをPDF.jsで描画し、注釈面をレンダリング後のページの上に置く。annotation_modeオプションがアンカーの種類を選び、span、bounding_box、linkを取る。参照の問いに答えるのはリンクモードで、アンカーとリンクを別々のスキーマ名の下に記録するので、書き出しでも両者を区別できる。

yaml
instance_display:
  fields:
    - key: pdf
      type: pdf
      label: "Document"
      display_options:
        annotation_mode: link
        view_mode: scroll
        zoom: page-width
        enable_text_anchors: true
        enable_region_anchors: true
        anchor_schema: pdf_anchors
        link_schema: pdf_links
        anchor_labels:
          - name: claim
            color: "#dc2626"
          - name: figure
            color: "#2563eb"
        link_types:
          - name: refers_to
            directed: true
            allowed_source_labels: [claim]
            allowed_target_labels: [figure]

view_mode: scrollはページを一つのコンテナに積み上げるので、2ページ目と9ページ目のあいだのリンクを一本の弧として引き、そのまま見ることができる。ページ送り方式は一度に一ページを表示し、長い文書では読みやすいが、作業者がページをまたぐリンクを引いているあいだ、その反対側の端を隠してしまう。

allowed_source_labelsとallowed_target_labelsは、ガイドラインをインターフェースが強制するものに変える。上の設定なら、refers_toリンクは主張からしか始められず図にしか終われないので、作業者が関係を逆向きに記録することはできない。文書の中にしか存在しないガイドラインは守られ方にばらつきが出るが、道具が適用する制約は強制するコストがかからない。

WordやMarkdownのファイルには代わりにdocumentディスプレイを使う。見出しと段落の構造を保ち、span_targetを受け付けるので、スパンスキーマが直接そこを指せる。

スキャン文書

スキャンにはテキストレイヤーがないので、テキストアンカーには付く先がない。選択肢は二つあり、それぞれ向くコーパスが違う。領域で注釈する方法はテキストをまったく必要とせず、すぐに使える。OCRを有効にするとサーバー側でテキストレイヤーが作られてテキストアンカーが使えるようになるが、処理が遅く、Tesseractへの依存が加わる。

yaml
instance_display:
  fields:
    - key: pdf
      type: pdf
      display_options:
        annotation_mode: link
        ocr: auto

autoは埋め込みのテキストレイヤーが空で返ってきたときだけOCRを走らせる。デジタル生成のファイルとスキャンが混在するコーパスではこの設定を使う。Potatoはocrオプションをリンクモードでのみ読む。

OCRの出力は完全ではなく、誤認識された語に固定した注釈はその誤りを引き継ぐ。認識品質の悪いコーパスでは、領域アンカーのほうがテキストアンカーより長持ちする記録になる。ある箇所を囲んだ枠は、その下の文字がどう読まれていようと正しいままだからだ。

ラベルを原典と突き合わせる

どの注釈も、それが載っているページを伴って返ってくる。だからこそラベルを原典と突き合わせて確認できる。ページ番号と領域を渡された確認者は、原本を開いて判断を確かめられる。連結された文字列への文字オフセットを渡された確認者はたいていそれができない。オフセットを再現するには、それを生んだ抽出器とそのバージョンをそのまま再現しなければならないからだ。

この手法が見返りより高くつくのは、参照構造を持たない短い一段組みのデジタル生成文書のコーパスで、抽出が信頼でき、ページ番号が何も担っていない場合だ。そこでは抽出テキストに対する素のスパンアノテーションのほうが単純で、同じ答えが得られる。

さらに読む