1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Presidio】個人情報検出・匿名化ライブラリ Presidio を使ってみる

1
Posted at

はじめに

今回は Kong AI Proxy 周りについて調べている際に発見した、個人情報(PII: Personally Identifiable Information)を検出・匿名化するオープンソースライブラリ Presidio を試してみます。

Presidio とは

Presidio は、テキストや画像に含まれる PII の 識別(identification) と 匿名化(anonymization) を行うためのオープンソースライブラリです。
もともとは Microsoft が開発していましたが、現在は Data Privacy Stack プロジェクトとしてメンテナンスされています。

Presidio の特徴は、あらかじめ用意された認識器(recognizer)だけでなく、独自の正規表現やロジックによる認識器を追加できる点です。
そのため、汎用的な PII だけでなく、社内の会員番号や契約番号のような独自形式のデータも検出対象に加えることができるようになります。

主な機能(パッケージ構成)

Presidio は役割ごとに複数のパッケージに分かれています。

パッケージ 役割
Presidio Analyzer テキストから PII を検出する。
Presidio Anonymizer Analyzer の検出結果をもとに、テキストを匿名化する。
置換・マスキング・ハッシュ化・暗号化など複数の方式(operator)を選べる。
Presidio Image Redactor 画像内の PII を OCR で検出し、該当箇所を黒塗りするなどして匿名化する。
Presidio Structured CSV やデータフレームのような構造化データ(テーブル形式)に対して、列単位で PII 検出・匿名化を行う

いずれも Python ライブラリとして利用できるほか、Analyzer・Anonymizer・Image Redactor は Docker イメージも提供されており、REST API サービスとして単体で起動することもできます。

Analyzer による検出では、それぞれの認識器がスコア(0〜1)を返し、どの程度確信を持って PII と判定したかを表します。
認識器の種類には主に次のようなものがあります。

  • NER(固有表現抽出)ベース
    spaCy などの NLP モデルを使い、人名・地名・組織名などを文脈から推定する
  • パターン(正規表現)ベース
    メールアドレスや電話番号のように、形式が決まっているものを正規表現で検出する
  • チェックサムベース
    クレジットカード番号(Luhnアルゴリズム)のように、値自体の妥当性を検証できるものに利用される
  • コンテキストによるスコア補正
    「クレジットカード」「電話」といった周辺の単語(context words)があると、スコアが引き上げられる。

Entities(検出対象の種類)について

Presidio では検出対象となる PII の種類を entity(エンティティ) と呼びます。あらかじめ多数の entity 用の認識器が組み込まれており、代表的なものは次のとおりです。

entity 内容
PERSON 人名
EMAIL_ADDRESS メールアドレス
PHONE_NUMBER 電話番号
CREDIT_CARD クレジットカード番号
IP_ADDRESS IPアドレス
LOCATION 地名・住所
DATE_TIME 日付・時刻
URL URL
CRYPTO 暗号資産のウォレットアドレス
NRP 国籍・宗教・政治的所属など

このほかにも、特定の国・地域に固有の識別番号用の entity(米国の社会保障番号など)も多数用意されていますが、今回は割愛します。
対応している entity は言語・地域(supported_languages や地域別パッケージ)によって異なり、以下のページで一覧を確認できます。

独自の entity を追加したい場合は PatternRecognizer で正規表現ベースの認識器を自作するか、リクエストのたびに一時的な認識器を渡せる ad_hoc_recognizers を使う方法があります。
社内独自の会員IDのような、標準では用意されていない PII を検出したい場合に有効です。

動作確認

実際に presidio-analyzer と presidio-anonymizer を使って、PII 検出と匿名化を試します。
今回は uv を使用します。

Analyzer は内部で spaCy を NLP エンジンとして利用するため、対応する言語モデル(今回は英語の en_core_web_lg)を別途ダウンロードしておく必要があります。

PII を検出する(Analyzer)

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "presidio-analyzer",
#     "presidio-anonymizer",
#     "en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl",
# ]
# ///

from presidio_analyzer import AnalyzerEngine

text = "My name is John Smith and my email is john.smith@example.com. Call me at 212-555-5555."

analyzer = AnalyzerEngine()
results = analyzer.analyze(
    text=text,
    language="en",
    entities=["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER"],
)

for r in results:
    print(r)

出力

type: EMAIL_ADDRESS, start: 38, end: 60, score: 1.0
type: PERSON, start: 11, end: 21, score: 0.85
type: PHONE_NUMBER, start: 73, end: 85, score: 0.4

start/end はテキスト中での検出範囲(文字インデックス)、score は認識器が算出した確信度です。
メールアドレスは正規表現による厳密な一致のため score=1.0、人名は NER モデルによる推定のため 0.85、電話番号は形式のブレ幅が大きいため 0.4 と、認識器の種類によってスコアの出方が異なる点が確認できます。

検出結果をもとに匿名化する(Anonymizer)

Analyzer の検出結果(results)をそのまま Anonymizer に渡すと、匿名化されたテキストを得られます。

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "presidio-analyzer",
#     "presidio-anonymizer",
#     "en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl",
# ]
# ///

from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import OperatorConfig

text = "My name is John Smith and my email is john.smith@example.com. Call me at 212-555-5555."

analyzer = AnalyzerEngine()
results = analyzer.analyze(
    text=text,
    language="en",
    entities=["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER"],
)

anonymizer = AnonymizerEngine()

# 何も指定しない場合は <ENTITY_TYPE> に置換される
default_result = anonymizer.anonymize(text=text, analyzer_results=results)
print("# 指定なし")
print(default_result.text)

# operators で entity ごとに匿名化方式(operator)を指定できる
custom_result = anonymizer.anonymize(
    text=text,
    analyzer_results=results,
    operators={
        "PERSON": OperatorConfig("replace", {"new_value": "<REDACTED>"}),
        "EMAIL_ADDRESS": OperatorConfig("mask", {"masking_char": "*", "chars_to_mask": 100, "from_end": False}),
        "PHONE_NUMBER": OperatorConfig("hash", {"hash_type": "sha256"}),
    },
)
print("# 指定あり")
print(custom_result.text)

出力

# 指定なし
My name is <PERSON> and my email is <EMAIL_ADDRESS>. Call me at <PHONE_NUMBER>.
# 指定あり
My name is <REDACTED> and my email is **********************. Call me at ae2fd9f935fe737aec8adf23629e2c07e73f58acdc42b4d6c48455df0c388c4f.

operators を指定しない場合は entity 名への置換(replace)がデフォルトの動作になります。
operators で entity ごとに replace(任意の文字列に置換)・mask(一部を記号でマスク)・hash(ハッシュ化)・redact(完全に削除)・encrypt(可逆的な暗号化)などを個別に指定できるため、用途に応じて「氏名は固定文字列に置き換え、電話番号は一方向のハッシュにする」といった使い分けが可能です。

Analyzer の構成

AnalyzerEngine は、NlpEngine・RecognizerRegistry・ContextAwareEnhancer という3つのコンポーネントを組み合わせて動作します。

  1. NlpEngine
    テキストをトークン化・レンマ化し、固有表現抽出(NER)を行います。
    結果は NlpArtifacts としてまとめられ、以降のすべての認識器から共有で参照されます。
    デフォルトは spaCy を使う SpacyNlpEngine です。

  2. RecognizerRegistry
    組み込み・カスタムを問わずすべての認識器(基底クラス EntityRecognizer)を管理します。
    各認識器は NlpArtifacts を受け取り、RecognizerResult(entity_type・start・end・score)のリストを返します。

  3. ContextAwareEnhancer
    デフォルト実装の LemmaContextAwareEnhancer が、認識器に登録された context(周辺語のリスト)が検出箇所の前後に出現しているかを確認し、出現していればスコアを引き上げます。
    補正は「元のスコア + context_similarity_factor(デフォルト 0.35)」で計算され、結果が min_score_with_context_similarity(デフォルト 0.4)を下回る場合は 0.4 に引き上げられます。

※ これらの結果として得られるスコアは analyze() の score_threshold 引数で足切りでき、確信度の低い検出結果(誤検出)を除外できます。

Anonymizer の仕組み

AnonymizerEngine.anonymize() は、元のテキスト・Analyzer の検出結果(RecognizerResult のリスト)・operators(entity ごとの OperatorConfig)を受け取り、次の手順で匿名化テキストを組み立てます。

  1. 検出結果の重複を解決する
    複数の認識器が同じ範囲や重なる範囲を検出することがあるため、operator を適用する前に重複関係を次のルールで解決します。

    • 重複なし:そのまま指定した operator を適用
    • 完全に同じ範囲で重複:スコアが高い方を採用(同点の場合はどちらかを採用)
    • 包含関係(一方がもう一方を完全に含む):スコアに関わらず範囲が広い方を優先
    • 部分的に重なる:それぞれを個別に処理し、結果のテキストを連結
  2. Operator が実際の変換を行う
    すべての operator は抽象クラス Operator を継承しており、operate(text, params)(実際の変換処理)・validate(params)(パラメータの検証)・operator_type()(Anonymize か Deanonymize かの区別)を実装します。replace / mask / hash / redact / encrypt / custom は、いずれもこの共通インターフェースの上に実装された組み込み operator です。

まとめ

今回は、PII 検出・匿名化ライブラリ Presidio の概要と、Analyzer・Anonymizer・Image Redactor・Structured という4つのパッケージ構成、検出対象となる entities について整理しました。

  • Presidio は PII の 検出(Analyzer) と 匿名化(Anonymizer) を分離した構成になっており、それぞれ Python ライブラリ・REST API サービスの両方で利用できる
  • 検出対象の PII 種別は entity と呼ばれ、PERSON や EMAIL_ADDRESS など汎用的なものから、国・地域固有の識別番号まで幅広く用意されている
  • Analyzer は NlpEngine・RecognizerRegistry・ContextAwareEnhancer の3コンポーネントで構成されており、認識器のスコアは周辺の文脈(context words)に応じて補正される。
    独自形式の PII を検出したい場合は PatternRecognizer や ad_hoc_recognizers で認識器を追加でき、score_threshold で確信度の低い検出結果を除外することもできる。
  • Anonymizer は operators で entity ごとに匿名化方式(置換・マスク・ハッシュ化など)を切り替えられる。
    内部では検出結果の重複をルールベースで解決したうえで、共通インターフェース Operator を介して replace / mask / hash / redact / encrypt / custom などの変換を行う。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?