Dataset synthétique de documents administratifs français destiné à entraîner et évaluer la pipeline OCR / classification / détection d'anomalies du projet hackathon.
dataset-hackaton/
├── Backend/
│ ├── app.py
│ ├── generate_dataset.py # Script principal de génération
│ ├── datalake.py # Script pour MinIO / datalake
│ ├── companies_pool.json # Pool de 20 entreprises réelles (SIRET INSEE)
│ ├── requirements.txt
│ └── output/
│ ├── raw_zone/ # 170 documents générés (PDF + JPG) ← Raw Zone du Data Lake
│ ├── ground_truth.json # Vérité terrain complète (170 entrées)
│ ├── ground_truth_train.json # 80% — entraînement (136 docs)
│ └── ground_truth_test.json # 20% — évaluation (34 docs)
├── Frontend/
│ ├── Dockerfile
│ └── ... # Projet Laravel/Vue minimal
└── docker-compose.yml
pip install faker reportlab pillowInstaller Docker Desktop Vérifier que Docker fonctionne docker --version docker composer version Lancer l’application avec Docker
Tout est conteneurisé : backend Python, frontend Laravel/Vue et MinIO pour le datalake.
1️⃣ Build et démarrage des conteneurs
Depuis la racine du projet :
docker compose up --build
Backend : exposé sur http://localhost:8000
Frontend : exposé sur http://localhost:8080
MinIO (datalake) : console web sur http://localhost:9001 , API S3 sur http://localhost:9000
Credentials :
User : admin
Password : password123
2️⃣ Accéder au conteneur backend pour générer le dataset docker compose exec backend python generate_dataset.py
Les fichiers seront créés dans /app/output/ et persistés sur ton PC grâce au volume Docker.
3️⃣ Accéder au datalake MinIO docker compose exec minio mc alias set local http://localhost:9000 admin password123 docker compose exec backend python datalake.py # Pour uploader les fichiers dans MinIO
python generate_dataset.pyLe script est déterministe (seed fixe à 42) : il produit toujours exactement les mêmes fichiers.
Durée : ~1-2 minutes.
| Métrique | Valeur |
|---|---|
| Total documents | 170 |
| Format PDF natif | 105 |
| Format image (JPG) | 65 |
| Split entraînement | 136 (80%) |
| Split test | 34 (20%) |
| Entreprises dans le pool | 20 |
| Type | Nombre |
|---|---|
| Facture | 88 |
| Devis | 25 |
| URSSAF | 31 |
| Kbis | 11 |
| Attestation SIRET | 10 |
| RIB | 5 |
| Scénario | Nb docs | Format | Description | Anomalie injectée |
|---|---|---|---|---|
| SCN-1 | 30 | Documents parfaits, lisibles | Aucune | |
| SCN-2 | 25 | JPG | Scans bruités (rotation, flou, grain) | dirty_scan |
| SCN-3 | 15 | Factures avec SIRET falsifié | siret_mismatch |
|
| SCN-4 | 15 | Attestations URSSAF expirées | urssaf_expired |
|
| SCN-5 | 10 | Erreurs de calcul HT + TVA ≠ TTC | vat_calculation_error |
|
| SCN-6 | 15 | JPG | Photos smartphone (perspective, ombre) | smartphone_photo |
| SCN-7 | 15 | JPG | Combinés : dégradation + erreur métier | combined_* |
| SCN-8 | 25 | PDF/JPG | Packs inter-documents (5 docs liés) | siret_cross_mismatch (cas B) |
| SCN-9 | 10 | JPG | Très dégradés (pixelisé, taché) | heavy_degradation |
| SCN-10 | 10 | Zones masquées / champs partiels | partial_occlusion |
Chaque entrée de ground_truth.json contient :
{
"filename": "SCN3_siret_mismatch_001.pdf",
"scenario": "SCN-3",
"doc_type": "facture",
"emetteur": "Moustapha ABDI ALI",
"valideur": "Yannis Bouttier",
"entreprise": "JLB LOGICIELS & SERVICES",
"siret_attendu": "52935972100014", ← SIRET officiel de l'entreprise (source INSEE)
"siret_affiche": "96256706492763", ← SIRET écrit sur le document (falsifié ici)
"client": "C.RAPINE DATA & ANALYTICS",
"total_ht": 51517.88,
"tva": 10303.58,
"total_ttc": 61821.46,
"is_valid": false, ← false si une anomalie est présente
"error_type": "siret_mismatch", ← null si document valide
"linked_files": [], ← liste des docs liés (SCN-8 uniquement)
"category": "FACTURE",
"format": "pdf",
"difficulty": "easy", ← easy / medium / hard
"split": "train" ← train / test
}Tous les fichiers Python et JSON sont dans Backend/.
Volumes Docker garantissent que les fichiers générés restent sur ton PC même après arrêt des conteneurs.
L'OCR extrait
siret_affichedepuis le document. La validation croise ensuite ce SIRET avecsiret_attendu(source SIRENE/companies_pool). Sisiret_affiche ≠ siret_attendu→ anomalie détectée.
| Fichier | Utilisé par | Pour quoi faire |
|---|---|---|
raw_zone/*.pdf / *.jpg |
Étudiant 2 (OCR) | Extraire le texte brut |
ground_truth_train.json |
Étudiant 2 / Étudiant 5 | Entraîner les modèles |
ground_truth_test.json |
Étudiant 2 / Étudiant 5 | Évaluer les modèles (données jamais vues) |
ground_truth.json |
Étudiant 4 (Data Lake) | Alimenter la Curated Zone |
companies_pool.json |
Étudiant 5 (Validation) | Référentiel SIRET pour détecter les fraudes |
raw_zone/ (Étudiant 1 — ce dataset)
↓ OCR (Étudiant 2)
Clean Zone — texte extrait
↓ NER / structuration (Étudiant 2)
Curated Zone — JSON structuré (Étudiant 4)
↓ Validation (Étudiant 5)
Anomalies détectées → CRM / Outil conformité (Étudiant 3)
Les 20 entreprises de companies_pool.json sont des entreprises françaises réelles issues de l'API SIRENE (data.gouv.fr). Elles couvrent 4 secteurs :
- Tech : JLB Logiciels, ARC Informatique, BIG DATA & Analytics, etc.
- BTP : Eiffage, Provence Rénovation, NGE Routes, etc.
- Service : Domino Transports, Newrest Restauration, etc.
- Personnalisé : noms des binômes du groupe intégrés dans les champs émetteur/valideur
En production (et pour la démo live), la plateforme peut vérifier les SIRET extraits par l'OCR en temps réel via l'API gratuite de l'annuaire des entreprises :
GET https://recherche-entreprises.api.gouv.fr/search?q={siret_ou_nom}
Aucune clé API requise, l'accès est public et gratuit.
import requests
def verifier_siret(siret):
url = f"https://recherche-entreprises.api.gouv.fr/search?q={siret}"
response = requests.get(url)
data = response.json()
if data["total_results"] == 0:
return {"valide": False, "raison": "SIRET introuvable"}
entreprise = data["results"][0]
return {
"valide": True,
"nom": entreprise["nom_complet"],
"siret": siret,
"adresse": entreprise.get("siege", {}).get("adresse", ""),
"activite": entreprise.get("activite_principale", "")
}
# SIRET réel → valide
verifier_siret("52935972100014")
# → {"valide": True, "nom": "JLB LOGICIELS & SERVICES", ...}
# SIRET falsifié (SCN-3) → introuvable
verifier_siret("96256706492763")
# → {"valide": False, "raison": "SIRET introuvable"}Document uploadé
↓
OCR extrait : SIRET "96256706492763" + Entreprise "JLB LOGICIELS"
↓
Requête API : GET recherche-entreprises.api.gouv.fr/search?q=JLB+LOGICIELS
↓
API répond : le vrai SIRET est 52935972100014
↓
Comparaison : 96256706492763 ≠ 52935972100014
↓
→ ALERTE : "SIRET incohérent détecté !"
| Mode | Source | Usage |
|---|---|---|
| Offline | companies_pool.json (20 entreprises) |
Développement, tests unitaires, démo sans internet |
| Online | API recherche-entreprises.api.gouv.fr |
Démo live, production, couvre toutes les entreprises françaises |
L'Étudiant 5 peut combiner les deux : vérifier d'abord dans companies_pool.json (instantané), puis appeler l'API en fallback si l'entreprise n'est pas dans le pool. Cela garantit que la démo fonctionne même sans connexion internet.
- CER (Character Error Rate) par niveau de difficulté
- Objectif : CER < 5% sur SCN-1, tolérance plus haute sur SCN-9
- Accuracy / F1-score sur les 6 types de documents
- Évaluation sur
ground_truth_test.jsonuniquement
- Precision / Recall / F1 sur la détection des
is_valid = false - Détail par type d'erreur :
siret_mismatch,urssaf_expired,vat_calculation_error
docker compose up --build
docker compose exec backend python generate_dataset.py
docker compose exec backend python datalake.py
- Le script est entièrement reproductible :
random.seed(42)garantit les mêmes documents à chaque exécution. - Pour augmenter le volume, modifier les paramètres
n=dans la fonctionmain()degenerate_dataset.py. - Pour ajouter des entreprises, enrichir
companies_pool.jsonvia l'API SIRENE officielle. =======
IPSSI hackaton 2026
e3d5ccab6779955bf330bfc1bb8cc8c760aaa018