demo: Ajout script de démonstration et correction tests
- 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)
This commit is contained in:
314
demo_evaluation.py
Executable file
314
demo_evaluation.py
Executable file
@@ -0,0 +1,314 @@
|
||||
#!/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()
|
||||
@@ -50,7 +50,7 @@ class TestQualityEvaluator:
|
||||
precision, recall, f1 = evaluator.calculate_metrics(8, 2, 2)
|
||||
assert precision == 0.8 # 8 / (8 + 2)
|
||||
assert recall == 0.8 # 8 / (8 + 2)
|
||||
assert f1 == 0.8
|
||||
assert abs(f1 - 0.8) < 0.0001 # Tolérance pour les flottants
|
||||
|
||||
# Cas zéro
|
||||
precision, recall, f1 = evaluator.calculate_metrics(0, 0, 0)
|
||||
|
||||
Reference in New Issue
Block a user