- 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
913 lines
28 KiB
Markdown
913 lines
28 KiB
Markdown
# 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"(?<!\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** :
|
|
```python
|
|
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** :
|
|
```python
|
|
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** :
|
|
1. Détecter les noms avec contexte fort (haute confiance)
|
|
2. Détecter les noms en MAJUSCULES (moyenne confiance)
|
|
3. Détecter les noms avec contexte faible (basse confiance)
|
|
4. Filtrer les faux positifs via stopwords médicaux
|
|
5. Dédupliquer et trier par confiance
|
|
|
|
**Patterns de contexte** :
|
|
```python
|
|
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** :
|
|
```python
|
|
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** :
|
|
```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
|
|
<!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é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`
|