Introduction
"Clinical data never leaves the device."
This is article #93 in the Open Source Project of the Day series. Today's project is OpenMed — a local-first healthcare AI library built by HuggingFace researcher Maziyar Panahi, designed specifically for clinical text processing without cloud data movement.
The standard healthcare AI workflow sends patient data to a cloud vendor and receives structured results back. That's a persistent compliance exposure point. HIPAA, GDPR, and national data protection laws place specific constraints on how patient data moves, and "sending it to the cloud" often hits hard limits for healthcare organizations before the conversation even starts.
OpenMed brings the processing on-device instead: over 1,000 biomedical NLP models that run locally, no network calls, no external API keys, deployable from Python services to iOS apps.
What You'll Learn
- Why OpenMed chose encoder transformers over generative LLMs for clinical tasks
- All 13 clinical NER domains: from disease detection to genomics
- The engineering design of PII de-identification: covering all 18 HIPAA Safe Harbor identifiers
- Multi-platform support: Python/MLX, Swift/OpenMedKit, Docker/FastAPI
- Zero-shot NER and relation extraction introduced in v1.2.0
- Performance on Apple Silicon vs CPU PyTorch
Prerequisites
- Familiarity with NLP basics (named entity recognition, Transformer models)
- Python experience
- Basic understanding of healthcare data privacy (HIPAA, data de-identification context)
Project Background
What Is OpenMed?
OpenMed is a local-first healthcare AI toolkit positioned as "turning clinical text into structured insight without your data leaving the secure environment."
The core capability is not generative AI — it's encoder Transformers (BERT, ELECTRA, DeBERTa families) doing extraction and classification. The paper (arXiv:2508.01630) reports state-of-the-art performance on 10 of 12 biomedical NER benchmarks.
Author / Team
- Author: Maziyar Panahi
- Background: HuggingFace researcher, biomedical NLP, contributor to spaCy and HuggingFace Transformers communities
- License: Apache-2.0
- Latest version: v1.5.5 (June 2026)
Project Stats
- ⭐ GitHub Stars: 2,800+
- 🍴 Forks: 274+
- 📦 HuggingFace models: 1,000+
- 🌍 Supported languages: 12
- 📄 License: Apache-2.0
Core Features
What It Does
Clinical text input
↓
Local model inference (BERT/ELECTRA/DeBERTa)
↓
┌──────────────────────────────────────────┐
│ NER: identify diseases, drugs, genes │
│ PII de-identification: detect and mask │
│ Relation extraction: entity semantics │
└──────────────────────────────────────────┘
↓
Structured output (data never left the device)
Use Cases
-
Clinical text structuring
- Extract disease names, medications, and anatomical locations from discharge notes and medical records
- 13 biomedical NER domains: chemicals, diseases, genes, proteins, species, anatomy, oncology, and more
-
Patient data de-identification
- All 18 HIPAA Safe Harbor identifiers covered
- Four redaction methods: mask (
[NAME]), replace (Faker-backed surrogates), hash, date-shift
-
iOS and macOS medical app development
- OpenMedKit Swift package with native APIs — PHI never leaves the device
- The v1.2.0 iOS Scan Demo: a five-step clinical workflow — scan, OCR review, de-identification, clinical extraction, export
-
Enterprise healthcare system integration
- Docker/FastAPI REST API for embedding in existing workflows
- AWS SageMaker Marketplace managed version with sub-100ms latency endpoints
Quick Start
Install:
# CPU
pip install openmed
# Apple Silicon (MLX acceleration)
pip install openmed[mlx]
# CUDA GPU
pip install openmed[cuda]
Clinical NER:
from openmed import analyze_text
# Disease detection
result = analyze_text(
"Patient started on imatinib for CML.",
model_name="disease_detection_superclinical"
)
# {entities: [{text: "CML", label: "DISEASE", start: 30, end: 33}], ...}
# Drug detection
result = analyze_text(
"Prescribed metformin 500mg twice daily for type 2 diabetes.",
model_name="pharma_detection_superclinical"
)
PII De-identification:
from openmed import deidentify
text = "Patient John Smith (DOB: 1985-03-15, SSN: 123-45-6789) was admitted..."
# Masking
result = deidentify(text, method="mask")
# "Patient [NAME] (DOB: [DATE], SSN: [SSN]) was admitted..."
# Faker replacement (readable text, fully anonymized)
result = deidentify(text, method="replace")
# "Patient Michael Johnson (DOB: 1972-08-22, SSN: 987-65-4321) was admitted..."
Batch processing:
from openmed import BatchProcessor
processor = BatchProcessor(
operation="extract_pii",
model_name="pii_superclinical_large",
on_progress=lambda p: print(f"{p:.0%} complete")
)
results = processor.run([record1, record2, record3, ...])
Swift/iOS:
import OpenMedKit
let analyzer = OpenMedNER(model: .diseaseDetectionSuperClinical)
let result = try await analyzer.analyze("Patient presents with hypertension and T2DM")
// result.entities: [{text: "hypertension", label: "DISEASE"}, {text: "T2DM", label: "DISEASE"}]
Model Registry
| Model | Domain | Parameters | HuggingFace Downloads |
|---|---|---|---|
disease_detection_superclinical |
Diseases/conditions | 434M | 104K |
pharma_detection_superclinical |
Drugs/compounds | 434M | — |
pii_superclinical_large |
PII identifiers | 434M | — |
chemical_detection_electramed |
Chemicals | 33M | 117K |
anatomy_detection_electramed |
Anatomy | 109M | — |
genomic_detection_pubmed |
Genes/genomics | 109M | 103K |
oncology_detection_multimed |
Oncology entities | 568M | 102K |
Deep Dive
Why Encoder Transformers, Not Generative LLMs
This is the most consequential technical choice in OpenMed's design.
Generative LLMs (GPT-4, Claude, etc.) have a fundamental problem in medical text tasks: their outputs are not deterministic. Ask a generative model to do PII detection and it might, in some inputs, reproduce a patient's name in its output, or hallucinate a drug name that doesn't appear in the source text. For clinical applications, that's not an acceptable failure mode.
Encoder-only transformers doing NER are a classification problem: assign each token a label from a fixed set. These models produce deterministic outputs for a given input, generate no new tokens (so no hallucination), run locally with parameter counts between 33M and 568M, and produce auditable results with explicit source positions. For healthcare work, those properties outweigh the ability to generate fluent prose.
Privacy Filter Engineering
OpenMed's PII detection is more than running a NER model. There are several layers on top:
Context-aware detection: Keyword boosting within a 100-character window around candidates. A number sequence following SSN: gets a higher confidence score than an identical number sequence with no label nearby.
Checksum validation: Reduces false positives for structured identifier formats.
- US SSN: format validation
- Indian Aadhaar: Verhoeff checksum algorithm
- Brazilian CPF/CNPJ: Luhn checksum
- Italian Codice Fiscale: format + character validation
- German Steuer-ID: format validation
Smart Entity Merging: Solves the subword tokenization fragmentation problem. BERT-family models split "O'Brien" into ["O", "'", "Brien"]. The entity merging logic reassembles these fragments into complete entities, preventing incomplete PII detection results.
Three Privacy Filter variants:
- Baseline: general-purpose PII detection
- Nemotron fine-tuned: higher precision
- Multilingual (v1.4.0): unified model across 16 languages
Multi-Platform Runtime
┌──────────────────────────────────────────────────────┐
│ OpenMed Runtime │
├────────────────┬─────────────────┬───────────────────┤
│ Python / MLX │ Swift │ Docker / FastAPI │
│ │ OpenMedKit │ │
│ • CPU │ • macOS │ • REST API │
│ • CUDA │ • iOS │ • Batch endpoints │
│ • Apple MLX │ • iPadOS │ • Model lifecycle │
│ │ │ /models/loaded │
│ 24-33x faster │ PHI stays on │ /models/unload │
│ vs CPU PyTorch│ device │ keep_alive │
└────────────────┴─────────────────┴───────────────────┘
↑ ↑
Shared MLX model files (including 8-bit variants)
The Swift and Python paths share the same MLX model files. A hospital system can run Python-based server inference while running OpenMedKit on-device on iPads using the same model artifacts, with no separate model maintenance.
Zero-Shot Capabilities (v1.2.0)
v1.2.0 introduced zero-shot interfaces that don't require pre-defined entity categories:
from openmed import zero_shot_ner
# Custom entity labels, not bound to pretrained NER categories
result = zero_shot_ner(
text="The patient's creatinine level was 2.3 mg/dL, suggesting CKD.",
labels=["LAB_VALUE", "UNIT", "CONDITION"]
)
# Identifies "2.3 mg/dL" as LAB_VALUE, "CKD" as CONDITION
from openmed import extract_relations
# Extract semantic relationships between entities
relations = extract_relations(
text="Metformin was prescribed for type 2 diabetes.",
entity_pairs=[("DRUG", "DISEASE")]
)
# [{drug: "Metformin", relation: "prescribed_for", disease: "type 2 diabetes"}]
For clinical entities outside the coverage of any pretrained NER model, zero-shot provides a flexible escape hatch.
Fine-Tuning Design
OpenMed's models use domain-adaptive pre-training (DAPT) combined with LoRA fine-tuning:
- Pre-training corpus: 350K biomedical passages
- LoRA: updates less than 1.5% of model parameters
- Training time: under 12 hours on a single GPU
- Carbon footprint: under 1.2 kg CO₂e for the full training run
For practitioners who want to fine-tune on their own data, these numbers are significant: no multi-GPU cluster required, a single consumer GPU and a few hours.
Version History
| Version | Date | Key Changes |
|---|---|---|
| v1.0.0 | Apr 2026 | First stable release, MLX backend, Swift package |
| v1.2.0 | Apr 2026 | Zero-shot NER/classification/relation extraction, iOS Scan Demo |
| v1.4.0 | May 2026 | Multilingual Privacy Filter, 16 languages |
| v1.5.0 | May 2026 | Arabic/Japanese/Turkish PII, 247 registered models |
| v1.5.2 | May 2026 | Security hardening, trust_remote_code defaults to False |
| v1.5.5 | Jun 2026 | Batch PII, REST model lifecycle management, 13 README translations |
Links and Resources
Official Resources
- 🌟 GitHub: maziyarpanahi/openmed
- 🚀 Starter tutorials: maziyarpanahi/openmed-starter
- 🌐 Website: openmed.life
- 🤖 Agent tool (preview): agent.openmed.life
- 📄 Paper: arXiv:2508.01630
- ☁️ AWS SageMaker: Marketplace managed deployment
Standards Referenced
- HIPAA Safe Harbor (18 patient identifiers)
- OWASP healthcare data security guidelines
- STRIDE threat modeling (Privacy Filter security design)
Conclusion
OpenMed addresses a problem with a specific shape: medical NLP, where data cannot move off the device.
That constraint is optional in many industries and legally mandatory in many healthcare contexts. OpenMed turns the constraint into an architecture: encoder models for deterministic classification, MLX for local acceleration, a Swift package for mobile native integration, checksum validation to reduce false positives, Smart Entity Merging to handle tokenization fragments. Each layer addresses a real engineering problem that shows up in clinical text processing.
For developers building healthcare AI applications, or researchers working on clinical text, OpenMed is among the most complete local-first options in the current open-source ecosystem. The PII de-identification capabilities and multilingual support also have direct relevance outside healthcare — anywhere sensitive data requires processing without cloud movement: finance, legal, insurance, public records.
Explore PrimeSkills — A marketplace for handpicked AI Agents and skills. Each is validated in real enterprise workflows, stripping away hype and keeping only what truly works.
Welcome to my Homepage for more useful insights and interesting products.
Top comments (0)