OpenAI Privacy Filterは、テキスト中の個人を特定できる情報(PII)を検出してマスキングするための、OpenAIのオープンウェイトモデルです。2026年4月22日にApache 2.0ライセンスで公開され、Hugging FaceとGitHubから入手できます。APIとして呼ぶ製品ではなく、ウェイトを取得して手元のCPUやGPUで動かします。
この記事では、公式モデルカードとソースコードで仕様を確かめたうえで、日本語の文を実際に通した結果を載せています。漢字の氏名や半角の電話番号は拾えました。一方で、全角数字の電話番号と、かな表記の氏名(同じ名前で試した4文すべて)は検出されませんでした。導入前に知っておくべき差なので、実測の章で詳しく扱います。
まとめ:Privacy Filterの要点と導入判断
- 総パラメータ15億・推論時に使うのは5,000万の小型MoEモデル。128,000トークンまで一度に処理でき、商用利用もできる(Apache 2.0)
- 検出対象は氏名・住所・メール・電話・URL・日付・口座番号・シークレットの8カテゴリに固定されている。ラベルを増やすにはファインチューニングが要る
- 動かし方は公式CLI「opf」、Transformers(v5.6.0以降)、ONNX版の3経路。Viterbi復号が既定で効くのはopfだけ
- 日本語の実測では、文脈のある文なら漢字氏名・半角電話番号・住所・マイナンバー・カード番号を検出した(短い入力で住所やマイナンバーを取りこぼした他者の検証もある)。全角数字の電話番号はNFKC正規化で、かな表記の氏名は追加学習などで補う必要がある
- 公式は「匿名化・コンプライアンス・安全性の保証ではない」と明記している。医療・金融・人事などでは人手レビューを残す前提で使う
Privacy Filterの仕様と公開状況
主な仕様は次のとおりです。数値はHugging Faceのモデルカード、config.json、公式モデルカードPDFで確認しました。
| 項目 | 内容 |
|---|---|
| 公開日 | 2026年4月22日(モデルカードの日付) |
| パラメータ数 | 総数1.5B/アクティブ50M |
| 構造 | Transformer 8層・幅640 |
| MoE | エキスパート128個・トークンごとに上位4個 |
| アテンション | GQA(クエリ14ヘッド・KV2ヘッド) |
| コンテキスト長 | 128,000トークン |
| 出力ラベル | 33クラス(8カテゴリ×4+背景) |
| ライセンス | Apache 2.0 |
| 配布 | Hugging Face openai/privacy-filter・GitHub |
パラメータの大半は128個のエキスパートに分かれていて、1トークンの処理で動くのは4個だけです。総数1.5Bに対してアクティブが50Mと小さいのはこのためで、ノートPCやブラウザでも動くと公式は説明しています。MoEの仕組みそのものはMoE(Mixture of Experts)の解説で扱っています。
公開から5か月以上たった2026年9月30日時点でも、配布物は初版のままです。Hugging Faceのリポジトリの最終更新は2026年4月22日で、GitHubにはリリースもタグもありません。パッケージのバージョンは0.1.0で、OpenAI自身も発表で「preview」と呼んでいます。一方でダウンロードは直近30日で334,109回、累計で約200万回に達しています。
検出できる8カテゴリとプレースホルダー表記
検出対象はモデルカードで定義された8カテゴリだけです。opfで置換すると、ラベル名を大文字にした<PRIVATE_PERSON>のような文字列に置き換わります。
| ラベル | 対象(モデルカードの定義) | 置換後の表記 |
|---|---|---|
| private_person | 個人の氏名、個人を特定するユーザー名・ハンドル | <PRIVATE_PERSON> |
| private_address | 個人に結びつく住所・場所 | <PRIVATE_ADDRESS> |
| private_email | 個人の連絡用、または個人を特定するメールアドレス | <PRIVATE_EMAIL> |
| private_phone | 個人に結びつく電話番号 | <PRIVATE_PHONE> |
| private_url | 非公開向け、または個人を特定するURL・IPアドレス | <PRIVATE_URL> |
| private_date | 生年月日・生まれ年など個人を特定する日時 | <PRIVATE_DATE> |
| account_number | クレジットカード番号、銀行口座番号などの識別子 | <ACCOUNT_NUMBER> |
| secret | APIキー、パスワードなどの認証情報 | <SECRET> |
カテゴリ名に「private」が付いているとおり、狙いは実在する個人に結びつく情報です。モデルカードは、サンプルのAPIキーのようなプレースホルダー値は検出対象に含めないと書いています。公開された組織の代表番号のように、個人と結びつかない情報を残す判断もモデルがします(実測の章で確かめています)。
ラベル体系は実行時に変えられません。社員番号や会員IDのような自社固有の識別子を独立したカテゴリとして扱いたい場合は、ファインチューニングで新しいラベル体系を学習させます。PIIという概念の範囲と日本の個人情報との違いは、PII(個人を特定できる情報)の定義と種類で整理しています。
仕組み:双方向トークン分類とViterbi復号
自己回帰モデルを分類器へ変換する2段階の学習
Privacy Filterは、まずgpt-ossと同系統の構造を小さくしたモデルを、文章生成と同じ自己回帰方式で事前学習しています。そのうえで、語彙を出力する層をプライバシーラベル用の分類層に付け替え、前後両方の文脈を見られるアテンションに変えてから、トークンごとのラベル分類を教師あり学習しました。
アテンションは前後128トークンずつの帯状で、自分を含めて257トークンの範囲を参照します。入力全体に1回のフォワードパスでラベルを付けるので、1トークンずつ生成するLLMより処理が速く済みます。出力は各トークンについて33個のロジットで、系列長Tなら[T, 33]の形になります。
33個の内訳は、8カテゴリそれぞれの開始(B-)・内部(I-)・終端(E-)・単独(S-)の4種と、背景のOです。このBIOES方式により、「山田」「太郎」のように複数トークンにまたがる氏名も1つのまとまりとして扱えます。
Viterbi復号と精度・再現率の調整
トークンごとに最も確率の高いラベルを選ぶ(argmax)だけだと、「B-の後にO」のような文法上ありえない並びや、途切れたスパンが出ます。Privacy Filterは、許されるラベル遷移だけを通る経路を系列全体で最適化する制約付きViterbi復号を使います。
遷移には6つのバイアス(背景の継続、スパン開始、スパン継続、スパン終了など)があり、値を変えると再現率寄りにも精度寄りにも動かせます。ただし配布されているviterbi_calibration.jsonに入っている動作点はdefaultだけで、6つの値はすべて0.0です。調整するには自分で評価データを用意して値を探すことになります。
注意したいのは、Viterbi復号がopf側の実装だという点です。Transformersに入ったモデル実装(modeling_openai_privacy_filter.py)はトークン分類の汎用クラスを使っていて、Viterbiの処理を含みません。Transformersのpipelineで得られる結果はargmaxベースです。実測では、この差が誤検出の有無として表れました。
使い方:opf CLI・Transformers・ONNXの3経路
公式CLI「opf」でのマスキング
GitHubリポジトリを取得してpip install -e .でインストールすると、opfコマンドが使えます。Python 3.10以上が必要です。モデルは初回実行時に~/.opf/privacy_filterへダウンロードされ、置き場所は環境変数OPF_CHECKPOINTで変えられます。
git clone https://github.com/openai/privacy-filter
cd privacy-filter
pip install -e .
# 既定はGPU(--device cuda)。GPUが無い環境では --device cpu を付ける
opf --device cpu "Ben Morgan lives at 12 3rd St. Call him at 123 456 7890."
# <PRIVATE_PERSON> lives at <PRIVATE_ADDRESS>. Call him at <PRIVATE_PHONE>.
# ファイル単位・パイプ入力
opf --device cpu -f ./support_log.txt
cat ./app.log | opf --device cpu
# カテゴリを区別せず <REDACTED> に統一する
opf --device cpu --output-mode redacted "Ben Morgan lives at 12 3rd St."
--deviceの既定値はcudaで、CPUへ自動で切り替える処理はありません。CPUだけのサーバーでは、バッチに組み込むときに明示しておきます。また--device cpuでは文脈長の既定が4,096トークンに下がります(--n-ctxで変更できます)。--format jsonを付けると、検出スパンの位置(start・end)、ラベル、置換後テキストがschema_version: 1のJSONで返ります。パイプで渡した入力は1行ずつ別の入力として処理され、-fはファイル全体を1件として扱います。行をまたぐ住所などを拾うなら-fを使います。
Pythonから呼ぶ場合はOPFクラスを使います。
from opf import OPF
redactor = OPF(device="cpu", output_mode="typed")
result = redactor.redact("Alice was born on 1990-01-02.")
print(result.redacted_text) # 公式ドキュメントの出力例: <PRIVATE_PERSON> was born on <PRIVATE_DATE>.
for span in result.detected_spans:
print(span.label, span.start, span.end, span.text)
TransformersとTransformers.jsから呼ぶ方法
Hugging Face Transformersには、v5.6.0でPrivacy Filterのモデル実装が追加されました。それより前の版では読み込めません。
from transformers import pipeline
classifier = pipeline(task="token-classification", model="openai/privacy-filter")
print(classifier("My name is Alice Smith", aggregation_strategy="simple"))
ブラウザではTransformers.jsで{ device: "webgpu", dtype: "q4" }を指定する例がHugging Faceのモデルカードに載っています。どちらの経路も前章のとおりViterbi復号を通らないので、スパンの断片化や誤検出を減らしたい場合はopfを使うか、ロジットを取り出して自前で復号します。
Python版Transformersには、もう1つ注意点があります。v5.6.0のpipelineのソースでは、aggregation_strategyがB-とI-の接頭辞しか解釈しません。E-やS-の付いたトークンは別のグループとして扱われ、「Alice Smith」のような氏名が「Alice」と「Smith」の2件に分かれることがあります。Transformers.js側はB・I・E・Sの4種を解釈します。
ONNX量子化版をCPUで動かすときの注意点
Hugging Faceのリポジトリには、ONNX形式の変換版が5種類あります(fp32・fp16・8bit量子化・q4・q4f16)。4bit量子化のq4版は外部データを含めて約917MBで、fp32版(約5.6GB)の6分の1ほどです。量子化で何が失われるかはモデル量子化の仕組みとPTQ・QATの違いを参照してください。
q4版はcom.microsoft:GatherBlockQuantizedという演算子を使います。onnxruntime 1.19.2ではこの演算子が未登録で読み込みに失敗し、1.23.2では動きました。Python 3.9環境で入る版は1.19系止まりなので、Python 3.10以上の環境を用意します。また、Transformers.js 4.3.0をIntel Mac(darwin x64)のNode.jsで動かそうとしたところ、onnxruntime-nodeのネイティブバイナリが無く起動できませんでした。
日本語テキストでの実測結果
Hugging Faceのモデルカードの言語欄は「主に英語」(Primarily English)です。日本語は合成データでの評価値しか公表されていないため、業務でありそうな日本語の文を実際に通しました。条件は次のとおりです。
- 実施日:2026年9月30日
- モデル:Hugging Faceのonnx/model_q4.onnx(リビジョン7ffa9a0)
- 実行環境:onnxruntime 1.23.2、Intel Core i9-9880H(CPUのみ)
- 復号:opfの
decoding.pyと同じ遷移制約のViterbi(バイアスは配布値の0.0)をnumpyで再実装。比較用にargmaxも出力
4bit量子化版での結果なので、元のbf16ウェイトとは細部が違う可能性があります。5〜42トークンの短文1件の推論は、初回の呼び出しを含めて0.04〜0.37秒でした。
| 入力 | 検出結果(Viterbi) |
|---|---|
| 山田太郎です。電話番号は090-1234-5678、メールはtaro.yamada@… | 氏名・電話・メールの3件 |
| 自宅の住所は大阪府大阪市北区梅田3丁目1-1 グランドハイツ502号室です。 | 住所全体を1件 |
| マイナンバーは1234 5678 9012です。 | account_number |
| カード番号は4980-1234-5678-9012です。 | account_number |
| 〒100-0001 東京都千代田区千代田1-1(文脈なし) | 検出なし |
| 問い合わせ者の住所:〒530-0001 大阪府大阪市… | 住所全体を1件 |
| 本番のAPIキーは sk-proj-9fKq…(35文字) | secret |
| お客様の佐藤花子様(生年月日1985年4月12日) | 「佐藤花子様」と「生年月日1985年4月12日)」 |
| 振込先は三菱UFJ銀行 新宿支店 普通口座 1234567 | 全体をprivate_personと誤分類 |
| やまだ たろう さんの携帯は 090ー1234ー5678 | 検出なし |
| 同上をNFKC正規化(090ー1234ー5678) | 電話のみ検出 |
| 東京都庁の代表番号は03-5321-1111です。 | 検出なし |
| 山田さんの自宅の電話は03-5321-1111です。 | private_phone |
全角数字とかな表記の氏名の検出漏れ
いちばん影響が大きいのは、全角数字の電話番号「090ー1234ー5678」が素通りした点です。同じ文をNFKC正規化して半角にすると検出されました。漢字の氏名と全角の電話番号を組み合わせた文でも、氏名だけが検出されて電話番号は残りました。opfのソースを確認したところ、入力を正規化する処理は入っていません。日本語の入力フォームや帳票から来たテキストは、unicodedata.normalize("NFKC", text)を通してからモデルに渡してください。
もう1つは、ひらがな・カタカナの氏名です。「やまだ たろう さん」「ヤマダ タロウ様」はどちらも氏名として検出されず、同じ文の電話番号や口座番号だけが拾われました。コールセンターの聞き取りメモや振込名義(カナ)のように、かな表記の氏名が多いデータには、このままでは使えません。自社データでの評価とファインチューニングが前提になります。
カテゴリと境界のずれ
検出はできても、ラベルや範囲がずれる例がありました。銀行名・支店名・口座番号の並びは、全体がprivate_personに分類されています。マスキングの目的なら結果的に隠れますが、カテゴリ別に件数を集計したり、口座番号だけを別処理に回したりする設計では誤りになります。
範囲については、敬称の「様」「さん」や、直後の「(営業部)」まで氏名スパンに含まれる例がありました。生年月日の例では、見出し語の「生年月日」と閉じ括弧まで日付に含まれています。モデルカードも故障モードとして、混在形式でのスパン境界のずれを挙げています(敬称については、敬称の多い表記での取りこぼしを挙げています)。
同じ電話番号の文脈による公私判定
03-5321-1111という同じ番号が、「東京都庁の代表番号」では検出されず、「山田さんの自宅の電話」ではprivate_phoneになりました。電話番号の形式だけを見る正規表現には出せない挙動で、公開情報を残すというモデルカードの設計どおりです。
ただし、都庁の文をargmaxで復号すると、先頭の「03」だけがaccount_numberとして切り出されました。Viterbi復号ではこの断片が消えています。前章で触れた「Transformersのpipelineはargmaxベース」という差が、実際の誤検出として出た例です。
文脈の有無で結果が変わる点は住所でも同じでした。個人との結びつきを示す語の無い「〒100-0001 東京都千代田区千代田1-1」を単独で渡すと検出されず、「問い合わせ者の住所:」を前に付けた別の住所は郵便番号ごと検出されています。2026年4月25日にZennで公開された別の検証では、郵便番号・住所・マイナンバー、クレジットカード番号4形式、文中の「田中花子さん」がいずれも未検出でした。この記事の実測ではカード番号も「田中花子さん」も検出できており、入力の形や文脈、実行経路で結果が変わります。自社のデータで確かめてから使ってください。
公式ベンチマークと第三者評価の数値
OpenAIがモデルカードで公表している主な数値は次のとおりです(F1はトークン単位)。
| 評価 | 適合率 | 再現率 | F1 |
|---|---|---|---|
| PII-Masking-300k | 0.940 | 0.980 | 0.960 |
| PII-Masking-300k(ラベル補正後) | 0.968 | 0.981 | 0.974 |
| CredData(secretのみ) | 0.747 | 0.965 | 0.842 |
| SPY(医療・法律、追加学習なし) | 0.414 | 0.795 | 0.545 |
| SPY(訓練データの10%で追加学習) | 0.963 | 0.960 | 0.962 |
| 日本語の合成データ(968件) | 0.897 | 0.866 | 0.881 |
「F1 96%」として報じられた数値は、英語や欧州言語が中心のPII-Masking-300kでのものです。日本語は、PII-Masking-300kに含まれない言語向けに作った合成データでの評価で、F1は0.881でした。PIIの種類を示す手がかり語が値の直前にある日本語データ(1,189件)では、適合率0.900に対して再現率0.758まで下がります。医療・法律の相談文(SPY)は追加学習なしだとF1 0.545で、1%の追加学習で0.879、10%で0.962まで上がっています。
OpenAI以外による評価としては、2026年8月にarXivで改訂版が公開された論文(arXiv:2608.02616)があります。32のデータセットで比較した結果、Microsoft PresidioのF1が合成PIIデータのAI4Privacyで0.431、医療のSPYで0.273だったのに対し、Privacy Filterはそれぞれ0.855と0.464でした。一方で、PII以外の一般的な固有表現データでは大きく落ちます。そのうえ、ドメインによってはラベル付き約500件で追加学習したXLM-RoBERTaに抜かれると報告しています。また、全体に再現率寄りで適合率は0.31〜0.54にとどまり、医療のSPYではGLiNER(0.498)やGPT-4o(0.702)が上回ったとも報告しています。著者はフロリダ州立大学の研究者で、日本語は評価していません。
Presidio・正規表現との使い分け
Privacy Filterの利点は、正規表現では拾えない氏名・住所を文脈から拾える点です。ただし成績そのものは、形式の決まった項目のほうが高く出ます。第三者論文の誤り分析では、メール(F1 0.78)や電話(0.76)に比べて氏名(0.40)と住所(0.49)が低く、モデルカードのストレステストでも住所の再現率は0.214〜0.414と8カテゴリで最も低い値でした。
マイナンバーや郵便番号のように形式が決まっている項目は、正規表現で形式を拾うルールベースのほうが確実です。マイナンバーならチェックデジットの検証もコードで加えられます。実測でもマイナンバーはaccount_numberとして検出できましたが、モデルは番号として正しいか(チェックデジット)までは見ていません。
日本語の業務テキストに入れるなら、次の順で組むのが現実的です。
- NFKC正規化で全角英数字・記号を半角にそろえる
- 形式が決まった識別子(マイナンバー、クレジットカード番号、郵便番号)を正規表現で置換する
- 残りのテキストをPrivacy Filterに通し、氏名・住所・私用連絡先を検出する
- 医療・金融・人事などのデータは、人がマスキング結果を確認してから次工程へ渡す
Presidioには画像向け(presidio-image-redactor)や表形式データ向け(presidio-structured)のモジュールがありますが、Privacy Filterが扱うのはテキストだけです。既にPresidioで基盤を組んでいる場合は置き換えず、Privacy Filterをカスタムの検出器として追加するほうが、移行の手戻りが少なく済みます。
採用を見送るべきなのは、かな表記の氏名が中心のデータを追加学習なしで処理する場合と、マスキング結果を「匿名化済み」として社外へ出す場合です。後者について、モデルカードは匿名化やコンプライアンスの保証として扱うことを想定外の使い方に挙げています。個人情報保護法上の匿名加工情報や仮名加工情報は、加工基準と安全管理措置で決まります。モデルが氏名を置換したことだけでは、どちらの要件も満たしません。
ファインチューニングで自社のラベルに合わせる手順
opfには追加学習の機能が入っています。学習データはopf evalと同じ形式のJSONLで、テキストとラベル付きスパンを持たせます。
# 検証データを分けて追加学習
opf train ./train.jsonl \
--validation-dataset ./validation.jsonl \
--output-dir ./finetuned_checkpoint
# 独自のラベル体系で学習(先頭は必ず "O")
opf train ./train.jsonl \
--validation-dataset ./validation.jsonl \
--label-space-json ./custom_label_space.json \
--output-dir ./finetuned_checkpoint
# custom_label_space.json の例
# {"category_version": "custom_v1", "span_class_names": ["O", "custom_account_id", "custom_secret"]}
出力先にはconfig.json、model.safetensors、finetune_summary.json、USAGE.txtが書き出されます。ラベル体系が自社データと違うまま精度を測りたいときは、opf eval --eval-mode untypedでカテゴリ名を無視したスパン単位の評価ができます。
前章のSPYの結果では、訓練データの1%でF1が0.545から0.879に上がりました。かな氏名のように取りこぼしが偏っている場合は、まず数百件規模のラベル付きデータで効果を確かめるのが手堅い進め方です。OpenAIも社内では、公的な住所と私的な住所を区別するラベルで追加学習した版を使っていると書いています。LLMの追加学習全般の考え方はファインチューニングの仕組みとRAGとの違いで解説しています。
高リスク領域での限界と運用設計
モデルカードは、医療・法務・金融・人事・教育・行政の業務では追加の注意が必要だとしています。見落としは機微情報の露出に、過剰なマスキングは審査や監査に必要な文脈の欠落につながり、どちらの誤りもコストになるからです。
モデルカードの故障モードとストレステストから、運用設計で手当てすべき点を挙げます。
- 珍しい氏名、地域特有の命名、イニシャル、敬称の多い表記は取りこぼしやすい
- 改行で分割されたURLなどは、断片が氏名と誤判定される(行をつなげてから渡す)
- 「後で言う『marigold』は口座番号」のように、離れた位置で定義された別名の値は再現率が落ちる
- ハッシュ値やサンプルの認証情報など、ランダムに見える文字列を過剰にマスキングすることがある
運用では、検出結果をそのまま捨てずにスパン位置とラベルをログへ残し、定期的に抜き取りで見逃しを確認します。OpenAIも推奨事項として、本番投入前に自社のポリシーに照らしてドメイン内で評価すること、ポリシーが異なる部分はファインチューニングすること、高感度の業務では人手レビューの経路を残すことを挙げています。
よくある質問
OpenAI Privacy Filterは日本語に対応していますか?
動作はしますが、公式の対応言語は「主に英語」です。日本語の合成データでのF1は0.881と公表されています。実測では漢字の氏名や半角の電話番号は検出できた一方、全角数字の電話番号とかな表記の氏名は検出されませんでした。入力前のNFKC正規化と、自社データでの評価が必要です。
無料で商用利用できますか?
できます。ライセンスはApache 2.0で、OpenAIの発表も試験・カスタマイズ・商用展開を想定用途に挙げています。ウェイトをダウンロードして自社環境で動かすため、利用料金はかかりません。
<PRIVATE_PERSON>や<PRIVATE_PHONE>という表記は何ですか?
Privacy Filterの公式CLI「opf」が、検出した個人情報を置き換えるときに使う文字列です。ラベル名を大文字にして山括弧で囲んだ形で、氏名なら<PRIVATE_PERSON>、住所なら<PRIVATE_ADDRESS>になります。--output-mode redactedを指定すると、すべて<REDACTED>に統一されます。
GPUが無くても動きますか?
動きます。opfは既定でGPUを使うため、CPUで動かすときは--device cpuを指定します。ONNXの4bit量子化版をCPU(Intel Core i9-9880H)で動かした実測では、短文1件の推論は0.04〜0.37秒(初回の呼び出しを含む)でした。
ChatGPTやOpenAI APIから使えますか?
いいえ。Privacy Filterは、ウェイトとコードをHugging FaceとGitHubで配布する形で提供されています。インストール前に挙動を見るなら、Hugging Face上のデモ(openai/privacy-filterのSpace)が使えます。手元の環境にダウンロードして実行するもので、OpenAIのAPIキーは要りません。クラウドへ送る前のテキストから個人情報を取り除く用途を想定した設計です。