Bunkai(日本語文境界判定器)でPDFテキスト抽出の改行位置をいい感じにする

R&D チームの徳田(@dakuton)です。
前回記事にてPDFの改行補正に関する記事を書いたあと、前回記事のような日本語文境界判定での利用に特化したBunkaiというライブラリが公開されたので、今回は続編として紹介します。

前回記事 tech-blog.optim.co.jp

テストデータ

前回と同様、下記記事のPDFを使用しました。

Poppler(pdftotext)を用いた場合のテキスト抽出結果(冒頭部分のみ記載)

$ wget https://www.mof.go.jp/public_relations/finance/202102/202102b.pdf
$ pdftotext 202102b.pdf -
巻頭言

ポストコロナ時代を形作る、
コロナ禍で生まれる DX(デジ
タルトランスフォーメーション)
株式会社オプティム 代表取締役社長

菅谷 俊二

現

在、私が代表を勤めております株式会社オ
プティムは、私が佐賀大学農学部在学時に
創業した企業で、2015 年には東証一部に上場し
ております。実は数多くの上場企業の中で唯一国
立大学(佐賀大学)内に本店を置いた企業でもあ

・
・
・

実験コード(parse_pdf_bunkai.py)

import re
import subprocess
from pathlib import Path

import plac
from bunkai import Bunkai


def parse_lines_bunkai(lines, bunkai_model):
    lines = lines.strip().replace("\n", "\u2581")
    return bunkai("".join(lines))


@plac.annotations(
    src=("pdf or txt", "positional"),
    model=("bunkai model path", "option", None, str)
)
def main(src, model):
    file_path = Path(src)
    if file_path.suffix[1:] == "pdf":
        res = subprocess.run(["pdftotext", file_path, "-"], stdout=subprocess.PIPE)
        lines = "".join(res.stdout.decode("utf-8"))
    else:
        lines = open(file_path, "r").read()

    path_model = Path(bunkai_model) if model is not None else None
    bunkai_model = Bunkai(path_model=path_model)
    for line in parse_lines_bunkai(lines, bunkai_model):
        print(line)


if __name__ == "__main__":
    plac.call(main)

実行結果

句点のみで分割した場合(前回記事)

$ python parse_pdf.py 202102b.pdf
巻頭言ポストコロナ時代を形作る、コロナ禍で生まれるDX(デジタルトランスフォーメーション)株式会社オプティム代表取締役社長菅谷俊二現在、私が代表を勤めております株式会社オプティムは、私が佐賀大学農学部在学時に創業した企業で、2015年には東証一部に上場しております
実は数多くの上場企業の中で唯一国立大学(佐賀大学)内に本店を置いた企業でもあります
そんな私達が夢中になって取り組んでいる事業とは「日本の多くの課題をDX(AI・IoT)によって解決すること」です
我々はこの取組を“〇〇×IT:(あらゆる産業)×(AI・IoT・Cloud・Robotics)”と呼んでいます
・
・
・

Bunkaiによる分割を用いた場合

$ bunkai --model bunkai-model-directory --setup
$ python parse_pdf_bunkai.py -model bunkai-model-directory 202102b.pdf
巻頭言▁▁
ポストコロナ時代を形作る、▁コロナ禍で生まれる DX(デジ▁タルトランスフォーメーション)▁
株式会社オプティム 代表取締役社長▁▁
菅谷 俊二▁▁
現▁▁在、私が代表を勤めております株式会社オ▁プティムは、私が佐賀大学農学部在学時に▁創業した企業で、2015 年には東証一部に上場し▁ております。
実は数多くの上場企業の中で唯一国▁立大学(佐賀大学)内に本店を置いた企業でもあ▁ります。
そんな私達が夢中になって取り組んでい▁る事業とは「日本の多くの課題を DX(AI・IoT)▁によって解決すること」です。
我々はこの取組を▁“〇〇×IT:▁(あらゆる産業)×(AI・IoT・Cloud・▁Robotics)▁”と呼んでいます。
・
・
・

Bunkaiには句点の分割ルールが含まれているため、今回は独自ルールを併用せずBunkai単体で実行しています。(結果の"▁"は元のPDFでの改行出現位置にあたります)
Bunkaiは下記のように品詞や正規表現に基づく結合・分割ルールを複数組み合わせて読みやすい文に整形できるようになっています。
特にSNSのようなテキストでの分境界判定に有効そうです。

Bunkaiに組み込まれている結合・分割ルール

  • 形態素解析に基づく判断
  • ピリオド(".")含む句読点に基づく判断
    • ただし、"1.5リットル"のように小数表現にあたる場合は分割しない
  • チャットメッセージ向けの判断("笑","泣"や絵文字)
  • [オプション] 改行に基づく判断
    • bunkai-model-directoryにて利用(bunkai --model bunkai-model-directory --setup)
    • bunkai-model-directoryを利用しない場合は"▁"("\u2581")を改行とみなして連結するかどうかを判断しているため、改行文字のまま判断したい場合に導入します。

おわりに

今回紹介した方法も、下記のようにExampleレベルのコードを5行ほど追加すれば使えてお手軽なものとなっています。
簡単なのでぜひともチャレンジしてみてください。

from bunkai import Bunkai
bunkai_model = Bunkai()
sents = bunkai_model('オプティムでは自然言語処理に限らずさまざまなエンジニアをお待ちしています★興味があるかたはこちらまで🚀😁https://www.optim.co.jp/recruit/')
for sent in sents:
    print(sent)

実行結果

$ python recruit.py
オプティムでは自然言語処理に限らずさまざまなエンジニアをお待ちしています★
興味があるかたはこちらまで🚀😁
https://www.optim.co.jp/recruit/

www.optim.co.jp