Added some pictures to blog.md

This commit is contained in:
Grégoire Mureau 2025-07-13 14:18:04 +02:00
parent ae6fd8946d
commit 8aec5041f9
19 changed files with 107 additions and 83 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/bert_roc_curve.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -1,151 +1,176 @@
# ✨ Analyse de sentiments et déploiement MLOps : retour d’expérience complet # ✨ Analyse de sentiments et déploiement MLOps : retour d’expérience complet
Dans le cadre du projet *Air Paradis*, nous avons mis en œuvre une chaîne complète d’analyse de sentiments à partir de tweets, depuis la modélisation jusqu’au déploiement MLOps. Ce billet de blog revient sur l’ensemble des étapes réalisées, les choix méthodologiques, la comparaison des modèles et la mise en production d’une API. 🧠🔧 Dans le cadre du projet *Air Paradis*, nous avons mis en œuvre une chaîne complète d’analyse de sentiments à partir de tweets, depuis la modélisation jusqu’au déploiement MLOps. Ce billet de blog revient sur les étapes clés du projet : choix méthodologiques, comparaison des modèles, mise en production d’une API robuste, et déploiement sur deux environnements (local + cloud). 🧠🔧
--- ---
## 🔍 Trois approches de modélisation supervisée ## 🔍 Trois approches de modélisation supervisée
Nous avons testé trois approches complémentaires pour prédire le sentiment d’un tweet. Chacune s’appuie sur un type de représentation des textes et un algorithme différent : Nous avons testé trois approches complémentaires pour prédire le sentiment d’un tweet. Chacune repose sur une représentation textuelle différente et un algorithme d’apprentissage adapté :
### 1. 🧱 Approche simple : Bag-of-Words (BoW) + modèles de machine learning ### 1. 🧱 Approche simple : Bag-of-Words (BoW) + modèles de machine learning
Cette approche utilise des vecteurs BoW ou BoW + TF-IDF, qui capturent la fréquence des mots dans les tweets. On fait le choix d’éviter des techniques non supervisées comme **LDA**, car elles sont plus adaptées à des tâches de topic modeling et non de classification de sentiments (binaire qui plus est). On vectorise les tweets via BoW ou BoW + TF-IDF, qui comptent la fréquence des mots.
Contrairement à d'autres projets NLP, nous avons évité LDA (non supervisé, orienté topic modeling).
➡️ **Modèles testés :** ➡️ **Modèles testés** :
- Régression Logistique - Régression Logistique
- Naive Bayes - Naive Bayes
- SVM - SVM
- Random Forest - Random Forest
Ces modèles ont été entraînés avec **GridSearchCV**, en optimisant la **précision (`precision` et non `accuracy`)**, notre KPI principal. Ces modèles sont entraînés avec **GridSearchCV**, en optimisant la **précision (`precision`)**, notre KPI métier. Différents KPI ont été calculés sur l'ensemble de test.
📸 *[Insérer ici une capture d’écran MLFlow avec les scores des modèles simples]* ![Scores modèles simples](./Basic-Models.png)
--- ---
### 2. 🔥 Approche avancée : Word Embeddings (Word2Vec & GloVe 300d) + LSTM ### 2. 🔥 Approche avancée : Word Embeddings + LSTM
On représente les mots non plus comme des fréquences mais comme des vecteurs denses, porteurs de signification. Nous utilisons des vecteurs denses Word2Vec (entraînés) et GloVe 300d (préentraînés par Stanford), qui capturent les similarités sémantiques entre mots. Ces représentations ne prennent toutefois **pas en compte le contexte** exact (ex : "annulé" est probablement un mot très négatif pour un vol mais positif pour un procès contenant des charges contre vous).
Nous avons utilisé :
- **Word2Vec**, entraîné sur un grand corpus (skip-gram),
- **GloVe 300d**, embeddings précalculés par Stanford.
Ces représentations sont **statistiquement proches** entre mots au sens sémantique. La classification est réalisée par un **LSTM**, capable d'exploiter la séquentialité des mots.
Elles **ne tiennent cependant pas compte du contexte exact dans la phrase** : Le **modèle déployé est automatiquement sélectionné** entre Word2Vec + LSTM et GloVe + LSTM, selon la **précision maximale enregistrée dans MLFlow**.
> « banque » aura le même vecteur que ce soit pour une banque d'argent ou une banque de données.
La classification se fait via un **LSTM** (Long Short-Term Memory), une architecture de **deep learning supervisée** adaptée aux séquences de mots. Modèle `Word2Vec + LSTM` (`test_precision : 0.797 , val_precision : 0.795`):
![Scores entrainement modèle avancé LSTM + Word2Vec](./Word2Vec_LSTM_epoch9.png)
Modèle `GloVe 300d + LSTM` (`test_precision : 0.792 , val_precision : 0.798`):
![Scores entrainement modèles avancés LSTM + GloVe 300d](./GloVe-300d_LSTM_epoch12.png)
🧠 **Le modèle retenu pour le déploiement est sélectionné automatiquement dans MLflow parmi les deux (Word2Vec ou GloVe), en fonction de celui qui obtient la précision maximale.** Bien que GloVe 300d + LSTM présente un score de précision légèrement supérieur à Word2Vec + LSTM sur l'ensemble de validation, il reste inférieur pour l'ensemble de test (relativement négligeable dans l'absolu).
Cela permet d’avoir un compromis optimal entre légèreté (CPU-friendly) et performance.
📸 *[Insérer ici une capture d’écran du graphe de précision sur validation pour GloVe+LSTM]*
--- ---
### 3. 🤖 BERT : Bidirectional Encoder Representations from Transformers ### 3. 🤖 Approche transformer : DistilBERT, embeddings contextuels allégés
BERT est un modèle de langage avancé, préentraîné par Google, capable de **comprendre le contexte bidirectionnel** d’un mot dans une phrase. Contrairement aux word embeddings classiques, BERT : Nous avons utilisé **DistilBERT**, une version plus légère et plus rapide de BERT (créée par Hugging Face).
- **encode dynamiquement les mots** selon leur contexte d’usage (ex. « banque » n’aura pas le même vecteur selon la phrase), Il conserve **95 % de la performance de BERT** tout en étant **40 % plus petit** et **60 % plus rapide**, ce qui le rend particulièrement adapté à un **entraînement sur GPU limité** (Google Colab) et à une **inférence plus rapide**.
- est entraîné par **masked language modeling** et **next sentence prediction**, puis fine-tuné pour la classification supervisée.
Cependant, il a ses **limites** : Comme BERT, DistilBERT produit des **embeddings contextuels**, c’est-à-dire que le sens des mots varie selon leur contexte. Par exemple, *"banque"* dans *"je vais à la banque"* et *"banque de données"* n’aura pas le même vecteur.
> ⚠️ BERT **ne comprend pas l’ironie, le sarcasme ou les références culturelles implicites**.
> Il reste un modèle basé sur des cooccurrences statistiques, sans connaissance du monde ou intentionnalité.
> Or, **Twitter est un terrain propice au second degré**, aux détournements ou aux trolls : ce type de contenu reste un **véritable défi pour les modèles classiques de NLP.**
📸 *[Insérer ici un graphique montrant la précision de BERT sur le jeu de validation]* Il a été **fine-tuné** ici pour une classification binaire (positif/négatif), avec un tokenizer et un encodage spécifiques à son architecture.
> **Modèle `DistilBERT`** (`test_precision : 0.861 , val_precision : 0.858`)
![Courbes d'entraînement DistilBERT](./bert_training_curves_epoch1.png)
![Matrice de Confusion](./bert_confusion_matrix.png)
![ROC Curve](./bert_roc_curve.png)
⚠️ **Limites** : comme tous les modèles préentraînés, DistilBERT **n’est pas sensible à l’ironie, aux jeux de mots, ni aux références culturelles implicites**.
Or, Twitter est un terrain de jeu idéal pour ce genre de contenus détournés. Ces limites s’appliquent aussi à Word2Vec, GloVe et aux modèles classiques.
--- ---
## 📐 Choix du KPI : pourquoi la précision (precision) ? ## 🎯 Pourquoi optimiser la précision (et non l'accuracy) ?
Nous avons fait le choix de nous focaliser sur la **précision** plutôt que l’**accuracy** : **Accuracy** = proportion de bonnes prédictions globales.
**Precision** = proportion de tweets *prévus positifs* qui sont *vraiment positifs*.
> 🎯 **Objectif métier :** éviter de **classer un tweet comme positif alors qu’il est négatif**, car cela pourrait induire une mauvaise communication ou une mauvaise interprétation dans un contexte sensible. > Exemple : sur 100 tweets, si le modèle prédit 30 positifs, mais 15 sont des erreurs, la précision est de 50 %.
### Pour chaque type de modèle : Nous avons préféré **minimiser les faux positifs**, car dans un contexte sensible, **annoncer un tweet positif alors qu’il est négatif peut être problématique**.
- Modèles classiques → optimisation via **GridSearchCV sur la précision**
- Modèles DL → **EarlyStopping sur val_loss**, mais sélection du **meilleur modèle selon la précision** sur l’ensemble de validation. - Modèles classiques : **GridSearchCV optimisé sur la précision**
- Modèles DL : **EarlyStopping sur val_loss**, mais **sélection finale sur la précision**
--- ---
## ⚙️ Mise en place de l’environnement MLOps maison ## ⚙️ Environnement MLOps souverain
Nous avons choisi une **infrastructure MLOps souveraine et autonome** pour orchestrer les expérimentations. ### 🖥️ Serveur personnel NAS + OpenMediaVault
### 🏠 Serveur personnel (NAS) + OpenMediaVault MLFlow est installé sur un **NAS personnel (Intel N100)**, sous **OpenMediaVault (Debian)**.
Nous avons déployé MLFlow sur un NAS personnel tournant sous **OpenMediaVault** (basé sur Debian). Nous utilisons **MinIO** pour stocker artefacts et modèles, garantissant une gestion locale, souveraine et compatible S3.
### 📦 Artefacts dans MinIO > Les notebooks, exécutés sur Google Colab pour bénéficier de GPU, loggent dans MLFlow et envoient les modèles vers MinIO via des **variables d’environnement**.
Les modèles, métriques, visualisations et paramètres sont stockés dans **MinIO**, un système compatible S3.
➡️ MinIO est open-source, léger, et permet une **gestion souveraine des artefacts**, contrairement à AWS S3.
> Les notebooks (en particulier ceux entraînés sur Google Colab, pour bénéficier de GPU) envoient automatiquement leurs logs vers MLFlow et leurs artefacts vers MinIO, via des **variables d’environnement** pour sécuriser les identifiants. Dashboard Expériences MLFlow :
![Dashboard MLFLow](./mlflow_experiments_dashboard.png)
📸 *[Insérer ici une capture du dashboard MLFlow]* Modèles en production (registre de modèles):
![Models Registry](mlflow_models_registry_2-1.png)
Meilleur modèle non BERT retenu :
![Modèle retenu](mlflow_models_registry_best_LSTM_model-1.png)
![Meilleur modèle non BERT](mlflolw_experiments_dashboard_best_LSTM_model-1.png)
--- ---
## 🧪 Intégration continue, tests, versioning ## 🧪 CI, tests et versioning
Une fois les modèles validés, nous avons implémenté toute la chaîne MLOps pour le déploiement API : ### ✅ Tests unitaires
### ✅ Tests automatisés - Vérifient le bon fonctionnement du prétraitement, des prédictions et de la gestion d’erreurs.
- **Tests unitaires** pour valider le prétraitement des tweets, les prédictions et la gestion des erreurs. - Intégrés à **GitHub Actions**, **ils bloquent le déploiement si les tests échouent.**
- Ces tests sont intégrés dans **GitHub Actions**, et **empêchent tout déploiement Docker si les tests échouent**.
### 🐳 Conteneurisation & CI/CD ### 🐳 Conteneurisation & CI/CD
- L’API est packagée via **Docker**, puis poussée automatiquement sur **DockerHub** en cas de succès.
- Cela permet un déploiement cohérent et rapide sur n’importe quelle plateforme. L’API FastAPI est packagée via Docker, puis poussée sur DockerHub.
Un pipeline CI/CD complet garantit **un déploiement reproductible**.
![github workflow](github_ci-cd-1.png)
---
## 🚀 Déploiement de l’API FastAPI (double)
### 🔧 Backend : FastAPI
- Expose un endpoint `/predict`
- Charge dynamiquement le **meilleur modèle Word Embedding + LSTM** via le *Model Registry* MLFlow
- Applique les bons embeddings selon le tag `embedding_type`
### 💻 Interface Streamlit
Interface locale pour tester les requêtes, debug, démonstrations.
![Streamlit Local](./streamlit_interface_local.png)
--- ---
## 🚀 Déploiement de l’API FastAPI ### 🌐 Double déploiement (NAS + Google Cloud)
### 🛠️ Backend : FastAPI #### 🔧 Problème AVX2
- Fournit un endpoint `/predict`, avec chargement dynamique du modèle via MLFlow et récupération des embeddings (BoW, GloVe ou Word2Vec) selon le tag `embedding_type`.
🧠 Le modèle de type Word Embeddings + LSTM avec la **meilleure précision** est automatiquement récupéré depuis le *Model Registry* MLFlow. Initialement, l’API ne fonctionnait pas sur le NAS (Intel N100), faute d’instruction **AVX2**, requise par TensorFlow.
### 💻 Frontend local : Streamlit > 🛠️ **Solution : activer AVX2 dans le BIOS** (désactivée par défaut sur certaines cartes mères, souvent optimisées pour NAS).
- Sert d’**interface utilisateur** pour tester les prédictions en local, simuler des requêtes API et visualiser les résultats.
- C’est aussi un outil utile pour les démonstrations ou le debug.
📸 *[Insérer capture Streamlit en action]* Depuis, l’API tourne sur :
- 🏠 NAS local (accès via [sentiment-api.greg-madman-nas.duckdns.org](https://sentiment-api.greg-madman-nas.duckdns.org))
Interface Docker Compose de NAS:
![docker compose openmediavault](openmediavault_docker-compose_settings-1.png)
- ☁️ Google Cloud Run : [sentiment-api-service-7772256003.europe-west1.run.app](https://sentiment-api-service-7772256003.europe-west1.run.app)
Interface Déploiement Google Cloud:
![déploiement google cloud](google_cloud_interface-1.png)
--- ---
### 🌐 Problème matériel sur le NAS ## 📊 Monitoring maison
Le NAS utilise un processeur **Intel N100**, théoriquement compatible AVX2.
> ❗ Malheureusement, **certaines cartes mères désactivent cette instruction** au niveau BIOS, ce qui empêche l’exécution de TensorFlow, nécessaire pour le LSTM et BERT.
🛠️ Solution : **déploiement sur Google Cloud Run**, à l’adresse suivante : Une logique d’alerte légère embarquée dans l’API :
🔗 [https://sentiment-api-service-7772256003.europe-west1.run.app/](https://sentiment-api-service-7772256003.europe-west1.run.app/)
📸 *[Insérer capture console Google Cloud / endpoint]* ![1ère erreur](./api_first_error.png)
--- ![2ème erreur](./api_second_error.png)
## 📊 Monitoring maison en production ![3ème erreur](./api_third_error.png)
L'API embarque une **logique d’alerte interne** en cas de dérive.
🎯 **Comportement :** Si **3 erreurs de prédiction** consécutives sont détectées sur une **fenêtre de 5 min**, un rapport est généré automatiquement.
- Si plus de **3 prédictions incorrectes** sont identifiées dans une **fenêtre de 5 minutes**, un **rapport d’erreur** est généré automatiquement.
- Ces erreurs peuvent être loggées ou transmises via webhook selon la configuration future. ![Rapport d'erreur envoyé par mail²](./api_error_report.png)
Cela permet de garder un **œil léger mais réactif** sur les dérives en production, sans surcharger l’infrastructure.
--- ---
## 🧠 Schéma global du pipeline MLOps ## 🧠 Schéma global du pipeline MLOps
```
┌────────────┐ ┌────────────┐
│ Données │ │ Données │
│ (tweets) │ │ (tweets) │
└────┬───────┘ └─────┬──────┘
│ │
Prétraitement (regex, tokenisation, lemmatisation, stopwords) Prétraitement (regex, tokenisation, lemmatisation, stopwords)
│ │
@ -162,26 +187,25 @@ Cela permet de garder un **œil léger mais réactif** sur les dérives en produ
│ + Streamlit │ │ + Streamlit │
└─────────┬──────────┘ └─────────┬──────────┘
│ │
Déploiement sur Google Cloud Run Déploiement sur NAS + Google Cloud Run
│ │
┌──────────▼─────────────┐ ┌──────────▼─────────────┐
│ Monitoring léger maison│ │ Monitoring léger maison│
│ (alertes sur mauvaises │ │ (alertes sur mauvaises │
│ prédictions) │ │ prédictions) │
└────────────────────────┘ └────────────────────────┘
```
--- ---
## ✨ Conclusion ## ✨ Conclusion
Ce projet a permis d’explorer en profondeur les enjeux de classification de sentiments et les **limites des approches standards face à la richesse du langage naturel**, en particulier sur Twitter. Ce projet démontre comment construire une chaîne MLOps complète, autonome et reproductible pour du NLP appliqué à Twitter.
Il a aussi démontré l’intérêt d’un **pipeline MLOps souverain** (le plus possible), reproductible et automatisé. Il souligne les **forces et limites des modèles** (y compris BERT), la nécessité d’une métrique adaptée au contexte (précision vs accuracy), et l’importance d’un **déploiement maîtrisé**, même sur une infrastructure personnelle.
> ✅ En alliant des modèles puissants, des outils robustes comme MLFlow et FastAPI, et un déploiement maîtrisé, on pose les bases d’un produit de NLP industrialisable.
--- ---
💬 Merci pour votre lecture ! 💬 Merci pour votre lecture !
Pour tester l’API : [https://sentiment-api-service-7772256003.europe-west1.run.app/](https://sentiment-api-service-7772256003.europe-west1.run.app/) Testez l’API :
- En local (NAS) : [https://sentiment-api.greg-madman-nas.duckdns.org/](https://sentiment-api.greg-madman-nas.duckdns.org/)
- Sur Google Cloud : [https://sentiment-api-service-7772256003.europe-west1.run.app/](https://sentiment-api-service-7772256003.europe-west1.run.app/)

BIN
docs/github_ci-cd-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
docs/github_ci-cd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View file

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB