- Ajout documentation VLM (Ollama qwen2.5vl:7b) - Pipeline complet: Regex → VLM → EDS-Pseudo → CamemBERT → Contextuel - Nouvelles exigences REQ-013/REQ-014 pour optimisation VLM - Tâches Phase 2.5: amélioration prompt, validation croisée, perf - Document ARCHITECTURE_REELLE.md avec détails complets - Matériel: AMD Ryzen 9 9950X, 128GB RAM, RTX 5070 12GB - Objectifs: Rappel ≥99.5%, Précision ≥97%, F1 ≥0.98
11 KiB
Architecture Réelle du Système d'Anonymisation
Vue d'Ensemble
Le système d'anonymisation combine 5 couches de détection pour garantir une couverture maximale des PII dans les documents médicaux PDF.
1. Extraction de Texte (5 passes)
Méthodes d'extraction (ordre de priorité)
- pdfplumber : Extraction textuelle native (PDF textuels)
- pdfminer : Extraction alternative avec LAParams
- PyMuPDF : Fallback si artefacts
(cid:xx)détectés - docTR OCR : OCR deep learning pour PDF scannés (parallèle avec tesseract)
- tesseract OCR : OCR complémentaire
OCR Word Map
Pour les PDF scannés, génération d'une carte de mots avec coordonnées normalisées (0→1) :
OcrWordMap = Dict[int, List[Tuple[str, float, float, float, float]]]
# {page_idx: [(word_text, x0_norm, y0_norm, x1_norm, y1_norm), ...]}
Utilisée pour :
- Matching flou des identifiants numériques manuscrits (VLM)
- Redaction raster précise
2. Détection PII (5 couches)
2.1 Regex (Couche 1)
Fichier : anonymizer_core_refactored_onnx.py
PII détectés :
- EMAIL, TEL (formats fragmentés), IBAN, NIR (avec validation clé modulo 97)
- IPP, FINESS, RPPS, OGC
- Dates (multiples formats), dates de naissance
- Adresses postales, codes postaux, BP
- Âges, numéros de dossier, épisodes
- Établissements de santé, services hospitaliers
Patterns contextuels :
Dr. NOM,Patient: NOM,Rédigé par NOM- Listes virgulées :
Dr. X, Y, DUPONT - Champs structurés Trackare
Configuration : config/dictionnaires.yml
- Listes blanches (sections_titres, noms_maj_excepts)
- Force-mask (termes/regex à masquer systématiquement)
- Regex overrides personnalisées
2.2 VLM - Vision Language Model (Couche 2)
Fichier : vlm_manager.py
Modèle : Ollama avec qwen2.5vl:7b (ou qwen3-vl:8b)
Fonctionnement :
- Conversion de chaque page PDF en image (150 DPI)
- Redimensionnement si > 2048px (côté le plus long)
- Envoi à Ollama avec prompt structuré
- Parsing de la réponse JSON (avec réparation si tronquée)
20+ catégories détectées :
- Identité : NOM, PRENOM, DATE_NAISSANCE, NIR, AGE
- Contact : ADRESSE, TELEPHONE, EMAIL, CODE_POSTAL, VILLE
- Identifiants médicaux : IPP, NDA, RPPS, NUMERO_PATIENT, NUMERO_LOT, NUMERO_ORDONNANCE, NUMERO_SEJOUR
- Contexte : ETABLISSEMENT, SERVICE, DATE
Gestion des pages manuscrites :
- Si < 100 mots OCR détectés → masquage total de la page
- Évite les hallucinations du VLM sur pages complexes
Matching flou :
- Pour identifiants numériques manuscrits
- Compare séquences de chiffres (ratio ≥ 0.7)
- Utilise l'OCR word map pour localisation
Configuration :
@dataclass
class VlmConfig:
base_url: str = "http://localhost:11434"
model: str = "qwen2.5vl:7b"
timeout: int = 180
max_image_size: int = 2048
temperature: float = 0.1
num_predict: int = 8192
min_confidence: float = 0.5
Prompt système :
Tu identifies les données personnelles et identifiants traçables dans les documents
médicaux français. Réponds uniquement en JSON.
Dégradation gracieuse : Si Ollama indisponible, le pipeline continue sans VLM.
2.3 NER - Named Entity Recognition (Couche 3)
Option A : EDS-Pseudo (recommandé)
Fichier : eds_pseudo_manager.py
Modèle : AP-HP/eds-pseudo-public via edsnlp
Performance : F1=0.97 sur données cliniques AP-HP (11M documents)
13 labels détectés :
- NOM, PRENOM, MAIL, TEL, SECU (NIR)
- ADRESSE, ZIP, VILLE, HOPITAL
- DATE, DATE_NAISSANCE, IPP, NDA
Mapping vers PLACEHOLDERS :
EDS_LABEL_MAP = {
"NOM": "NOM",
"PRENOM": "NOM",
"MAIL": "EMAIL",
"TEL": "TEL",
"SECU": "NIR",
# ...
}
Option B : CamemBERT-NER (fallback)
Fichier : ner_manager_onnx.py
Modèles :
cmarkea/distilcamembert-base-ner(rapide, quantifié)Jean-Baptiste/camembert-ner(robuste)
Runtime : ONNX avec optimum.onnxruntime
Tags supportés : PER, LOC, ORG, DATE
Seuils de confiance :
@dataclass
class NerThresholds:
per: float = 0.90
org: float = 0.90
loc: float = 0.90
date: float = 0.85
2.4 Extraction Trackare (Couche 4)
Fonction : _extract_trackare_identity()
Champs structurés extraits :
- Nom de naissance, Nom et Prénom
- Lieu de naissance, Ville de résidence
- Contacts (Conjoint, Parent, Époux, etc.)
- Personnel médical (Rédigé par, Aide:, IDE:, etc.)
- Pattern multi-lignes :
Prenom\nNOM
Regex spécialisées :
RE_EXTRACT_PATIENT,RE_EXTRACT_NOM_NAISSANCERE_EXTRACT_CONTACT,RE_EXTRACT_STAFF_ROLERE_EXTRACT_DR_DEST,RE_EXTRACT_PR
2.5 Détection Contextuelle (Couche 5)
Patterns :
- Noms après titres :
Dr. DUPONT,Pr. MARTIN - Noms en MAJUSCULES (hors stopwords médicaux)
- Listes virgulées :
le Dr X, Y, LAZARO
Stopwords médicaux : ~4000 termes
- Médicaments (edsnlp drugs.json)
- Termes médicaux, anatomie, pathologies
- Abréviations, services hospitaliers
- Mots courants français
3. Consolidation Globale
Fonction : _apply_global_pii_tokens()
Propagation des PII
Chaque PII détecté est propagé sur toutes les pages du document :
Types propagés globalement :
NOM_GLOBAL: tous les tokens de nomsTEL_GLOBAL,EMAIL_GLOBAL,ADRESSE_GLOBALCODE_POSTAL_GLOBAL,VILLE_GLOBAL,ETAB_GLOBALVLM_SERVICE,VLM_ETAB,DATE_NAISSANCE
Noms compagnons
Détection de mots en MAJUSCULES adjacents à un nom connu :
# Si "DUPONT" est détecté, et "JEAN-PIERRE DUPONT" apparaît
# → "JEAN-PIERRE" est ajouté comme nom compagnon
Noms composés
Traitement en bloc : JEAN-PIERRE, CAZELLES-BOUDIER
Rescan sélectif
Rescan des PII critiques ayant pu échapper au premier passage :
- EMAIL, TEL, IBAN, NIR
- Téléphones fragmentés sur plusieurs lignes
- Codes postaux orphelins
4. Redaction PDF
4.1 Vector Redaction (PDF textuels)
Méthode : page.search_for() (PyMuPDF)
- Pour chaque PII dans l'audit
- Recherche du texte exact dans la page
- Si non trouvé et PII avec espaces → recherche mot par mot
- Génération de rectangles de redaction
- Application des rectangles (texte remplacé par noir)
4.2 Raster Redaction (PDF scannés)
Méthode : Conversion en image + redaction pixel
- Conversion de chaque page en pixmap (DPI configurable)
- Matching des PII via OCR word map
- Matching flou pour VLM : identifiants numériques manuscrits
- Dessin de rectangles noirs sur les zones PII
- Reconstruction du PDF depuis les images
Matching flou VLM :
def _search_ocr_words_fuzzy_digits(ocr_words, token, page_rect, min_ratio=0.7):
"""Compare les séquences de chiffres entre le token VLM et les mots OCR.
Accepte une correspondance si ≥ min_ratio des chiffres matchent."""
Appliqué aux catégories :
VLM_NUM_PATIENT,VLM_NUM_LOT,VLM_NUM_ORDVLM_NDA,VLM_NIR,VLM_IPP,VLM_RPPS
5. Audit Trail
Format : .audit.jsonl (JSON Lines)
Structure :
@dataclass
class PiiHit:
page: int
kind: str # Type de PII (EMAIL, NOM, VLM_TEL, etc.)
original: str # Texte original détecté
placeholder: str # Placeholder de remplacement
bbox_hint: Optional[Tuple[float, float, float, float]] # Coordonnées (optionnel)
Traçabilité :
- Chaque PII détecté est enregistré avec sa méthode de détection
- Permet l'analyse post-traitement
- Utilisé pour la validation post-anonymisation
6. Matériel et Performance
Configuration actuelle
- CPU : AMD Ryzen 9 9950X (16 cœurs / 32 threads)
- RAM : 128 GB
- GPU : NVIDIA RTX 5070 (12 GB VRAM)
- CUDA : PyTorch 2.10.0 avec support CUDA
Temps de traitement
PDF textuels :
- Extraction : < 1s
- Détection (Regex + NER GPU) : 2-5s
- Redaction : 1-2s
- Total : < 10s par PDF
PDF scannés :
- Extraction + OCR : 5-15s
- VLM (Ollama) : 10-30s par page (dépend du modèle)
- Détection (Regex + NER GPU) : 2-5s
- Redaction raster : 3-10s
- Total : 20-60s par PDF (selon nombre de pages)
Optimisations possibles
- VLM : Vérifier support GPU Ollama (CUDA)
- NER : Batch processing optimisé (12 GB VRAM)
- Parallélisation : 8-12 workers sur 16 cœurs
- Cache : Résultats VLM par hash d'image
7. Points d'Attention
Hallucinations VLM
Le VLM peut détecter des PII inexistants, surtout sur :
- Pages manuscrites complexes
- Documents mal orientés
- Tableaux denses
Mitigation actuelle :
- Masquage total si < 100 mots OCR
- Seuil de confiance (0.5)
Amélioration proposée :
- Validation croisée VLM ↔ NER
- Prompt amélioré avec exemples négatifs
- Rejet des détections non confirmées
Faux Positifs NER
EDS-Pseudo peut détecter comme NOM :
- Médicaments (ex: "Eliquis", "Trulicity")
- Termes médicaux (ex: "Diabétique", "Cutanée")
- Mots courants (ex: "Toilette", "Repas")
Mitigation : Stopwords médicaux (~4000 termes)
Performance VLM
Ollama en local peut être lent (10-30s par page).
Solutions :
- Vérifier support GPU
- Réduire
max_image_size(trade-off qualité/vitesse) - Cache des résultats
- Traitement parallèle (attention : charge GPU)
8. Dépendances Complètes
# Core
python>=3.12
pyyaml
# PDF
pymupdf
pdfplumber
pdfminer.six
pillow
# OCR
doctr[torch]
pytesseract
# NER
edsnlp[ml]>=0.12.0 # EDS-Pseudo
transformers
optimum
onnxruntime # ou onnxruntime-gpu
sentencepiece
# VLM
# Aucune dépendance Python (utilise urllib)
# Nécessite Ollama installé localement : https://ollama.ai/
# Tests & Qualité (nouveaux)
pytest
pytest-cov
pydantic
structlog
jinja2
matplotlib
9. Configuration Ollama
Installation
# Linux
curl -fsSL https://ollama.ai/install.sh | sh
# macOS
brew install ollama
# Windows
# Télécharger depuis https://ollama.ai/download
Téléchargement du modèle
ollama pull qwen2.5vl:7b
# ou
ollama pull qwen3-vl:8b
Vérification
# Lister les modèles disponibles
ollama list
# Tester le modèle
ollama run qwen2.5vl:7b
Configuration GPU (optionnel)
Ollama détecte automatiquement le GPU CUDA si disponible.
Vérifier dans les logs :
ollama serve
# Chercher : "GPU detected: NVIDIA GeForce RTX 5070"
10. Workflow Complet
1. Chargement PDF
↓
2. Extraction texte (5 passes)
↓
3. Détection Regex
↓
4. VLM (si PDF scanné)
↓
5. NER (EDS-Pseudo ou CamemBERT)
↓
6. Extraction Trackare
↓
7. Détection contextuelle
↓
8. Consolidation globale
↓
9. Rescan sélectif
↓
10. Redaction PDF (vector ou raster)
↓
11. Génération audit.jsonl
↓
12. Validation post-anonymisation (à implémenter)
↓
13. Certificat de conformité (à implémenter)
Conclusion
Le système actuel est très sophistiqué avec 5 couches de détection complémentaires. Les principaux axes d'amélioration sont :
- Métriques de qualité : Créer un dataset annoté et mesurer Précision/Rappel/F1
- Validation VLM : Réduire les hallucinations via validation croisée
- Optimisation GPU : Accélérer NER et vérifier support GPU Ollama
- Tests de régression : Suite automatique pour éviter les régressions
- Validation post-anonymisation : Scanner de fuite automatique
La spécification mise à jour reflète maintenant cette architecture réelle.