Membres du groupe :
- HIROUCHE Walid : walid.hirouche@centrale-casablanca.ma
- BENKIRANE Reda : reda.benkirane@centrale-casablanca.ma
- LOZI Adam : adam.lozi@centrale-casablanca.ma
- El OTMANI Hamza* : hamza.elotmani@centrale-casablanca.ma
Ce projet a été réalisé dans le cadre du Hackathon SmartDoc.ai, et son objectif principal est de résoudre les problématiques liées à l’analyse des rapports SFCR grâce à des outils de Machine Learning et une architecture RAG (Retrieval-Augmented Generation).
Lorsqu'un Data Scientist travaille sur des rapports au format PDF, il est souvent confronté à des défis liés à la qualité des résultats des OCR :
- Les informations inutiles (bas/hauts de page, contenus répétitifs) perturbent l’analyse.
- Les données non structurées (ex. tableaux, graphes) nécessitent un travail supplémentaire pour en extraire des informations exploitables.
Une méthode basique comme un tri par la longueur des textes n'est pas efficace. Par exemple :
- Certains paragraphes courts peuvent être plus courts que certains titres, créant une zone grise difficile à distinguer.
- Les bas de page répétitifs, malgré leur contenu inutile, peuvent avoir une longueur suffisante pour être classés comme des titres.
C’est pourquoi nous avons développé une approche hybride combinant des manipulations manuelles et des modèles de Machine Learning.
Tous les scripts sont flexibles et acceptent un chemin en argument lors de leur exécution. Si aucun chemin n'est spécifié, un chemin par défaut sera utilisé. Cela vous évite de modifier constamment les scripts pour changer les dossiers de travail.
Exemple :
python csv_to_txt.py data/csv_modelSi le chemin data/csv_model n'est pas spécifié, le script utilisera par défaut data/csv_model.
Le projet est structuré comme suit :
.
├── classification
│ ├── [requirements.txt](classification/requirements.txt)
│ ├── [training_model.ipynb](classification/training_model.ipynb)
│ └── weights
│ ├── pca.pkl
│ ├── scaler.pkl
│ └── xgboost_classifier.json
├── data
│ ├── csv
│ │ ├── allianz-1-to-94.csv
│ │ ...
│ │ └── covea-output-1-to-98.csv
│ ├── csv_manual
│ │ ├── axa-output-1-to-71.csv
│ │ └── training_data.csv
│ ├── csv_model
│ │ ├── predicted_allianz-1-to-94.csv
│ │ ...
│ │ └── predicted_covea-output-1-to-98.csv
│ ├── json
│ │ ├── allianz-1-to-94.json
│ │ ...
│ │ └── covea-output-1-to-98.json
│ └── txt
│ ├── predicted_allianz-1-to-94.txt
│ ...
│ └── predicted_covea-output-1-to-98.txt
├── [csv_to_txt.py](csv_to_txt.py)
├── [helper_improved.py](helper_improved.py)
├── [predict_labels.py](predict_labels.py)
├── [prepare_training_data.py](prepare_training_data.py)
└── README.md
1. Conversion des JSON en CSV : helper_improved.py
- But : Convertir les fichiers JSON produits par l’OCR en CSV structurés pour faciliter le traitement ultérieur.
- Ajouts importants :
- Une colonne
idpour conserver l’ordre des lignes, utile pour le reclassement après modification de l'ordre des lignes sur Excel. - Une colonne
labelvide pour permettre une classification manuelle dans Excel.
- Une colonne
python helper_improved.py [data/json]- Les fichiers JSON dans
data/json/sont convertis en fichiers CSV et enregistrés dansdata/csv/.
- Trier sur la colonne
chars:- Permet de regrouper les lignes avec peu de caractères, souvent inutiles, et de distinguer les paragraphes et titres.
- Trier sur la colonne
text:- Regroupe les lignes avec un format similaire (ex.
B.1,B.2,/), pour classifier les titres par blocs.
- Regroupe les lignes avec un format similaire (ex.
- Finaliser avec la colonne
label:- Afin d'identifiez les lignes encore non classées si on a encore de la patience.
- Rétablir l’ordre initial :
- On utilise la colonne
idpour cela.
- On utilise la colonne
Attention : Pas besoin de labéliser toutes les lignes. Le fichier suivant ne prendra que les lignes déjà classées dans les 4 fichiers et les fusionera dans un seul fichier d'entraînement training_data.csv.
3. Préparation des données d’entraînement : prepare_training_data.py
- But : Générer un fichier
training_data.csvavec uniquement les lignes labélisées. - Actions :
- Concatène tous les fichiers de
data/csv_manual/. - Supprime les colonnes inutiles à l'entraiînement :
id,text,num_page.
- Concatène tous les fichiers de
python prepare_training_data.py [data/csv_manual]- Un fichier
training_data.csvest généré dansdata/csv_manual/.
4. Entraînement du modèle : training_model.ipynb
-
But : Entraîner plusieurs modèles de Machine Learning et choisir le meilleur pour la classification.
-
Modèles testés :
-
K-Nearest Neighbors (KNN)
-
Support Vector Machine (SVM)
-
Decision Tree
-
Random Forest
-
XGBoost
-
Logistic Regression
-
Naive Bayes
-
Artificial Neural Network (ANN)
-
Résumé des performances des modèles :
Modèle Accuracy Precision Recall F1-Score K-Nearest Neighbors (KNN) 0.938889 0.941433 0.938889 0.938577 Support Vector Machine (SVM) 0.705556 0.736379 0.705556 0.708535 Decision Tree 0.969444 0.969521 0.969444 0.969347 Random Forest 0.969444 0.969654 0.969444 0.969403 XGBoost 0.969444 0.970706 0.969444 0.969348 Logistic Regression 0.680556 0.691033 0.680556 0.680753 Naive Bayes 0.722222 0.735850 0.722222 0.712403 Artificial Neural Network (ANN) 0.897222 0.903691 0.897222 0.897544 Optimized ANN (Keras Tuner) 0.930556 0.932593 0.930556 0.930051 -
-
Étapes :
- Préparation des données :
- Analyse des corrélations.
- Traitement des valeurs aberrantes.
- Application de PCA pour réduire la dimensionnalité.
- Scaling et équilibrage des classes (over/undersampling).
- Évaluation des modèles :
- Validation croisée.
- Meilleur modèle : XGBoost avec une précision de 97%.
- Sauvegarde des poids :
- Modèle, PCA, et scaler sauvegardés dans
classification/weights/.
- Modèle, PCA, et scaler sauvegardés dans
- Préparation des données :
- Installer les dépendances :
pip install -r classification/requirements.txt
- Lancer le notebook :
jupyter notebook classification/training_model.ipynb
5. Prédiction automatique : predict_labels.py
- But : Remplir la colonne
labeldes fichiers CSV non labélisés. - Actions :
- Utilise les poids sauvegardés pour prédire les labels.
- Génère les fichiers labélisés dans
data/csv_model/.
python predict_labels.py [data/csv]- Les fichiers prédits sont enregistrés dans
data/csv_model/.
6. Génération des fichiers texte : csv_to_txt.py
- But : Convertir les CSV labélisés en fichiers texte pour le RAG.
- Contenu des fichiers texte :
- Titres : Précédés de
#. - Paragraphes : Simples.
- Séparations de page :
=======page <num>=======.
- Titres : Précédés de
python csv_to_txt.py [data/csv_model]- Les fichiers texte sont générés dans
data/txt/.
Cette partie bonus du projet vise à extraire les tableaux des documents PDF, les segmenter, puis les traiter pour extraire leurs données textuelles sous une forme exploitable.
.
bonus/
├── cropped_images
│ ├── Allianz-VIE-SFCR-2022
│ │ ├── page_10_1.png
│ │ ...
│ │ └── page_91_1.png
│ ├── AXA_SA_SFCR_FY22_VF
│ │ ├── page_10_1.png
│ │ ...
│ │ └── page_69_2.png
│ └── Rapport-SFCR-Groupe-CAMCA-2022
│ ├── page_10_1.png
│ ...
│ └── page_48_2.png
├── extracted_tables
│ ├── Rapport-SFCR-Groupe-CAMCA-2022-page_10_1.csv
│ └── Rapport-SFCR-Groupe-CAMCA-2022-page_10_1.png
├── images
│ ├── Allianz-VIE-SFCR-2022
│ │ ├── page_10.png
│ │ ...
│ │ └── page_94.png
│ ├── AXA_SA_SFCR_FY22_VF
│ │ ├── page_10.png
│ │ ...
│ │ └── page_71.png
│ └── Rapport-SFCR-Groupe-CAMCA-2022
│ ├── page_10.png
│ ...
│ └── page_49.png
├── pdf
│ ├── Allianz-VIE-SFCR-2022.pdf
│ ├── AXA_SA_SFCR_FY22_VF.pdf
│ ├── Rapport-SFCR-Groupe-CAMCA-2022.pdf
│ └── sfcr_covea_2022.PDF
├── pdf_to_images.py
├── process_table_image.py
├── requirements.txt
└── segment_tables.py
11 directories, 323 files
Pour installer les dépendances, utilisez :
pip install -r bonus/requirements.txtCette partie utilise trois scripts principaux :
1. Conversion des PDF en images : pdf_to_images.py
Le script convertit les fichiers PDF en images, chaque page étant une image distincte.
python pdf_to_images.py [bonus/pdf]- Chemin optionnel : Par défaut, le script utilise le dossier
bonus/pdf/pour les fichiers PDF. - Résultat attendu :
- Les images générées sont enregistrées dans
bonus/images/<nom_du_pdf>/. - Si une image existe déjà, elle n'est pas recréée pour éviter les doublons.
- Les images générées sont enregistrées dans
2. Segmentation des tableaux : segment_tables.py
Ce script utilise un modèle YOLO pour détecter les tableaux dans les images générées, les découper, et les sauvegarder.
python segment_tables.py [bonus/images]- Chemin optionnel : Par défaut, le script traite les images dans
bonus/images/. - Résultat attendu :
- Les tableaux segmentés sont enregistrés dans
bonus/cropped_images/<nom_du_pdf>/. - Chaque tableau est sauvegardé comme une image séparée, nommée
page_<num>_<index>.png.
- Les tableaux segmentés sont enregistrés dans
3. Traitement des tableaux segmentés : process_table_image.py
Ce script prend une image contenant un tableau segmenté, tente de détecter les colonnes, puis applique l’OCR pour reconstruire les données sous forme de tableau exploitable.
python process_table_image.py [bonus/cropped_images/example_image.png]- Chemin optionnel : Par défaut, le script traite l’image
bonus/cropped_images/example_image.png. - Résultat attendu :
- Une image annotée avec des lignes verticales pour les colonnes dans
bonus/extracted_tables/. - Les données extraites sont sauvegardées dans un fichier CSV dans
bonus/extracted_tables/.
- Une image annotée avec des lignes verticales pour les colonnes dans
Le script pdf_to_images.py transforme chaque page d’un PDF en une image distincte. Par exemple :
Le script segment_tables.py détecte les tableaux dans une image de page grâce au modèle YOLO. Chaque tableau est extrait et sauvegardé comme une image séparée.
Image avec un tableau segmenté :

Le script process_table_image.py utilise l’OCR pour extraire les données textuelles du tableau. En analysant les lignes verticales, il tente de détecter les colonnes et reconstruit un tableau ligne par ligne.
Données extraites du tableau :
line
0 en millions d’euros FY 2021 FY 2022 Différence...
1 Risque de Marché 21 681 22 522 +4% +841
2 Risque Non Vie 1 756 1 893 +8 % +136
3 Risque de Crédit 681 811 +19 % +130
4 Risque Vie 318 332 +4% +14
5 Risque Santé 41 53 +27 % +11
Ces données sont sauvegardées dans un fichier CSV (bonus/extracted_tables/<nom_du_tableau>.csv).
-
Colonnes non détectées correctement :
- Les lignes verticales ne sont pas toujours visibles dans les tableaux, rendant la séparation en colonnes difficile.
- On a eu l'idée d'utiliser une méthode qu'on a basé sur la détection de variations de clarté (
delta_y), où on regarde si dans un (delta_y) assez réduit, tous les pixels de la longueurs restent blancs, ce qui voudrais dire que c'est une séparation entre deux colonnes, et donc on trace une ligne verticale noir pour aider l'ocr à mieux détecter les colonne, mais cette méthode s'est avérée partiellement efficace.
-
Résultats actuels :
- Les lignes sont bien détectées.
- Les colonnes ne sont pas toujours reconstruites correctement.
-
Segmentation des colonnes :
- Utiliser des approches basées sur des modèles plus avancés (par exemple, entraînés spécifiquement pour des tableaux sans lignes).
- Détecter les séparateurs implicites dans les tableaux via des techniques de clustering.
-
OCR plus robuste :
- Tester des alternatives comme EasyOCR ou d’autres frameworks spécialisés.
Cette application fournit une interface de chat interactive pour explorer et interroger des documents textuels à l'aide des modèles Gemini de Google Generative AI. Conçue pour gérer jusqu'à quatre fichiers texte simultanément, l'application permet des conversations intelligentes et contextuelles, simplifiant la recherche d'informations et l'analyse documentaire.
L'application s'appuie sur les modèles SOTA Gemini de Google Generative AI, incluant Gemini Pro, Pro Vision et Ultra. Elle prend en charge le téléversement de plusieurs documents (jusqu'à 4 fichiers) et les transforme en indices vectoriels à l'aide de LlamaIndex. Le moteur de chat personnalisé interagit avec les documents, offrant des réglages configurables pour la créativité (température), la limite de tokens et d'autres paramètres. L'interface interactive Streamlit facilite l'utilisation, permettant de téléverser des fichiers, de configurer des paramètres et de discuter avec les documents.
L'application suit un processus clair :
- Configuration initiale : L'application initialise les clés API, les modèles GEMINI et les indices vectoriels.
- Téléversement de documents : Les utilisateurs téléversent des fichiers texte via la barre latérale (sidebar). Ces fichiers sont segmentés en blocs et indexés pour permettre une recherche et un traitement efficaces.
- Chat interactif : Les utilisateurs posent des questions ou demandent des informations à partir des documents téléversés, et le système génère des réponses basées sur la similarité sémantique et l'inférence des modèles.
- Ajustements en temps réel : Les paramètres des modèles et le contexte des conversations peuvent être modifiés dynamiquement pour affiner les réponses.
L'application est construite avec Streamlit pour l'interface interactive, utilise le SDK de Google Generative AI pour interagir avec les modèles Gemini et exploite LlamaIndex pour le traitement et l'indexation des documents. Les documents sont divisés en blocs de 512 tokens (avec un chevauchement de 50 tokens) pour une intégration et une requête optimales. Les embeddings sont générés à l'aide des modèles d'intégration de Gemini pour une représentation sémantique précise. De plus, un système de journalisation intégré enregistre les activités de l'application et les erreurs pour faciliter le débogage.
Les paramètres de l'application sont définis dans la classe AppConfig :
Taille des blocs : 512 tokens (par défaut) Limite de tokens : 1024 tokens par réponse Température : 0,3 (valeur par défaut pour la créativité) Fichier de journalisation : app.log
1- Construisez l'image Docker :
docker build -t rag .2- Exécutez le conteneur Docker :
docker run -p 5000:5000 rag3 - Accédez à l'application via votre navigateur à l'adresse suivante :
http://localhost:5000