# Design - Optimisation de la Qualité d'Anonymisation ## 1. Vue d'Ensemble de l'Architecture ### 1.1 Architecture Actuelle (Baseline) ``` ┌─────────────────────────────────────────────────────────────┐ │ PDF d'entrée │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ EXTRACTION DE TEXTE (5 passes) │ │ pdfplumber → pdfminer → PyMuPDF → docTR OCR → tesseract │ │ + OCR word map (coordonnées normalisées 0→1) │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ DÉTECTION PII (4 couches) │ │ ├─ 1. Regex (EMAIL, TEL, NIR, IBAN, etc.) │ │ ├─ 2. VLM Ollama (qwen2.5vl:7b) — si PDF scanné │ │ │ • Analyse visuelle de chaque page (150 DPI) │ │ │ • 20+ catégories PII (manuscrit, mal orienté) │ │ │ • Masquage total si < 100 mots OCR │ │ ├─ 3. NER EDS-Pseudo (13 labels, F1=0.97) │ │ │ ou CamemBERT-NER ONNX (fallback) │ │ └─ 4. Extraction Trackare (champs structurés) │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ CONSOLIDATION GLOBALE │ │ Propagation des PII sur toutes les pages │ │ + Noms compagnons + Rescan sélectif │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ REDACTION PDF │ │ • Vector (PyMuPDF search_for) │ │ • Raster (OCR word map + matching flou pour VLM) │ │ Génération PDF raster + audit.jsonl │ └─────────────────────────────────────────────────────────────┘ ``` **Problèmes identifiés** : - ❌ Pas de validation post-anonymisation - ❌ Pas de métriques de qualité - ❌ VLM peut halluciner (pages manuscrites complexes) - ❌ Pas d'optimisation GPU pour VLM - ❌ Pas de tests de régression --- ### 1.2 Architecture Cible (Optimisée) ``` ┌─────────────────────────────────────────────────────────────┐ │ PDF d'entrée │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ EXTRACTION DE TEXTE (inchangé) │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ DÉTECTION HYBRIDE (5 passes + améliorations) │ │ ├─ 1. Regex améliorées (formats structurés) │ │ ├─ 2. VLM optimisé (GPU, prompt amélioré, anti-hallucination) │ │ ├─ 3. EDS-Pseudo (noms, contexte médical) - GPU │ │ ├─ 4. CamemBERT-NER (fallback) - GPU │ │ └─ 5. Détection contextuelle renforcée │ │ │ │ → Masquage progressif pour éviter doublons │ │ → Fusion intelligente des résultats │ │ → Validation croisée VLM ↔ NER pour réduire hallucinations│ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ CONSOLIDATION GLOBALE (inchangé) │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ REDACTION PDF │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ VALIDATION POST-ANONYMISATION (NOUVEAU) │ │ ├─ Scanner de fuite PII │ │ ├─ Vérification métadonnées │ │ ├─ Détection hallucinations VLM (cross-check NER) │ │ └─ Certificat de conformité │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ÉVALUATION QUALITÉ (si ground truth disponible) │ │ ├─ Calcul Précision/Rappel/F1 par méthode │ │ ├─ Identification faux positifs/négatifs │ │ └─ Rapport de qualité │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 2. Composants Détaillés ### 2.1 Dataset de Test Annoté #### 2.1.1 Structure des Données **Format d'annotation** : ```json { "pdf_path": "OGC_257/FC14.pdf", "metadata": { "annotator": "expert_1", "annotation_date": "2024-01-15", "document_type": "compte_rendu_hospitalier", "page_count": 3, "difficulty": "medium" }, "annotations": [ { "id": "ann_001", "page": 1, "type": "NOM", "text": "DUPONT", "bbox": [100.5, 200.3, 150.2, 220.8], "context": "Dr. DUPONT a examiné le patient", "mandatory": true, "difficulty": "easy", "detection_method_expected": ["regex", "ner", "contextual"] } ], "medical_terms_to_preserve": [ "Médecin DIM", "Service de cardiologie", "Praticien conseil" ], "statistics": { "total_pii": 45, "by_type": { "NOM": 12, "PRENOM": 8, "TEL": 3, "EMAIL": 2, "DATE": 15, "ADRESSE": 5 } } } ``` #### 2.1.2 Outil d'Annotation **Composant** : `annotation_tool.py` **Fonctionnalités** : - Interface CLI pour annoter les PDF - Extraction automatique du texte + affichage - Saisie guidée des annotations - Validation du format JSON - Export au format standardisé **Workflow d'annotation** : 1. Sélectionner un PDF du corpus 2. Extraire et afficher le texte page par page 3. Pour chaque PII identifié : - Saisir le type (NOM, TEL, EMAIL, etc.) - Saisir le texte exact - Saisir le contexte (fenêtre de 50 caractères) - Marquer comme obligatoire/optionnel - Estimer la difficulté (easy/medium/hard) 4. Lister les termes médicaux à préserver 5. Sauvegarder l'annotation --- ### 2.2 Système d'Évaluation #### 2.2.1 Évaluateur de Qualité **Composant** : `quality_evaluator.py` **Classes principales** : ```python @dataclass class EvaluationResult: true_positives: int false_positives: int false_negatives: int precision: float recall: float f1_score: float missed_pii: List[Dict] # Faux négatifs détaillés false_detections: List[Dict] # Faux positifs détaillés by_type: Dict[str, Dict] # Métriques par type de PII class QualityEvaluator: def evaluate(self, pdf_path: Path, audit_path: Path) -> EvaluationResult def compare(self, annotations: List, detected: List) -> tuple def generate_report(self, results: List[EvaluationResult]) -> str ``` **Algorithme de comparaison** : 1. Charger les annotations manuelles (ground truth) 2. Charger l'audit généré (détections) 3. Normaliser les textes (lowercase, strip) 4. Créer des clés de comparaison : `(page, type, texte_normalisé)` 5. Calculer les intersections : - TP = annotations ∩ détections - FN = annotations - détections (PII manqués) - FP = détections - annotations (sur-détection) 6. Calculer les métriques 7. Générer le rapport détaillé --- #### 2.2.2 Scanner de Fuite **Composant** : `leak_scanner.py` **Classes principales** : ```python @dataclass class LeakReport: is_safe: bool leak_count: int leaks: List[Dict] severity_counts: Dict[str, int] class LeakScanner: def scan(self, anonymized_pdf: Path, original_audit: Path) -> LeakReport def scan_text(self, text: str, original_pii: List) -> List[Dict] def scan_metadata(self, pdf_path: Path) -> List[Dict] ``` **Algorithme de scan** : 1. Extraire le texte du PDF anonymisé 2. Charger les PII originaux depuis l'audit 3. Vérifier que les PII originaux ne sont plus présents 4. Détecter de nouveaux PII non masqués (regex + NER) 5. Scanner les métadonnées PDF 6. Classer les fuites par sévérité : - CRITIQUE : PII original présent - HAUTE : Nouveau PII détecté - MOYENNE : Métadonnée suspecte --- #### 2.2.3 Benchmark de Performance **Composant** : `benchmark.py` **Métriques collectées** : - Temps de traitement (total, par page, par étape) - Utilisation CPU (%) - Utilisation RAM (MB) - Métriques de qualité (Précision, Rappel, F1) - Nombre de PII détectés par type **Format de sortie** : ```json { "benchmark_date": "2024-01-15T10:30:00", "system_info": { "cpu": "Intel i7-9700K", "ram_gb": 16, "python_version": "3.12.0" }, "results": [ { "pdf_path": "OGC_257/FC14.pdf", "processing_time_s": 12.5, "time_per_page_s": 4.2, "cpu_usage_percent": 85.3, "ram_usage_mb": 1024, "pii_detected": 45, "quality_metrics": { "precision": 0.98, "recall": 0.995, "f1_score": 0.987 } } ], "summary": { "avg_time_per_doc": 15.2, "avg_precision": 0.975, "avg_recall": 0.992, "avg_f1": 0.983 } } ``` --- ### 2.3 Détecteurs Améliorés #### 2.3.1 Regex Améliorées **Composant** : `improved_regex_detector.py` **Améliorations par type** : **A) Téléphones** : ```python RE_TEL_IMPROVED = re.compile( r"(? List[PiiHit]: # Étape 1 : Regex (priorité haute) hits = self.regex_detector.detect(text, page) masked_text = self._mask_zones(text, hits) # Étape 2 : EDS-Pseudo if self.eds_pseudo.is_loaded(): eds_hits = self._detect_with_eds(masked_text, page) hits.extend(eds_hits) masked_text = self._mask_zones(masked_text, eds_hits) # Étape 3 : CamemBERT-NER if self.camembert_ner.is_loaded(): camembert_hits = self._detect_with_camembert(masked_text, page) hits.extend(camembert_hits) masked_text = self._mask_zones(masked_text, camembert_hits) # Étape 4 : Contextuel context_hits = self.contextual_detector.detect(masked_text, page) hits.extend(context_hits) # Fusion et dédoplication return self._merge_hits(hits) ``` **Stratégie de fusion** : - Dédupliquer par position (même zone = même PII) - En cas de conflit de type, garder le plus spécifique - Tracer la méthode de détection pour chaque PII --- ### 2.4 Validation Post-Anonymisation **Composant** : `post_validation.py` **Workflow** : ```python def validate_anonymized_pdf( anonymized_pdf: Path, original_audit: Path, strict_mode: bool = True ) -> ValidationResult: # 1. Scanner les fuites leak_report = leak_scanner.scan(anonymized_pdf, original_audit) # 2. Vérifier la lisibilité readability_score = check_readability(anonymized_pdf) # 3. Générer le certificat if leak_report.is_safe and readability_score > 0.7: certificate = generate_certificate(anonymized_pdf) return ValidationResult( is_valid=True, certificate=certificate, leak_report=leak_report ) else: if strict_mode: raise ValidationError("Document non conforme") return ValidationResult( is_valid=False, leak_report=leak_report ) ``` **Certificat de conformité** : ```json { "certificate_id": "CERT-2024-01-15-001", "pdf_path": "OGC_257/FC14.redacted_raster.pdf", "validation_date": "2024-01-15T10:30:00", "is_compliant": true, "checks": { "no_pii_leak": true, "metadata_clean": true, "readability_ok": true }, "validator": "LeakScanner v2.0", "signature": "sha256:abc123..." } ``` --- ### 2.5 Reporting **Composant** : `quality_reporter.py` **Rapport HTML** : - Vue d'ensemble (métriques globales) - Graphiques (répartition PII, évolution qualité) - Tableau détaillé par document - Liste des faux négatifs (si disponible) - Liste des faux positifs (si disponible) - Recommandations d'amélioration **Template Jinja2** : ```html Rapport de Qualité - Anonymisation

Rapport de Qualité d'Anonymisation

Vue d'Ensemble

{{ precision|round(3) }} Précision
{{ recall|round(3) }} Rappel
{{ f1_score|round(3) }} F1-Score
``` --- ## 3. Flux de Données ### 3.1 Flux d'Annotation ``` PDF réel → Extraction texte → Affichage CLI → Saisie annotations → Validation format → Sauvegarde JSON → Dataset annoté ``` ### 3.2 Flux d'Évaluation ``` PDF + Annotations → Anonymisation → Audit généré → Comparaison → Calcul métriques → Rapport qualité ``` ### 3.3 Flux de Validation ``` PDF anonymisé + Audit original → Scanner fuite → Vérification métadonnées → Check lisibilité → Certificat conformité (si OK) ``` --- ## 4. Décisions Techniques ### 4.1 Choix d'Implémentation **Langage** : Python 3.12 (existant) **Bibliothèques** : - `pytest` : Tests unitaires et de régression - `pydantic` : Validation des données (annotations, config) - `structlog` : Logging structuré - `jinja2` : Génération rapports HTML - `matplotlib` : Graphiques (léger, pas de dépendance JS) **Format de données** : - Annotations : JSON (lisible, éditable) - Rapports : HTML + JSON (double export) - Certificats : JSON signé (SHA256) ### 4.2 Optimisations Performance **Accélération GPU** : - Utiliser CUDA pour EDS-Pseudo et CamemBERT-NER - Batch processing optimisé pour 12 GB VRAM - Batch size dynamique selon la taille des documents - Fallback CPU automatique si GPU indisponible **Parallélisation CPU** : - Traitement parallèle de plusieurs PDFs (ProcessPoolExecutor) - Nombre de workers optimal : 8-12 (sur 16 cœurs / 32 threads) - Répartition intelligente de la charge **Mémoire** : - Pas de contrainte mémoire (128 GB disponibles) - Possibilité de charger plusieurs modèles simultanément - Cache des résultats NER pour paragraphes identiques **Optimisations supplémentaires** : - Quantification ONNX des modèles NER (optionnel) - Préchargement des modèles en mémoire - Pipeline asynchrone (extraction → détection → redaction) --- ## 5. Interfaces et API ### 5.1 API d'Évaluation ```python # Évaluer un document evaluator = QualityEvaluator(ground_truth_dir) result = evaluator.evaluate(pdf_path, audit_path) # Évaluer un batch results = evaluator.evaluate_batch(pdf_list, audit_list) report = evaluator.generate_report(results) ``` ### 5.2 API de Validation ```python # Valider un document anonymisé scanner = LeakScanner() leak_report = scanner.scan(anonymized_pdf, original_audit) if leak_report.is_safe: print("✅ Document conforme") else: print(f"❌ {leak_report.leak_count} fuites détectées") ``` ### 5.3 API de Benchmark ```python # Benchmarker le système benchmark = Benchmark(test_data_dir) results = benchmark.run() benchmark.export_results("benchmark_results.json") ``` --- ## 6. Configuration ### 6.1 Fichier de Configuration **Fichier** : `config/quality_config.yml` ```yaml evaluation: ground_truth_dir: "tests/ground_truth" min_documents: 30 confidence_thresholds: regex: 1.0 eds_pseudo: 0.85 camembert: 0.90 contextual: 0.80 validation: strict_mode: true scan_metadata: true readability_threshold: 0.7 detection: enable_regex: true enable_eds_pseudo: true enable_camembert: true enable_contextual: true # Accélération GPU gpu: enabled: true device: "cuda" # ou "cpu" pour fallback batch_size: 16 # Ajuster selon VRAM disponible max_vram_gb: 10 # Limite de VRAM (12 GB disponibles) regex: patterns: [EMAIL, TEL, NIR, IBAN, IPP, FINESS, RPPS, OGC, DATE, ADRESSE] contextual: strong_context_confidence: 0.95 uppercase_confidence: 0.80 weak_context_confidence: 0.60 performance: max_workers: 8 # Traitement parallèle de PDFs (16 cœurs / 32 threads disponibles) max_ram_gb: 32 # Limite RAM par processus (128 GB disponibles) enable_cache: true enable_gpu: true reporting: output_format: [html, json] include_graphs: true export_false_negatives: true export_false_positives: true ``` --- ## 7. Tests ### 7.1 Tests Unitaires **Couverture** : ≥ 80% pour les composants critiques **Tests par composant** : - `test_regex_improved.py` : Tests exhaustifs des regex - `test_contextual_detector.py` : Tests de détection contextuelle - `test_hybrid_detector.py` : Tests d'intégration détecteurs - `test_quality_evaluator.py` : Tests de calcul métriques - `test_leak_scanner.py` : Tests de détection de fuites ### 7.2 Tests de Régression **Suite de tests** : `tests/regression/` **Workflow** : 1. Charger le dataset annoté (30+ documents) 2. Anonymiser chaque document 3. Évaluer la qualité 4. Vérifier que les métriques respectent les seuils : - Rappel ≥ 99.5% - Précision ≥ 97% - F1-Score ≥ 0.98 5. Alerter si dégradation **Exécution** : ```bash pytest tests/regression/ --benchmark ``` --- ## 8. Monitoring et Observabilité ### 8.1 Logging Structuré ```python import structlog logger = structlog.get_logger() # Log avec contexte logger.info("detection_started", pdf_path=str(pdf_path), page_count=page_count) logger.info("pii_detected", pii_type="NOM", count=12, method="eds_pseudo", confidence=0.95) ``` ### 8.2 Métriques Collectées **Par document** : - Temps de traitement - Nombre de PII détectés par type - Méthode de détection utilisée - Métriques de qualité (si ground truth) **Agrégées** : - Temps moyen par document - Répartition des PII par type - Taux de faux positifs/négatifs - Évolution des métriques dans le temps --- ## 9. Sécurité ### 9.1 Protection des Données - Pas de logs contenant des PII - Suppression sécurisée des fichiers temporaires - Pas de transmission réseau - Traitement local uniquement ### 9.2 Validation des Entrées - Validation du format PDF (type MIME) - Limite de taille (< 100 MB) - Validation du format JSON (annotations) - Sanitization des chemins (pas de path traversal) --- ## 10. Déploiement ### 10.1 Structure des Fichiers ``` anonymizer/ ├── core/ │ ├── __init__.py │ ├── pipeline.py (existant) │ └── ... ├── detectors/ │ ├── __init__.py │ ├── improved_regex.py (nouveau) │ ├── contextual.py (nouveau) │ └── hybrid.py (nouveau) ├── evaluation/ │ ├── __init__.py │ ├── quality_evaluator.py (nouveau) │ ├── leak_scanner.py (nouveau) │ └── benchmark.py (nouveau) ├── tools/ │ ├── __init__.py │ ├── annotation_tool.py (nouveau) │ └── quality_reporter.py (nouveau) └── tests/ ├── unit/ ├── regression/ └── ground_truth/ (nouveau) ``` ### 10.2 Installation ```bash # Installer les nouvelles dépendances pip install pytest pytest-cov pydantic structlog jinja2 matplotlib # Créer les répertoires mkdir -p tests/ground_truth mkdir -p reports/quality # Copier la configuration cp config/quality_config.yml.example config/quality_config.yml ``` --- ## 11. Migration depuis la Version Actuelle ### 11.1 Compatibilité Ascendante - ✅ API existante inchangée - ✅ Format d'audit `.jsonl` inchangé - ✅ Configuration YAML compatible - ✅ Pas de breaking changes ### 11.2 Activation Progressive **Phase 1** : Mesure (pas d'impact sur production) - Créer le dataset annoté - Implémenter l'évaluateur - Mesurer la baseline **Phase 2** : Amélioration (opt-in) - Implémenter les détecteurs améliorés - Activer via configuration - Comparer avec baseline **Phase 3** : Validation (opt-in) - Implémenter le scanner de fuite - Activer en mode non-bloquant - Valider sur corpus complet **Phase 4** : Production (opt-out) - Activer par défaut - Mode strict optionnel - Monitoring continu --- ## 12. Livrables Techniques 1. **Code source** : - `detectors/improved_regex.py` - `detectors/contextual.py` - `detectors/hybrid.py` - `evaluation/quality_evaluator.py` - `evaluation/leak_scanner.py` - `evaluation/benchmark.py` - `tools/annotation_tool.py` - `tools/quality_reporter.py` 2. **Tests** : - `tests/unit/test_*.py` - `tests/regression/test_regression.py` 3. **Documentation** : - `docs/annotation_guide.md` - `docs/evaluation_guide.md` - `docs/api_reference.md` 4. **Configuration** : - `config/quality_config.yml` 5. **Templates** : - `templates/quality_report.html` 6. **Dataset** : - `tests/ground_truth/*.pdf` - `tests/ground_truth/*.annotations.json`