- Script demo_evaluation.py montrant tous les outils - Correction test flottant dans test_quality_evaluator.py - Installation pytest/pytest-cov - Tous les tests passent (16/16)
315 lines
11 KiB
Python
Executable File
315 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Démonstration du système d'évaluation de la qualité d'anonymisation.
|
|
|
|
Ce script montre comment utiliser les 3 composants principaux :
|
|
1. QualityEvaluator - Évaluation de la qualité
|
|
2. LeakScanner - Détection de fuites
|
|
3. Benchmark - Mesure de performance
|
|
"""
|
|
import json
|
|
from pathlib import Path
|
|
from evaluation import QualityEvaluator, LeakScanner, Benchmark
|
|
|
|
def demo_annotation_tool():
|
|
"""Démo de l'outil d'annotation."""
|
|
print("\n" + "="*80)
|
|
print("DÉMO 1 : OUTIL D'ANNOTATION")
|
|
print("="*80)
|
|
|
|
print("\nL'outil d'annotation permet d'annoter manuellement les documents PDF.")
|
|
print("\nCommandes disponibles:")
|
|
print(" python tools/annotation_tool.py --list")
|
|
print(" → Liste les 27 documents disponibles")
|
|
print("\n python tools/annotation_tool.py --resume")
|
|
print(" → Reprend l'annotation au prochain document non annoté")
|
|
print("\n python tools/annotation_tool.py tests/ground_truth/pdfs/001_simple_unknown_BACTERIO_23018396.pdf")
|
|
print(" → Annote un document spécifique")
|
|
|
|
# Afficher les documents disponibles
|
|
pdfs_dir = Path("tests/ground_truth/pdfs")
|
|
pdfs = sorted(pdfs_dir.glob("*.pdf"))
|
|
|
|
print(f"\n📁 {len(pdfs)} documents disponibles dans {pdfs_dir}")
|
|
print("\nExemples:")
|
|
for pdf in pdfs[:5]:
|
|
annotation_file = pdf.parent / f"{pdf.stem}.annotations.json"
|
|
status = "✓ Annoté" if annotation_file.exists() else "○ À annoter"
|
|
print(f" {status} {pdf.name}")
|
|
print(f" ... et {len(pdfs) - 5} autres")
|
|
|
|
|
|
def demo_quality_evaluator():
|
|
"""Démo de l'évaluateur de qualité."""
|
|
print("\n" + "="*80)
|
|
print("DÉMO 2 : ÉVALUATEUR DE QUALITÉ")
|
|
print("="*80)
|
|
|
|
print("\nL'évaluateur compare les annotations manuelles avec les détections automatiques.")
|
|
print("Il calcule : Précision, Rappel, F1-Score")
|
|
|
|
# Créer des données de test fictives
|
|
print("\n📝 Création d'annotations de test...")
|
|
|
|
test_pdf = Path("tests/ground_truth/pdfs/001_simple_unknown_BACTERIO_23018396.pdf")
|
|
test_annotation = test_pdf.parent / f"{test_pdf.stem}.annotations.json"
|
|
test_audit = test_pdf.parent / f"{test_pdf.stem}.audit.jsonl"
|
|
|
|
# Créer une annotation fictive
|
|
annotation_data = {
|
|
"pdf_path": str(test_pdf),
|
|
"metadata": {
|
|
"annotator": "demo",
|
|
"annotation_date": "2024-01-15T10:00:00",
|
|
"document_type": "bacterio",
|
|
"page_count": 1,
|
|
"difficulty": "simple"
|
|
},
|
|
"annotations": [
|
|
{
|
|
"id": "ann_001",
|
|
"page": 0,
|
|
"type": "NOM",
|
|
"text": "DUPONT",
|
|
"bbox": None,
|
|
"context": "Dr. DUPONT a examiné",
|
|
"mandatory": True,
|
|
"difficulty": "easy",
|
|
"detection_method_expected": ["regex", "ner"]
|
|
},
|
|
{
|
|
"id": "ann_002",
|
|
"page": 0,
|
|
"type": "TEL",
|
|
"text": "01 23 45 67 89",
|
|
"bbox": None,
|
|
"context": "Tel: 01 23 45 67 89",
|
|
"mandatory": True,
|
|
"difficulty": "easy",
|
|
"detection_method_expected": ["regex"]
|
|
},
|
|
{
|
|
"id": "ann_003",
|
|
"page": 0,
|
|
"type": "EMAIL",
|
|
"text": "jean.dupont@chu-bordeaux.fr",
|
|
"bbox": None,
|
|
"context": "Email: jean.dupont@chu-bordeaux.fr",
|
|
"mandatory": True,
|
|
"difficulty": "easy",
|
|
"detection_method_expected": ["regex"]
|
|
}
|
|
],
|
|
"medical_terms_to_preserve": ["Médecin DIM", "Service de bactériologie"],
|
|
"statistics": {
|
|
"total_pii": 3,
|
|
"by_type": {"NOM": 1, "TEL": 1, "EMAIL": 1}
|
|
}
|
|
}
|
|
|
|
with open(test_annotation, 'w', encoding='utf-8') as f:
|
|
json.dump(annotation_data, f, indent=2, ensure_ascii=False)
|
|
|
|
print(f"✓ Annotation créée: {test_annotation.name}")
|
|
print(f" - 3 PII annotés: 1 NOM, 1 TEL, 1 EMAIL")
|
|
|
|
# Créer un audit fictif (détections)
|
|
audit_data = [
|
|
{"page": 0, "kind": "NOM", "original": "DUPONT", "placeholder": "[NOM]"},
|
|
{"page": 0, "kind": "TEL", "original": "01 23 45 67 89", "placeholder": "[TEL]"},
|
|
{"page": 0, "kind": "EMAIL", "original": "jean.dupont@chu-bordeaux.fr", "placeholder": "[EMAIL]"},
|
|
{"page": 0, "kind": "NOM", "original": "MARTIN", "placeholder": "[NOM]"} # Faux positif
|
|
]
|
|
|
|
with open(test_audit, 'w', encoding='utf-8') as f:
|
|
for item in audit_data:
|
|
f.write(json.dumps(item) + '\n')
|
|
|
|
print(f"✓ Audit créé: {test_audit.name}")
|
|
print(f" - 4 PII détectés: 2 NOM, 1 TEL, 1 EMAIL")
|
|
|
|
# Évaluer
|
|
print("\n🔍 Évaluation en cours...")
|
|
evaluator = QualityEvaluator(Path("tests/ground_truth/pdfs"))
|
|
result = evaluator.evaluate(test_pdf, test_audit)
|
|
|
|
if result:
|
|
print("\n📊 RÉSULTATS:")
|
|
print(f" True Positives: {result.true_positives}")
|
|
print(f" False Positives: {result.false_positives}")
|
|
print(f" False Negatives: {result.false_negatives}")
|
|
print(f"\n Précision: {result.precision:.4f} ({result.precision*100:.2f}%)")
|
|
print(f" Rappel: {result.recall:.4f} ({result.recall*100:.2f}%)")
|
|
print(f" F1-Score: {result.f1_score:.4f}")
|
|
|
|
if result.false_positives > 0:
|
|
print(f"\n ⚠ Faux positifs détectés:")
|
|
for fp in result.false_detections:
|
|
print(f" - {fp['type']}: {fp['text']}")
|
|
|
|
# Générer un rapport
|
|
report = evaluator.generate_report([result])
|
|
print("\n" + "─"*80)
|
|
print(report)
|
|
|
|
# Nettoyer
|
|
test_annotation.unlink()
|
|
test_audit.unlink()
|
|
print("\n✓ Fichiers de test nettoyés")
|
|
|
|
|
|
def demo_leak_scanner():
|
|
"""Démo du scanner de fuite."""
|
|
print("\n" + "="*80)
|
|
print("DÉMO 3 : SCANNER DE FUITE")
|
|
print("="*80)
|
|
|
|
print("\nLe scanner vérifie qu'aucun PII ne subsiste dans les documents anonymisés.")
|
|
print("Il détecte:")
|
|
print(" - PII originaux encore présents (CRITIQUE)")
|
|
print(" - Nouveaux PII non masqués (HAUTE)")
|
|
print(" - Métadonnées suspectes (MOYENNE)")
|
|
|
|
# Créer des données de test
|
|
print("\n📝 Création de données de test...")
|
|
|
|
test_pdf = Path("tests/ground_truth/pdfs/001_simple_unknown_BACTERIO_23018396.pdf")
|
|
test_audit = test_pdf.parent / f"{test_pdf.stem}.audit.jsonl"
|
|
|
|
# Créer un audit fictif
|
|
audit_data = [
|
|
{"page": 0, "kind": "NOM", "original": "DUPONT", "placeholder": "[NOM]"},
|
|
{"page": 0, "kind": "TEL", "original": "01 23 45 67 89", "placeholder": "[TEL]"}
|
|
]
|
|
|
|
with open(test_audit, 'w', encoding='utf-8') as f:
|
|
for item in audit_data:
|
|
f.write(json.dumps(item) + '\n')
|
|
|
|
print(f"✓ Audit créé: {test_audit.name}")
|
|
|
|
# Scanner (simulation - le PDF n'est pas vraiment anonymisé)
|
|
print("\n🔍 Scan en cours...")
|
|
scanner = LeakScanner()
|
|
|
|
# Simuler un scan avec du texte
|
|
print("\n📄 Simulation de scan de texte:")
|
|
|
|
# Cas 1 : Document sûr
|
|
safe_text = "Le patient [NOM] a été examiné le [DATE]. Contact: [TEL]"
|
|
original_pii = [
|
|
{"kind": "NOM", "original": "DUPONT"},
|
|
{"kind": "TEL", "original": "01 23 45 67 89"}
|
|
]
|
|
|
|
leaks = scanner.scan_text(safe_text, original_pii)
|
|
print(f"\n Texte: {safe_text}")
|
|
print(f" Résultat: {'✓ Aucune fuite' if len(leaks) == 0 else f'✗ {len(leaks)} fuite(s)'}")
|
|
|
|
# Cas 2 : Document avec fuite
|
|
unsafe_text = "Le patient DUPONT a été examiné. Tel: 01 23 45 67 89"
|
|
leaks = scanner.scan_text(unsafe_text, original_pii)
|
|
print(f"\n Texte: {unsafe_text}")
|
|
print(f" Résultat: {'✓ Aucune fuite' if len(leaks) == 0 else f'✗ {len(leaks)} fuite(s)'}")
|
|
|
|
if leaks:
|
|
for leak in leaks:
|
|
print(f" - {leak['severity']}: {leak['message']}")
|
|
|
|
# Cas 3 : Nouveau PII détecté
|
|
new_pii_text = "Contact: marie.martin@example.com"
|
|
leaks = scanner.scan_text(new_pii_text, original_pii)
|
|
print(f"\n Texte: {new_pii_text}")
|
|
print(f" Résultat: {'✓ Aucune fuite' if len(leaks) == 0 else f'✗ {len(leaks)} fuite(s)'}")
|
|
|
|
if leaks:
|
|
for leak in leaks:
|
|
print(f" - {leak['severity']}: {leak['message']}")
|
|
|
|
# Nettoyer
|
|
test_audit.unlink()
|
|
print("\n✓ Fichiers de test nettoyés")
|
|
|
|
|
|
def demo_benchmark():
|
|
"""Démo du benchmark."""
|
|
print("\n" + "="*80)
|
|
print("DÉMO 4 : BENCHMARK DE PERFORMANCE")
|
|
print("="*80)
|
|
|
|
print("\nLe benchmark mesure les performances du système d'anonymisation:")
|
|
print(" - Temps de traitement (total, par page)")
|
|
print(" - Utilisation CPU (%)")
|
|
print(" - Utilisation RAM (MB)")
|
|
print(" - Nombre de PII détectés")
|
|
|
|
# Afficher les informations système
|
|
print("\n💻 Informations système:")
|
|
benchmark = Benchmark(Path("tests/ground_truth/pdfs"))
|
|
system_info = benchmark.get_system_info()
|
|
|
|
for key, value in system_info.items():
|
|
print(f" {key}: {value}")
|
|
|
|
print("\n📊 Exemple de résultats de benchmark:")
|
|
print("\n Document: 001_simple_unknown_BACTERIO_23018396.pdf")
|
|
print(" Temps: 3.45s")
|
|
print(" Temps/page: 3.45s")
|
|
print(" CPU: 45.2%")
|
|
print(" RAM: 512.3 MB")
|
|
print(" PII détectés: 12")
|
|
|
|
print("\n Document: 023_complexe_compte_rendu_CRH_23102610.pdf")
|
|
print(" Temps: 25.67s")
|
|
print(" Temps/page: 2.85s (9 pages)")
|
|
print(" CPU: 67.8%")
|
|
print(" RAM: 1024.5 MB")
|
|
print(" PII détectés: 45")
|
|
|
|
print("\n📈 Résumé (27 documents):")
|
|
print(" Temps moyen: 8.5s")
|
|
print(" Temps min/max: 2.1s / 25.7s")
|
|
print(" CPU moyen: 52.3%")
|
|
print(" RAM moyenne: 768.2 MB")
|
|
print(" PII détectés: 245 (moy: 9.1)")
|
|
|
|
|
|
def main():
|
|
"""Fonction principale."""
|
|
print("\n" + "="*80)
|
|
print("DÉMONSTRATION DU SYSTÈME D'ÉVALUATION DE LA QUALITÉ D'ANONYMISATION")
|
|
print("="*80)
|
|
|
|
print("\nCe système permet de:")
|
|
print(" 1. Annoter manuellement des documents PDF")
|
|
print(" 2. Évaluer la qualité des détections (Précision, Rappel, F1)")
|
|
print(" 3. Scanner les fuites de PII dans les documents anonymisés")
|
|
print(" 4. Benchmarker les performances du système")
|
|
|
|
# Lancer les démos
|
|
demo_annotation_tool()
|
|
demo_quality_evaluator()
|
|
demo_leak_scanner()
|
|
demo_benchmark()
|
|
|
|
print("\n" + "="*80)
|
|
print("FIN DE LA DÉMONSTRATION")
|
|
print("="*80)
|
|
|
|
print("\n📚 Pour en savoir plus:")
|
|
print(" - Guide d'annotation: docs/annotation_guide.md")
|
|
print(" - Documentation du module: evaluation/README.md")
|
|
print(" - Tests unitaires: tests/unit/")
|
|
|
|
print("\n🚀 Prochaines étapes:")
|
|
print(" 1. Annoter les 27 documents: python tools/annotation_tool.py --resume")
|
|
print(" 2. Anonymiser les documents avec le système actuel")
|
|
print(" 3. Évaluer la qualité: python -c 'from evaluation import QualityEvaluator; ...'")
|
|
print(" 4. Mesurer la baseline et identifier les améliorations")
|
|
|
|
print("\n✨ Bon travail !")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|