- 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
28 KiB
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 :
{
"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 :
- Sélectionner un PDF du corpus
- Extraire et afficher le texte page par page
- 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)
- Lister les termes médicaux à préserver
- Sauvegarder l'annotation
2.2 Système d'Évaluation
2.2.1 Évaluateur de Qualité
Composant : quality_evaluator.py
Classes principales :
@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 :
- Charger les annotations manuelles (ground truth)
- Charger l'audit généré (détections)
- Normaliser les textes (lowercase, strip)
- Créer des clés de comparaison :
(page, type, texte_normalisé) - Calculer les intersections :
- TP = annotations ∩ détections
- FN = annotations - détections (PII manqués)
- FP = détections - annotations (sur-détection)
- Calculer les métriques
- Générer le rapport détaillé
2.2.2 Scanner de Fuite
Composant : leak_scanner.py
Classes principales :
@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 :
- Extraire le texte du PDF anonymisé
- Charger les PII originaux depuis l'audit
- Vérifier que les PII originaux ne sont plus présents
- Détecter de nouveaux PII non masqués (regex + NER)
- Scanner les métadonnées PDF
- 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 :
{
"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 :
RE_TEL_IMPROVED = re.compile(
r"(?<!\d)"
r"(?:"
# Format international
r"(?:\+33|0033)\s*[1-9](?:[\s.\-]?\d){8}"
r"|"
# Format national
r"0[1-9](?:[\s.\-]?\d){8}"
r"|"
# Format fragmenté (max 3 espaces/retours entre groupes)
r"0[1-9][\s.\-]?\d{1,2}[\s.\-]?\d{1,2}[\s\n]{1,3}\d{1,2}[\s.\-]?\d{1,2}[\s.\-]?\d{1,2}"
r")"
r"(?!\d)",
re.MULTILINE
)
B) Emails :
RE_EMAIL_IMPROVED = re.compile(
r"\b[A-Za-z0-9._%+-]+"
r"@"
r"(?:"
# Domaines médicaux
r"(?:chu|ch|aphp|ap-hm|hospices-civils|clinique|hopital|ehpad)"
r"[\w\-]*\.[a-z]{2,}"
r"|"
# Domaines génériques
r"[A-Za-z0-9.-]+\.[A-Za-z]{2,}"
r")\b",
re.IGNORECASE
)
C) Adresses :
RE_ADRESSE_IMPROVED = re.compile(
r"\b"
r"(?:\d{1,4}\s*(?:bis|ter|quater|[A-Z])?\s*,?\s*)?"
r"(?:rue|avenue|boulevard|place|chemin|allée|impasse|route|"
r"résidence|lotissement|cité|hameau|quartier)\s+"
r"(?:de\s+(?:la\s+|l['']\s*|les\s+)?|du\s+|des\s+)?"
r"[A-ZÉÈÀÙÂÊÎÔÛa-zéèàùâêîôûäëïöüç\s\-']{2,}"
r"(?:\s*,?\s*(?:Bât(?:iment)?\.?\s*[A-Z0-9]+|Appt?\.?\s*\d+))?"
,
re.IGNORECASE
)
Tests unitaires :
- Minimum 20 cas de test par regex
- Cas positifs (doit matcher)
- Cas négatifs (ne doit pas matcher)
- Cas limites (formats rares)
2.3.2 Détecteur Contextuel
Composant : contextual_detector.py
Stratégie :
- Détecter les noms avec contexte fort (haute confiance)
- Détecter les noms en MAJUSCULES (moyenne confiance)
- Détecter les noms avec contexte faible (basse confiance)
- Filtrer les faux positifs via stopwords médicaux
- Dédupliquer et trier par confiance
Patterns de contexte :
STRONG_CONTEXTS = [
r"(?:Dr\.?|Docteur|Pr\.?|Professeur)\s+{name}",
r"(?:Mme|Madame|M\.|Monsieur)\s+{name}",
r"Patient(?:e)?\s*:\s*{name}",
r"Nom\s*:\s*{name}",
r"(?:Rédigé|Validé|Signé)\s+par\s+{name}",
]
WEAK_CONTEXTS = [
r"{name}\s+a\s+(?:examiné|consulté|prescrit)",
r"(?:examen|consultation)\s+(?:de|par)\s+{name}",
]
Niveau de confiance :
- Contexte fort : 0.95
- MAJUSCULES (hors stopwords) : 0.80
- Contexte faible : 0.60
2.3.3 Détecteur Hybride
Composant : hybrid_detector.py
Architecture :
class HybridDetector:
def __init__(self):
self.regex_detector = ImprovedRegexDetector()
self.eds_pseudo = EdsPseudoManager()
self.camembert_ner = NerModelManager()
self.contextual_detector = ContextualDetector()
def detect(self, text: str, page: int) -> 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 :
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é :
{
"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 :
<!DOCTYPE html>
<html>
<head>
<title>Rapport de Qualité - Anonymisation</title>
<style>/* CSS moderne */</style>
</head>
<body>
<h1>Rapport de Qualité d'Anonymisation</h1>
<section class="summary">
<h2>Vue d'Ensemble</h2>
<div class="metrics">
<div class="metric">
<span class="value">{{ precision|round(3) }}</span>
<span class="label">Précision</span>
</div>
<div class="metric">
<span class="value">{{ recall|round(3) }}</span>
<span class="label">Rappel</span>
</div>
<div class="metric">
<span class="value">{{ f1_score|round(3) }}</span>
<span class="label">F1-Score</span>
</div>
</div>
</section>
<!-- Graphiques, tableaux, etc. -->
</body>
</html>
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égressionpydantic: Validation des données (annotations, config)structlog: Logging structuréjinja2: Génération rapports HTMLmatplotlib: 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
# É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
# 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
# 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
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 regextest_contextual_detector.py: Tests de détection contextuelletest_hybrid_detector.py: Tests d'intégration détecteurstest_quality_evaluator.py: Tests de calcul métriquestest_leak_scanner.py: Tests de détection de fuites
7.2 Tests de Régression
Suite de tests : tests/regression/
Workflow :
- Charger le dataset annoté (30+ documents)
- Anonymiser chaque document
- Évaluer la qualité
- Vérifier que les métriques respectent les seuils :
- Rappel ≥ 99.5%
- Précision ≥ 97%
- F1-Score ≥ 0.98
- Alerter si dégradation
Exécution :
pytest tests/regression/ --benchmark
8. Monitoring et Observabilité
8.1 Logging Structuré
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
# 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
.jsonlinchangé - ✅ 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
-
Code source :
detectors/improved_regex.pydetectors/contextual.pydetectors/hybrid.pyevaluation/quality_evaluator.pyevaluation/leak_scanner.pyevaluation/benchmark.pytools/annotation_tool.pytools/quality_reporter.py
-
Tests :
tests/unit/test_*.pytests/regression/test_regression.py
-
Documentation :
docs/annotation_guide.mddocs/evaluation_guide.mddocs/api_reference.md
-
Configuration :
config/quality_config.yml
-
Templates :
templates/quality_report.html
-
Dataset :
tests/ground_truth/*.pdftests/ground_truth/*.annotations.json