技術
· 9分で読めます

日本語の個人情報検出エンジンを自社で作った理由

執筆:Amine Gaaliche · Claudeで下書きを作成し、内容は人が確認したうえで手作業で編集しています

日本語の文書から個人情報を検出できるようにしたとき、手間がかかったのはモデル選びではありませんでした。空白のない行に正しく枠を描くこと、番号や日付を検算すること、そして氏名のように見えて氏名ではない語を外すことです。どれも、検出ライブラリや固有名詞のモデルを1つ組み込めば済む作業ではなかったため、当社は検出エンジンを自社で作っています。

空白のない行に、氏名だけを覆う枠を描く

PDFのページで黒く塗るのは、文字列の位置ではなくページ上の長方形です。欧州の言語では、検出した範囲を空白で区切った単語の単位まで広げて枠にすれば、ほぼ問題はありません。日本語では、空白で区切った「単語」が1行全体になることがあります。日本語に対応する作業を始めて最初に調べたとき、検出範囲を単語の単位に広げる処理が、日本語では氏名1つのために行をまるごと塗ってしまうことがわかりました。

そこで、漢字やかなを含む行に限って1文字ずつ横の位置を持たせ、氏名の文字だけを覆うようにしました。検出範囲を広げる処理も、かなと漢字をまたいでは広げません。欧州の言語の行にはこの位置情報を付けないため、ほかの言語の枠は1つも変わっていません。

スキャンした文書では、別の問題が出ます。文字認識エンジンのTesseractは、日本語を「神奈 川 県 横浜 市」のように空白で区切って返すため、そのままではどのパターンにも一致しません。同じ行で隣り合う文字が日本語どうし(または日本語と数字)で、間隔が1文字分以内なら、つなぎ直しています。半角カタカナが全角で返ってくることにも合わせています。

形態素解析器は使っていません。氏名の範囲は、mecab-ipadicから作った辞書と、1文字ずつの位置で決めています。

番号と日付は、形と検算で確かめる

形の決まった情報は、パターンで検出したあとに中身を確かめます。

  • 電話番号:050、070、080、090、0800で始まる番号は11桁、それ以外は10桁であることを確かめます。かっこ付きの市外局番や+81の表記にも対応しています。
  • マイナンバー:12桁で、最後の1桁が検査用の数字です。ただ、でたらめな12桁でも約10回に1回は検算を通ってしまうため、「個人番号」や「マイナンバー」といった語が近くにある場合に限って検出します。
  • 法人番号:13桁で、先頭の1桁が検査用の数字です。法人の適格請求書の登録番号(Tで始まる番号)も同じ計算で確かめます。
  • 和暦の日付:明治以降の元号と「元年」を読み、年がその元号の範囲に収まるか(たとえば昭和は64年まで)を確かめます。「H5.1.27」のような略記や、西暦と和暦の併記にも対応しています。
  • 住所:47都道府県のいずれかで始まり、市区町村、番地と続く並びで判定します。前に郵便番号があれば一緒に覆います。「住所」「所在地」などの項目名に続く値も対象です。

確かめる段階を入れているのは、取りこぼしよりも、数字の並びを手当たり次第に塗ることを避けるためです。

敬称のない氏名と、モデルの選び方

日本語の文書では、氏名に目印が付いていることがよくあります。「様」「さん」「氏」「殿」などの敬称、「氏名」「フリガナ」といった項目名、署名のあとの「印」がそうです。規則はこうした目印を手がかりに氏名を見つけます。ただし「仕様」や「模様」の「様」は敬称ではなく、「氏名」自体も氏名ではありません。

本文の中で敬称なしに出てくる氏名には、モデルが必要です。当社はGiNZAやspaCyの日本語モデル、GLiNERを含む24通りの構成を、自社の評価用文書で比べました。漢字の氏名は、どの有力な候補もほぼ見つけます。差が出たのは、銀行の明細に出てくる半角カタカナの名義、ローマ字の氏名、ひらがなの読みでした。

選んだのはtsmatz/xlm-roberta-ner-japaneseです。精度がほぼ同じだったもう1つの候補は、前処理として文を単語に区切る必要があり、処理に約9倍の時間がかかりました。tsmatzのモデルは単語に区切らずに使え、文書を1回通すだけで済みます。

それまで多言語用に使っていたモデルは、配布サイズを抑えるために語彙を削っていました。その結果、日本語の文がまるごと未知の記号1つになり、何も検出できませんでした。日本語のモデルは、日本語専用の検出エンジンとして別に配布しています。

作業の大半は、検出しないものを決めること

検出の後段にある誤検出の除去は、ラテン文字を前提に書かれていました。「3文字未満は名前ではない」という規則は2文字で完結する「田中」を落とし、「大文字を含まない語は普通の文」という規則は、大文字のない日本語の氏名をすべて落としていました。どちらも、実際の文書で検出の流れを1件ずつ追って見つけたものです。

欧州の言語向けには、「CEO」のような役職名を氏名として扱わないための除外リストがあり、195語あります。このリストに日本語の語は1語もなく、あっても役に立ちません。日本語では、「代表取締役」「部長」のような役職、「総務部」のような部署名、押印欄の「印」が、氏名や組織名に紛れ込むからです。そのため、氏名は「印」で終わらない、「部」や「課」で始まらない、部署は組織名ではない、といった規則を日本語専用に設けています。

この規則は、新しい文書で測り直すたびに見直し、増やしてきました。検出ルールの作成に使っていない文書での2026年11月の測定では、個人情報にあたる文字の検出率は、一般的な文書8種類で94.3%、名簿や一覧表の多い文書8種類(うち5種類が名簿)で88.4%、表の多い別の文書8種類で93.1%でした。見落としで多いのは、名簿や一覧表に並ぶ住所や番号と、法人格の付かない事業所名です。そのため、検出結果は人が確認してから使う前提にしています。

既存のライブラリとの関係

Presidioは、Microsoftで作られ、現在はコミュニティが運営している検出ライブラリです。文字列を受け取って、どこが何に当たるかを返す仕事を、きちんとこなします。チャットのログやデータベースの列を扱う場合は、このほうが向いています。

ただ、ここまでに書いた日本語の作業は、ページ上の枠、スキャンのつなぎ直し、番号の検算、誤検出の除去と、どれもライブラリの外側にあります。Presidioの拡張として書くこともできますが、その場合Presidioに残る役割は、検出を呼び出すことだけになります。当社は、検出と誤検出の除去を同じ場所で扱えるように、エンジンごと自社で書くことにしました。

架空の日本語の文書8件で行ったPresidioとの比較では、読めるまま残った個人情報の文字数は、Presidioで733、promptShieldで63でした。Presidioの日本語モデルはja_core_news_lgで、日本語用の認識器は追加していません。2026年10月のその時点の測定です。

よくある質問

日本語の氏名の範囲を決めるのに、形態素解析器を使っていますか?

使っていません。mecab-ipadicから作った辞書で姓や名にあたる語を判定し、1文字ずつの位置で枠を描いています。固有名詞の検出モデルも、単語に区切る前処理なしで動くものを選んでいます。

日本語の固有名詞のモデルを入れるだけでは、足りないのですか?

足りません。モデルは候補を出しますが、ページ上の枠、番号の検算、役職名や部署名を外す判断は、モデルの外で行っています。誤検出がどれだけ少ないかは、主にこの外側の規則で決まります。

無料のブラウザー版でも、日本語の文書を検出できますか?

できます。ブラウザー版は、日本語用に小さくした別のモデルをブラウザーの中で動かし、文書をサーバーに送りません。モデルを小さくした方法は、ブラウザー版の日本語モデルについての記事で説明しています。なお、起動を伝える匿名の通知とエラーの報告は送るため、ブラウザー版をオフラインとは呼んでいません。

役に立った場合は、拍手でお知らせください。

共有