CoolFace
Modelpublic

OpenMed/privacy-filter-nemotron-v2-mlx

sourceHugging Faceotherupdated 3mo agoView on Hugging Face
1likes29downloads
Model Card

OpenMed Privacy Filter Nemotron v2 - MLX BF16

A native MLX port of `OpenMed/privacy-filter-nemotron-v2` for Apple Silicon PII detection and de-identification with OpenMed. This is the unquantized BF16 reference artifact. For the 8-bit sibling, see `OpenMed/privacy-filter-nemotron-v2-mlx-8bit`.

Family at a glance: - PyTorch source: `OpenMed/privacy-filter-nemotron-v2` - MLX BF16 (this repo): Apple Silicon, full precision, 2.6 GiB weights - MLX 8-bit: `OpenMed/privacy-filter-nemotron-v2-mlx-8bit` - Apple Silicon, 1.4 GiB weights

At a glance

  • Source checkpoint: `OpenMed/privacy-filter-nemotron-v2`
  • OpenMed MLX repo: `OpenMed/privacy-filter-nemotron-v2-mlx`
  • Label schema: 55 fine-grained Nemotron-style PII categories
  • Output space: 221 BIOES classes (O plus B/I/E/S for each category)
  • Languages: 20-language metadata from the source card: bg, cs, da, de, el, en, es, et, fi, fr, hr, hu, it, lt, lv, nl, pl, pt, ro, sk
  • Weight format: safetensors
  • Quantization: none (BF16 reference)

Q8 sibling validation

The 8-bit sibling was compared against this BF16 artifact on 10 golden PII samples. Decoded entity spans matched across all samples. Average Q8/BF16 argmax agreement was 99.55% with average logit MAE 0.1769; average local forward time was 14.2 ms for BF16 vs 7.9 ms for Q8.

What it does

This model is an MLX packaging of `OpenMed/privacy-filter-nemotron-v2`, the second-generation Nemotron-schema checkpoint with a broader training mix and a more recall-oriented adaptation recipe than the first Nemotron branch. It uses OpenAI's Privacy Filter architecture and predicts 221 BIOES classes (O plus B/I/E/S for each category). The OpenMed PrivacyFilterMLXPipeline runs BIOES-aware Viterbi decoding so callers receive grouped spans instead of raw token tags.

Label coverage highlights:

  • Identity and demographics: firstname, lastname, age, gender, nationality, language
  • Contact and address: email, phonenumber, streetaddress, city, country, postcode
  • Government and regulated IDs: ssn, nationalid, taxid, certificatelicensenumber
  • Financial and secrets: accountnumber, creditdebitcard, cvv, pin, password, swiftbic
  • Medical, workplace, online, vehicle, and time identifiers such as medicalrecordnumber, employeeid, ipv4, url, licenseplate, date, and time

The full label map is included in id2label.json.

Architecture

FieldValue
Source model typeopenai_privacy_filter
Source architectureOpenAIPrivacyFilterForTokenClassification
Hidden size640
Transformer layers8
AttentionGrouped-query attention (14 query heads / 2 KV heads, head_dim=64) with attention sinks
FFNSparse Mixture-of-Experts - 128 experts, top-4 routing, SwiGLU
Position encodingYARN-scaled RoPE (rope_theta=150000, factor=32)
Context length131,072 tokens (initial 4,096)
Tokenizero200k_base / tiktoken-compatible tokenizer assets, vocab 200,064
Output headLinear(640 -> 221) with bias

File set

FileSizePurpose
weights.safetensors2.6 GiBMLX weights
config.json19.5 KiBModel and OpenMed MLX runtime config
id2label.json5.4 KiBNumeric ID to BIOES label mapping
openmed-mlx.json0.7 KiBOpenMed MLX artifact manifest
tokenizer.json27 MiBTokenizer asset kept with the artifact
tokenizer_config.json0.2 KiBTokenizer metadata

The MLX runtime uses the tiktoken-compatible o200k_base tokenizer path. tokenizer.json and tokenizer_config.json are bundled so consumers can inspect the tokenizer assets and keep the artifact self-contained.

Quick start

With OpenMed

bash
pip install -U "openmed[mlx]"
python
from openmed import extract_pii, deidentify
from openmed.core import OpenMedConfig

model_name = "OpenMed/privacy-filter-nemotron-v2-mlx"
text = (
    "Patient Sarah Johnson (DOB 03/15/1985), MRN 4872910, "
    "phone 415-555-0123, email sarah.johnson@example.com."
)

result = extract_pii(
    text,
    model_name=model_name,
    config=OpenMedConfig(backend="mlx"),
)
for ent in result.entities:
    print(ent.label, ent.text, round(ent.confidence, 4))

masked = deidentify(
    text,
    method="mask",
    model_name=model_name,
    config=OpenMedConfig(backend="mlx"),
)
print(masked.deidentified_text)

For non-MLX hosts, use the source PyTorch checkpoint `OpenMed/privacy-filter-nemotron-v2`.

Direct MLX usage

python
from huggingface_hub import snapshot_download
from openmed.mlx.inference import PrivacyFilterMLXPipeline

model_path = snapshot_download("OpenMed/privacy-filter-nemotron-v2-mlx")
pipe = PrivacyFilterMLXPipeline(model_path)

print(pipe("Email me at alice.smith@example.com after 5pm."))

Loading from a local snapshot

python
from openmed.mlx.models import load_model
import mlx.core as mx

model = load_model("/path/to/privacy-filter-nemotron-v2-mlx")
ids = mx.array([[1, 100, 200, 300]], dtype=mx.int32)
mask = mx.ones((1, 4), dtype=mx.bool_)
logits = model(ids, attention_mask=mask)
print(logits.shape)

Hardware notes

  • Designed for Apple Silicon with MLX.
  • CPU inference may work, but GPU-backed MLX on M-series Macs is the intended runtime.
  • The Python package path is pip install -U "openmed[mlx]".

Credits

This artifact builds on:

License

The source checkpoint model card currently declares license: other; this MLX packaging follows that source license metadata. Review the source model card before redistribution.