documentation available both in english and french

This commit is contained in:
Grégoire Mureau 2025-07-16 09:44:17 +02:00
parent 20f4f1652b
commit f074ebb4f4
13 changed files with 672 additions and 170 deletions

62
README.fr.md Normal file
View file

@ -0,0 +1,62 @@
# Projet Air Paradis – Analyse de sentiments
📘 Ce projet est également disponible en [anglais 🇬🇧](./README.md)
Ce dépôt contient les notebooks et le code source du projet d’analyse de sentiments sur des tweets, réalisé dans le cadre du programme OpenClassrooms **"Air Paradis"**.
---
## Contexte métier
L’objectif est de prédire si un tweet (ou une courte expression écrite) est perçu comme **positif ou négatif**, dans le contexte d’une compagnie aérienne.
Le projet repose sur le jeu de données public **Sentiment140**.
---
## Description du projet
Le projet vise à développer un système complet d’analyse de sentiments, comprenant :
- Une analyse exploratoire des données textuelles,
- Une comparaison de plusieurs modèles de classification (classiques, avancés et à l’état de l’art),
- Le déploiement d’une API REST exposant un modèle avancé (embeddings + LSTM),
- La mise en place d’un pipeline CI/CD pour automatiser les tests et le déploiement,
- Un hébergement local sur un serveur NAS pour garantir la souveraineté des données.
---
## Structure du dépôt
- `notebooks/` : notebooks d’analyse et de modélisation,
- `deployment/` : code de l’API, tests, Dockerfile et fichiers liés au déploiement,
- `docs/` : documentation et ressources associées,
- `../.github/workflows/` : pipelines CI/CD.
---
## Entraînement des modèles
Les phases d’entraînement ont principalement été réalisées sur **Google Colab**, afin de bénéficier de ressources GPU nécessaires aux modèles de deep learning.
---
## Utilisation
Les notebooks détaillent les étapes d’analyse et d’entraînement.
L’API est disponible dans le dossier `deployment/api`.
---
## Remarques
- Les fichiers `requirements.txt` dans `notebooks/` proviennent des environnements Google Colab et peuvent contenir des dépendances peu optimisées pour un usage local.
- Le fichier `.env`, exclu du dépôt, contient les variables d’environnement nécessaires.
---
Ce README sera mis à jour progressivement au fur et à mesure de l’avancement du projet.
Certains commentaires de code peuvent rester en français (ainsi que les cellules Markdown des notebooks). Tout sera traduit en anglais ultérieurement.
---
**Projet réalisé dans le cadre du parcours "Data Scientist – Projet 7" d’OpenClassrooms**

View file

@ -1,4 +1,6 @@
# Air Paradis Project - Sentiment Analysis
# Air Paradis Project – Sentiment Analysis
📘 This project is also available in [French 🇫🇷](./README.fr.md)
This repository contains the notebooks and source code for the sentiment analysis project on tweets, carried out as part of the OpenClassrooms "Air Paradis" program.
@ -6,7 +8,8 @@ This repository contains the notebooks and source code for the sentiment analysi
## Business Context
The goal is to predict whether a tweet, or a short written expression, is perceived as positive or negative, in the context of an airline company. The project is based on the public **Sentiment140** dataset.
The goal is to predict whether a tweet, or a short written expression, is perceived as **positive or negative**, in the context of an airline company.
The project is based on the public **Sentiment140** dataset.
---
@ -50,9 +53,9 @@ The notebooks detail the analysis and training steps. The API is available in `d
---
This README will be updated progressively as the project advances.
Some code comments can remain in french language (as well as the notebooks markdowns etc.). All will be changed into english in the future.
This README will be updated progressively as the project advances.
Some code comments can remain in French (as well as notebook markdowns). All will be translated into English later.
---
**Project completed as part of the OpenClassrooms "Data Scientist - Project 7" program**
**Project completed as part of the OpenClassrooms "Data Scientist – Project 7" program**

128
deployment/README.fr.md Normal file
View file

@ -0,0 +1,128 @@
# Déploiement de l’API – Projet Air Paradis
📘 Cette documentation est aussi disponible en [anglais 🇬🇧](./README.md)
Ce dossier contient le code et la configuration nécessaires au déploiement de l’API de prédiction de sentiment.
---
## 🎯 Objectif
Déployer une API REST permettant de :
- Prédire le **sentiment d’un tweet** (positif ou négatif) à partir d’un modèle LSTM entraîné sur des embeddings (Word2Vec ou GloVe),
- Signaler les erreurs de prédiction.
L’API utilise le **meilleur modèle enregistré** dans MLflow et le sert via **FastAPI**.
---
## 📦 Architecture globale
Schéma :
```
┌──────────────────────────────┐
│ Utilisateur final (UI) │
│ (interface Streamlit) │
└────────────┬─────────────────┘
│
[Appel HTTP vers /predict]
│
┌────────────▼─────────────┐
│ API FastAPI │
│ (serveur lancé via │
│ Uvicorn) │
└────────────┬─────────────┘
│
Chargement du modèle (MLflow)
│
Prétraitement du tweet
│
Prédiction (inférence)
│
Réponse JSON avec le sentiment
```
---
## 🧪 Fonctionnement de l’API
1. **Chargement du modèle**
- Le meilleur modèle est enregistré dans MLflow sous le nom `SentimentAnalysisLSTM`, avec le stage `Production`.
- L’API le charge au démarrage via l’URI :
```
models:/SentimentAnalysisLSTM/Production
```
2. **Prétraitement du texte**
- Nettoyage, tokenisation, lemmatisation, puis conversion en vecteurs d’embedding.
3. **Prédiction**
- Le modèle LSTM prédit le sentiment.
- Le résultat (`positive` ou `negative`) est retourné en JSON.
4. **Interface utilisateur (optionnelle)**
- Une interface Streamlit permet une interaction visuelle avec l’API.
---
## 🐳 Déploiement via Docker
Un `Dockerfile` est fourni pour simplifier le déploiement sur tout hôte compatible Docker (NAS, cloud, etc.).
L’image Docker est poussée sur DockerHub à chaque version validée (voir CI/CD plus bas).
Par souci de **souveraineté des données**, le déploiement est préféré sur un **NAS équipé d’un processeur Intel N100** (peu énergivore et suffisant pour l’inférence LSTM).
Le redéploiement peut être automatisé via :
- une tâche `cron` simple, ou
- un conteneur **Watchtower** (dans docker-compose ou kubernetes).
**Commande pour test local :**
```bash
docker build -t sentiment-api .
docker run --rm --env-file ../../.env -p 8000:8000 -p 8080:8080 sentiment-api
```
En production, les variables d’environnement doivent être définies dans le `docker-compose.yml` ou via un gestionnaire de secrets.
---
## 🛠 Problèmes AVX2 / Déploiement cloud
**Mise à jour :** au départ, le NAS ne supportait pas les instructions AVX2 nécessaires à TensorFlow.
En réalité, les CPU N100 les prennent en charge, mais certains fabricants désactivent cette option dans le BIOS.
En attendant un accès physique au NAS, nous avons opté pour un déploiement sur **Google Cloud**.
Problème : un seul port peut être exposé publiquement sur ces services.
Deux solutions possibles :
- Séparer le backend (FastAPI) et le frontend (Streamlit) en deux conteneurs,
- Utiliser un **reverse proxy** (ex. : nginx) pour rediriger les ports.
➡️ Nous avons choisi la **deuxième solution**, pour garder une **seule image Docker**.
**Mise à jour 2 :** AVX2 a été activé via le BIOS. Le déploiement est donc opérationnel **à la fois en local (NAS)** et **sur le cloud**, avec déploiement automatique pour le NAS (voir section suivante).
---
## 🔁 CI/CD – GitHub Actions
Un pipeline est défini dans `.github/workflows/` pour :
- Lancer les tests unitaires à chaque `push`,
- Vérifier la stabilité de l’API avant déploiement (NAS ou cloud),
- Construire et pousser l’image Docker sur DockerHub.
Les tests sont situés dans :
```
deployment/api/tests/
```
Le redéploiement automatique sur le NAS est géré via **Watchtower**, qui surveille les mises à jour de l’image Docker (fonctionne comme un `cron`, avec logs).
---

View file

@ -1,14 +1,19 @@
# API Deployment - Air Paradis Project
# API Deployment – Air Paradis Project
📘 This documentation is also available in [French 🇫🇷](./README.fr.md)
This folder contains the code and configuration necessary to deploy the sentiment prediction API.
---
## 🎯 Objective
Deploy a REST API that allows to:
- predict the sentiment of a tweet (positive or negative) based on an LSTM model trained on embeddings (Word2Vec or GloVe).
- report prediction errors
The API uses the best-performing model, registered on MLflow, and serves it via FastAPI.
- Predict the sentiment of a tweet (positive or negative) using an LSTM model trained on embeddings (Word2Vec or GloVe),
- Report prediction errors.
The API uses the best-performing model registered on MLflow and serves it via FastAPI.
---
@ -16,6 +21,7 @@ The API uses the best-performing model, registered on MLflow, and serves it via
Diagram:
```
┌──────────────────────────────┐
│ End user (UI) │
│ (Streamlit interface) │
@ -36,72 +42,85 @@ Diagram:
Prediction (inference)
│
Sentiment response (JSON)
```
---
## 🧪 API Workflow
1. Model loading
- The best performing model is registered in MLflow under a fixed name (`SentimentAnalysisLSTM`) and a stage `Production`.
- The API loads this model at startup using a stable URI:
1. **Model loading**
- The best performing model is registered in MLflow under a fixed name (`SentimentAnalysisLSTM`) and a `Production` stage.
- The API loads this model at startup using the URI:
```
models:/SentimentAnalysisLSTM/Production
```
2. Text preprocessing
- The text sent by the user is cleaned, tokenized, lemmatized, and transformed into vectors (embeddings).
2. **Text preprocessing**
- Text is cleaned, tokenized, lemmatized, and converted into vectors (embeddings).
3. Prediction
- The LSTM model (loaded from MLflow) performs inference.
- The result (`positive` or `negative`) is returned as a JSON response.
3. **Prediction**
- The LSTM model (loaded from MLflow) makes a prediction.
- The result (`positive` or `negative`) is returned as JSON.
4. User interface (optional)
4. **User interface (optional)**
- A Streamlit interface allows visual interaction with the API.
---
## 🐳 Deployment with Docker
A Dockerfile is provided to facilitate deploying the API on any Docker-compatible host (NAS, cloud, etc.).
A `Dockerfile` is provided for easy deployment on any Docker-compatible host (NAS, cloud, etc.).
This Docker image should be pushed to DockerHub at each validated versioning (see the CI/CD section below).
This Docker image should be pushed to DockerHub with every validated version (see CI/CD below).
For data sovereignty reasons, deployment will preferably be done on a NAS equipped with an Intel N100 processor (energy efficient but probably sufficient for LSTM inference).
Docker can be redeployed each time the docker image on DockerHub is updated. Man can use `cron` instruction (simple) or `Watchtower` docker (docker-compose / kubernetes context).
For data sovereignty, deployment is preferably done on a **NAS with Intel N100** CPU (energy-efficient, sufficient for inference).
Docker can be automatically redeployed using:
- `cron` (simple), or
- `Watchtower` in docker-compose/kubernetes setups.
For running the Docker image locally (development), it is important to provide the `.env` file containing environment variables and to open the FastAPI and Streamlit ports for testing the application.
**Development command (local test):**
Example commands to build and run:
```bash
docker build -t sentiment-api .
docker run --rm --env-file ../../.env -p 8000:8000 -p 8080:8080 sentiment-api
```
docker build -t sentiment-api .
docker run --env-file ../../.env -p 8000:8000 -p 8080:8080 sentiment-api
For deployment on a personal or public cloud (with docker-compose, Kubernetes), it is preferable to specify the necessary environment variables in `docker-compose.yml` or in secrets.
Add the `--rm` argument so that the container is removed after stopping.
**UPDATE** : in our case, we encountered some issues with the NAS which CPU is not able to run AVX2 instructions so we are obliged to deploy it on a "standard" public cloud service. To be precise, N100 CPU is normally able to run AXV2 instructions but some NAS/home server motherboard manufacturers sometimes disable this feature. Maybe is it possible to renable it on the BIOS, but I am away from my NAS so it will have to wait.
So, we decided to use Google Cloud. A common issue arises here: we cannot expose multiple ports (contrary to the instruction above) on a cloud service.
Two options are possible:
- Separate the service into two dockers (one for FastAPI backend and one for Streamlit frontend)
- Configure a reverse proxy like nginx to redirect requests to different internal ports
We decided to keep the second solution to install the API from a single Docker container.
**UPDATE 2** : AVX2 instructions have been enabled by accessing the home server motherboard BIOS. So the deployment is also done here and complete continuous deployment will be done as well.
In production (e.g. via docker-compose or cloud services), environment variables must be defined in the compose file or secret manager.
---
## 🔁 Continuous Integration / Continuous Deployment
## 🛠 Notes on AVX2 / Cloud Deployment
A GitHub Actions pipeline (CI/CD) is planned at the very root of the repo to:
**Update:** the NAS initially failed due to lack of AVX2 support. Some N100-based NAS motherboards disable AVX2 in BIOS.
- Automatically run unit tests at every push on the GitHub repository.
- Ensure API stability before any deployment (NAS or cloud).
- Update the Dockerfile and push it to DockerHub.
When not accessible, fallback deployment is done on **Google Cloud**.
Issue: public cloud services do **not allow multiple ports** to be exposed easily.
Unit tests will be defined in `./deployment/api/tests/`
Two solutions:
- Split into two containers: FastAPI + Streamlit,
- Use a **reverse proxy** (e.g., Nginx) to map ports.
- API docker is automatically updated on home server docker using watchtower docker (= cron with logging options)
➡️ We chose the second option to keep a **single Docker image**.
**Update 2:** AVX2 was enabled via BIOS. Deployment is now active **both locally (NAS)** and **on cloud**, with continuous deployment on NAS (see section below).
---
## 🔁 CI/CD – GitHub Actions
A pipeline is defined in `.github/workflows/` to:
- Run unit tests on every push,
- Check API health before deployment (local/cloud),
- Build and push Docker image to DockerHub.
Tests are defined in:
```
deployment/api/tests/
```
Docker image auto-redeployment on NAS is handled by **Watchtower**, which checks DockerHub updates (like a cron job, with logs).
---

235
docs/blog.fr.md Normal file
View file

@ -0,0 +1,235 @@
# ✨ 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 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
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
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** :
- Régression Logistique
- Naive Bayes
- SVM
- Random Forest
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.
![Scores modèles simples](./Basic-Models.png)
---
### 2. 🔥 Approche avancée : Word Embeddings + LSTM
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).
La classification est réalisée par un **LSTM**, capable d'exploiter la séquentialité des mots.
Le **modèle déployé est automatiquement sélectionné** entre Word2Vec + LSTM et GloVe + LSTM, selon la **précision maximale enregistrée dans MLFlow**.
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)
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).
---
### 3. 🤖 Approche transformer : DistilBERT, embeddings contextuels allégés
Nous avons utilisé **DistilBERT**, une version plus légère et plus rapide de BERT (créée par Hugging Face).
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**.
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.
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.
---
## 🎯 Pourquoi optimiser la précision (et non l'accuracy) ?
**Accuracy** = proportion de bonnes prédictions globales.
**Precision** = proportion de tweets *prévus positifs* qui sont *vraiment positifs*.
> Exemple : sur 100 tweets, si le modèle prédit 30 positifs, mais 15 sont des erreurs, la précision est de 50 %.
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 : **GridSearchCV optimisé sur la précision**
- Modèles DL : **EarlyStopping sur val_loss**, mais **sélection finale sur la précision**
---
## ⚙️ Environnement MLOps souverain
### 🖥️ Serveur personnel NAS + OpenMediaVault
MLFlow est installé sur un **NAS personnel (Intel N100)**, sous **OpenMediaVault (Debian)**.
Nous utilisons **MinIO** pour stocker artefacts et modèles, garantissant une gestion locale, souveraine et compatible S3.
> 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**.
Dashboard Expériences MLFlow :
![Dashboard MLFLow](./mlflow_experiments_dashboard.png)
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)
---
## 🧪 CI, tests et versioning
### ✅ Tests unitaires
- Vérifient le bon fonctionnement du prétraitement, des prédictions et de la gestion d’erreurs.
- Intégrés à **GitHub Actions**, **ils bloquent le déploiement si les tests échouent.**
### 🐳 Conteneurisation & CI/CD
L’API FastAPI est packagée via Docker, puis poussée sur DockerHub.
Un [pipeline CI/CD](https://github.com/GrgMdmn/OpenClassrooms_AI_Projects/blob/main/.github/workflows/p7_ci-cd.yml) complet garantit **un déploiement reproductible**. Il assure:
- La validation de tests unitaires
- La construction et le push de l'api conteneurisée sur dockerhub
![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)
---
### 🌐 Double déploiement (NAS + Google Cloud)
#### 🔧 Problème AVX2
Initialement, l’API ne fonctionnait pas sur le NAS (processeur Intel N100), car celui-ci ne prenait pas en charge les instructions **AVX2**, requises par TensorFlow.
> 🛠️ **Solution** : activation manuelle du support AVX2 dans le BIOS. Cette option est souvent désactivée par défaut sur les cartes mères orientées NAS pour des raisons de consommation énergétique ou de stabilité.
Depuis cette modification, le NAS peut exécuter l’API sans erreurs.
---
#### 🚀 API déployées
Le projet propose un **double déploiement** de l’API, en local et dans le cloud :
- 🏠 **NAS local** : [sentiment-api.greg-madman-nas.duckdns.org](https://sentiment-api.greg-madman-nas.duckdns.org)\
Interface Docker Compose sur le 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 de déploiement Google Cloud :
![déploiement google cloud](google_cloud_interface-1.png)
---
#### 🔄 Déploiement continu automatisé (NAS uniquement)
Pour assurer un **déploiement continu** sur le NAS, il a été choisi d'installer [**Watchtower**](https://containrrr.dev/watchtower/arguments/) pour rester dans un logique d'environnement 100% `docker`. Cet outil surveille toutes les **10 minutes** l’image Docker de l’API (`sentiment_api`) et, en cas de nouvelle version disponible sur DockerHub, redémarre automatiquement le conteneur avec la dernière image.
![alt text](watchtower_docker-1.png)
> Un **système d’alerte mail** notifie automatiquement toute mise à jour effectuée.
![alt text](watchtower_logs_by_email-1.png)
Ce mécanisme garantit que la version déployée sur le NAS reste toujours synchronisée avec la dernière version validée par les tests sur GitHub Actions, **sans intervention manuelle**.
![alt text](watchtower_logs-1.png)
---
## 📊 Monitoring maison
Une logique d’alerte légère embarquée dans l’API :
![1ère erreur](./api_first_error.png)
![2ème erreur](./api_second_error.png)
![3ème erreur](./api_third_error.png)
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.
![Rapport d'erreur envoyé par mail](./api_error_report.png)
---
## 🧠 Schéma global du pipeline MLOps
### Schéma synthétique
```
┌────────────┐
│ Données │
│ (tweets) │
└─────┬──────┘
│
Prétraitement (regex, tokenisation, lemmatisation, stopwords)
│
┌─────────▼──────────┐
│ Entraînement ML │
└─────────┬──────────┘
│
Log via MLFlow + artefacts MinIO
│
Sélection du meilleur modèle "avancé" (hors BERT) selon précision
│
┌─────────▼──────────┐
│ API FastAPI │
│ + Streamlit │
└─────────┬──────────┘
│
Déploiement :
├─ Google Cloud Run (manuel)
└─ NAS local (automatisé via Watchtower)
│
┌──────────▼─────────────┐
│ Monitoring léger maison│
│ (alertes sur mauvaises │
│ prédictions) │
└────────────────────────┘
```
### Schéma détaillé
![Diagramme détaillé](diagram.drawio.png)
---
## ✨ Conclusion
Ce projet démontre comment construire une chaîne MLOps complète, autonome et reproductible pour du NLP appliqué à Twitter.
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.
---
💬 Merci pour votre lecture !

View file

@ -1,235 +1,244 @@
# ✨ Analyse de sentiments et déploiement MLOps : retour d’expérience complet
# ✨ Sentiment Analysis and MLOps Deployment: A Complete Experience Report
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). 🧠🔧
As part of the *Air Paradis* project, we implemented a full sentiment analysis pipeline from tweets, covering everything from modeling to MLOps deployment. This blog post reviews the key project stages: methodological choices, model comparison, deployment of a robust API, and deployment on two environments (local + cloud). 🧠🔧
---
## 🔍 Trois approches de modélisation supervisée
## 🔍 Three Supervised Modeling Approaches
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é :
We tested three complementary approaches to predict the sentiment of a tweet. Each relies on a different text representation and a suitable learning algorithm:
### 1. 🧱 Approche simple : Bag-of-Words (BoW) + modèles de machine learning
### 1. 🧱 Simple Approach: Bag-of-Words (BoW) + Machine Learning Models
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).
Tweets are vectorized using BoW or BoW + TF-IDF, which count word frequencies.
Unlike other NLP projects, we avoided LDA (unsupervised, topic modeling oriented).
➡️ **Modèles testés** :
- Régression Logistique
➡️ **Tested models**:
- Logistic Regression
- Naive Bayes
- SVM
- Random Forest
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.
These models are trained with **GridSearchCV**, optimizing **precision (`precision`)**, our key business KPI. Various KPIs were computed on the test set.
![Scores modèles simples](./Basic-Models.png)
![Simple Models Scores](./Basic-Models.png)
---
### 2. 🔥 Approche avancée : Word Embeddings + LSTM
### 2. 🔥 Advanced Approach: Word Embeddings + LSTM
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).
We use dense Word2Vec vectors (trained) and GloVe 300d (pretrained by Stanford), which capture semantic similarities between words. However, these representations **do not take into account the exact context** (e.g., "cancelled" is probably very negative for a flight but positive in a legal case with charges against you).
La classification est réalisée par un **LSTM**, capable d'exploiter la séquentialité des mots.
Le **modèle déployé est automatiquement sélectionné** entre Word2Vec + LSTM et GloVe + LSTM, selon la **précision maximale enregistrée dans MLFlow**.
Classification is done by an **LSTM**, capable of exploiting word sequentiality.
The **deployed model is automatically selected** between Word2Vec + LSTM and GloVe + LSTM, according to the **highest precision recorded in MLFlow**.
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)
Model `Word2Vec + LSTM` (`test_precision: 0.797, val_precision: 0.795`):
![Advanced LSTM + Word2Vec Training Scores](./Word2Vec_LSTM_epoch9.png)
Model `GloVe 300d + LSTM` (`test_precision: 0.792, val_precision: 0.798`):
![Advanced LSTM + GloVe 300d Training Scores](./GloVe-300d_LSTM_epoch12.png)
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).
Although GloVe 300d + LSTM shows a slightly better precision score on the validation set than Word2Vec + LSTM, it remains lower on the test set (relatively negligible overall).
---
### 3. 🤖 Approche transformer : DistilBERT, embeddings contextuels allégés
### 3. 🤖 Transformer Approach: DistilBERT, Lightweight Contextual Embeddings
Nous avons utilisé **DistilBERT**, une version plus légère et plus rapide de BERT (créée par Hugging Face).
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**.
We used **DistilBERT**, a lighter and faster version of BERT (created by Hugging Face).
It retains **95% of BERT's performance** while being **40% smaller** and **60% faster**, making it especially suited for **training on limited GPUs** (Google Colab) and **faster inference**.
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.
Like BERT, DistilBERT produces **contextual embeddings**, meaning word meanings vary depending on context. For example, *"bank"* in *"I go to the bank"* and *"database bank"* will not have the same vector.
Il a été **fine-tuné** ici pour une classification binaire (positif/négatif), avec un tokenizer et un encodage spécifiques à son architecture.
It was **fine-tuned** here for binary classification (positive/negative), with a tokenizer and encoding specific to its architecture.
> **Modèle `DistilBERT`** (`test_precision : 0.861 , val_precision : 0.858`)
> **Model `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)
![DistilBERT Training Curves](./bert_training_curves_epoch1.png)
![Confusion Matrix](./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.
⚠️ **Limits**: Like all pretrained models, DistilBERT **is not sensitive to irony, puns, or implicit cultural references**.
Twitter is an ideal playground for this kind of nuanced content. These limits also apply to Word2Vec, GloVe, and classical models.
---
## 🎯 Pourquoi optimiser la précision (et non l'accuracy) ?
## 🎯 Why Optimize Precision (Not Accuracy)?
**Accuracy** = proportion de bonnes prédictions globales.
**Precision** = proportion de tweets *prévus positifs* qui sont *vraiment positifs*.
**Accuracy** = overall proportion of correct predictions.
**Precision** = proportion of *predicted positive* tweets that are *actually positive*.
> Exemple : sur 100 tweets, si le modèle prédit 30 positifs, mais 15 sont des erreurs, la précision est de 50 %.
> Example: out of 100 tweets, if the model predicts 30 positives but 15 are wrong, precision is 50%.
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**.
We preferred to **minimize false positives**, because in a sensitive context, **announcing a tweet as positive when it is negative can be problematic**.
- Modèles classiques : **GridSearchCV optimisé sur la précision**
- Modèles DL : **EarlyStopping sur val_loss**, mais **sélection finale sur la précision**
- Classical models: **GridSearchCV optimized for precision**
- Deep Learning models: **EarlyStopping on val_loss**, but **final selection based on precision**
---
## ⚙️ Environnement MLOps souverain
## ⚙️ Sovereign MLOps Environment
### 🖥️ Serveur personnel NAS + OpenMediaVault
### 🖥️ Personal NAS Server + OpenMediaVault
MLFlow est installé sur un **NAS personnel (Intel N100)**, sous **OpenMediaVault (Debian)**.
Nous utilisons **MinIO** pour stocker artefacts et modèles, garantissant une gestion locale, souveraine et compatible S3.
MLFlow is installed on a **personal NAS (Intel N100)** running **OpenMediaVault (Debian)**.
We use **MinIO** to store artifacts and models, ensuring local, sovereign, and S3-compatible management.
> 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**.
> Notebooks, run on Google Colab to leverage GPUs, log in MLFlow and send models to MinIO via **environment variables**.
Dashboard Expériences MLFlow :
![Dashboard MLFLow](./mlflow_experiments_dashboard.png)
MLFlow Experiments Dashboard:
![MLFlow Dashboard](./mlflow_experiments_dashboard.png)
Modèles en production (registre de modèles):
Production Models (model registry):
![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)
Best non-BERT selected model:
![Selected Model](mlflow_models_registry_best_LSTM_model-1.png)
![Best Non-BERT Model](mlflolw_experiments_dashboard_best_LSTM_model-1.png)
---
## 🧪 CI, tests et versioning
## 🧪 CI, Testing and Versioning
### ✅ Tests unitaires
### ✅ Unit Tests
- Vérifient le bon fonctionnement du prétraitement, des prédictions et de la gestion d’erreurs.
- Intégrés à **GitHub Actions**, **ils bloquent le déploiement si les tests échouent.**
- Check correct preprocessing, predictions, and error handling.
- Integrated into **GitHub Actions**, **blocking deployment if tests fail.**
### 🐳 Conteneurisation & CI/CD
### 🐳 Containerization & CI/CD
L’API FastAPI est packagée via Docker, puis poussée sur DockerHub.
Un [pipeline CI/CD](https://github.com/GrgMdmn/OpenClassrooms_AI_Projects/blob/main/.github/workflows/p7_ci-cd.yml) complet garantit **un déploiement reproductible**. Il assure:
- La validation de tests unitaires
- La construction et le push de l'api conteneurisée sur dockerhub
The FastAPI API is packaged with Docker, then pushed to DockerHub.
A full [CI/CD pipeline](https://github.com/GrgMdmn/OpenClassrooms_AI_Projects/blob/main/.github/workflows/p7_ci-cd.yml) guarantees **reproducible deployment**, ensuring:
- Validation of unit tests
- Build and push of the containerized API to DockerHub
![GitHub Workflow](github_ci-cd-1.png)
![github workflow](github_ci-cd-1.png)
---
## 🚀 Déploiement de l’API FastAPI (double)
## 🚀 FastAPI API Deployment (Dual)
### 🔧 Backend : FastAPI
### 🔧 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`
- Exposes a `/predict` endpoint
- Dynamically loads the **best Word Embedding + LSTM model** via MLFlow Model Registry
- Applies appropriate embeddings according to the `embedding_type` tag
### 💻 Interface Streamlit
### 💻 Streamlit Interface
Interface locale pour tester les requêtes, debug, démonstrations.
Local interface for request testing, debugging, demonstrations.
![Streamlit Local](./streamlit_interface_local.png)
---
### 🌐 Double déploiement (NAS + Google Cloud)
### 🌐 Dual Deployment (NAS + Google Cloud)
#### 🔧 Problème AVX2
#### 🔧 AVX2 Issue
Initialement, l’API ne fonctionnait pas sur le NAS (processeur Intel N100), car celui-ci ne prenait pas en charge les instructions **AVX2**, requises par TensorFlow.
Initially, the API did not run on the NAS (Intel N100 CPU) because it did not support **AVX2 instructions**, required by TensorFlow.
> 🛠️ **Solution** : activation manuelle du support AVX2 dans le BIOS. Cette option est souvent désactivée par défaut sur les cartes mères orientées NAS pour des raisons de consommation énergétique ou de stabilité.
> 🛠️ **Solution**: manual activation of AVX2 support in BIOS. This option is often disabled by default on NAS motherboards for power consumption or stability reasons.
Depuis cette modification, le NAS peut exécuter l’API sans erreurs.
Since this change, the NAS can run the API without errors.
---
#### 🚀 API déployées
#### 🚀 Deployed APIs
Le projet propose un **double déploiement** de l’API, en local et dans le cloud :
The project offers a **dual API deployment**, locally and in the cloud:
- 🏠 **NAS local** : [sentiment-api.greg-madman-nas.duckdns.org](https://sentiment-api.greg-madman-nas.duckdns.org)\
Interface Docker Compose sur le NAS :
![docker compose openmediavault](openmediavault_docker-compose_settings-1.png)
- 🏠 **Local NAS**: [sentiment-api.greg-madman-nas.duckdns.org](https://sentiment-api.greg-madman-nas.duckdns.org)
Docker Compose interface on the 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 de déploiement Google Cloud :
![déploiement google cloud](google_cloud_interface-1.png)
- ☁️ **Google Cloud Run**: [sentiment-api-service-7772256003.europe-west1.run.app](https://sentiment-api-service-7772256003.europe-west1.run.app)
Google Cloud deployment interface:
![Google Cloud Deployment](google_cloud_interface-1.png)
---
#### 🔄 Déploiement continu automatisé (NAS uniquement)
#### 🔄 Automated Continuous Deployment (NAS only)
Pour assurer un **déploiement continu** sur le NAS, il a été choisi d'installer [**Watchtower**](https://containrrr.dev/watchtower/arguments/) pour rester dans un logique d'environnement 100% `docker`. Cet outil surveille toutes les **10 minutes** l’image Docker de l’API (`sentiment_api`) et, en cas de nouvelle version disponible sur DockerHub, redémarre automatiquement le conteneur avec la dernière image.
![alt text](watchtower_docker-1.png)
To ensure **continuous deployment** on the NAS, we chose to install [**Watchtower**](https://containrrr.dev/watchtower/arguments/) to maintain a 100% `docker` environment. This tool checks every **10 minutes** the Docker image of the API (`sentiment_api`) and, if a new version is available on DockerHub, automatically restarts the container with the latest image.
![Watchtower Docker](watchtower_docker-1.png)
> Un **système d’alerte mail** notifie automatiquement toute mise à jour effectuée.
![alt text](watchtower_logs_by_email-1.png)
> An **email alert system** automatically notifies any update performed.
![Watchtower Logs by Email](watchtower_logs_by_email-1.png)
Ce mécanisme garantit que la version déployée sur le NAS reste toujours synchronisée avec la dernière version validée par les tests sur GitHub Actions, **sans intervention manuelle**.
![alt text](watchtower_logs-1.png)
This mechanism ensures that the version deployed on the NAS always stays synchronized with the latest version validated by GitHub Actions tests, **without manual intervention**.
![Watchtower Logs](watchtower_logs-1.png)
---
## 📊 Monitoring maison
## 📊 Home-made Monitoring
Une logique d’alerte légère embarquée dans l’API :
A lightweight alert logic embedded in the API:
![1ère erreur](./api_first_error.png)
![2ème erreur](./api_second_error.png)
![3ème erreur](./api_third_error.png)
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.
![Rapport d'erreur envoyé par mail](./api_error_report.png)
![1st Error](./api_first_error.png)
![2nd Error](./api_second_error.png)
![3rd Error](./api_third_error.png)
If **3 consecutive prediction errors** are detected within a **5-minute window**, a report is automatically generated.
![Error Report Sent by Email](./api_error_report.png)
---
## 🧠 Schéma global du pipeline MLOps
## 🧠 Overall MLOps Pipeline Diagram
### Summary Diagram
### Schéma synthétique
```
┌────────────┐
│ Données │
│ (tweets) │
│ Data │
│ (tweets) │
└─────┬──────┘
│
Prétraitement (regex, tokenisation, lemmatisation, stopwords)
Preprocessing (regex, tokenization, lemmatization, stopwords)
│
┌─────────▼──────────┐
│ Entraînement ML │
│ ML Training │
└─────────┬──────────┘
│
Log via MLFlow + artefacts MinIO
Logging via MLFlow + artifacts MinIO
│
Sélection du meilleur modèle "avancé" (hors BERT) selon précision
Select best "advanced" model (excluding BERT) by precision
│
┌─────────▼──────────┐
│ API FastAPI │
│ + Streamlit │
│ FastAPI │
│ + Streamlit │
└─────────┬──────────┘
│
Déploiement :
├─ Google Cloud Run (manuel)
└─ NAS local (automatisé via Watchtower)
Deployment:
├─ Google Cloud Run (manual)
└─ Local NAS (automated via Watchtower)
│
┌──────────▼─────────────┐
│ Monitoring léger maison│
│ (alertes sur mauvaises │
│ prédictions) │
│ Lightweight in-house │
│ Monitoring │
│ (alerts on bad │
│ predictions) │
└────────────────────────┘
```
### Schéma détaillé
![Diagramme détaillé](diagram.drawio.png)
### Detailed Diagram
![Detailed Diagram](diagram.drawio.png)
---
## ✨ Conclusion
Ce projet démontre comment construire une chaîne MLOps complète, autonome et reproductible pour du NLP appliqué à Twitter.
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.
This project demonstrates how to build a complete, autonomous, and reproducible MLOps pipeline for NLP applied to Twitter.
It highlights the **strengths and limitations of the models** (including BERT), the need for a **metric adapted to the context** (precision vs accuracy), and the importance of **controlled deployment**, even on personal infrastructure.
---
💬 Merci pour votre lecture !
💬 Thank you for reading!
Demander à ChatGPT

View file

@ -83,7 +83,7 @@
<mxPoint x="-14" y="-1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="vVvrGs-YRXqjEHhurR4v-106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=oval;endFill=1;fillColor=#60a917;strokeColor=#2D7600;" parent="1" source="vVvrGs-YRXqjEHhurR4v-47" edge="1">
<mxCell id="vVvrGs-YRXqjEHhurR4v-106" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=oval;endFill=1;fillColor=#60a917;strokeColor=#2D7600;strokeWidth=2;" parent="1" source="vVvrGs-YRXqjEHhurR4v-47" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1140" y="712" as="targetPoint" />
</mxGeometry>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 135 KiB

46
notebooks/README.fr.md Normal file
View file

@ -0,0 +1,46 @@
# Notebooks
Ce dossier contient les notebooks Jupyter utilisés pour le développement des modèles et les analyses du projet.
## Contenu
- Analyse exploratoire des données
- Modèles classiques (régression logistique, SVM, etc.)
- Modèles avancés (LSTM avec embeddings GloVe)
- Modèle BERT
## Note concernant les fichiers requirements
Les fichiers `*requirements.txt` ont été générés depuis un environnement Google Colab. Cet environnement inclut de nombreuses librairies préinstallées, dont toutes ne sont pas nécessaires au projet.
Un nettoyage manuel ou une génération spécifique des fichiers requirements sera nécessaire pour alléger ces fichiers avant un usage en production ou un déploiement.
---
## Résumé des performances des modèles
### Modèles classiques (entraînés sur 200 000 tweets)
| Modèle | Précision | Temps d'entraînement|
|--------------------------------|-----------|---------------------|
| Random Forest + BoW | 0,7798 | ~5 heures |
| Régression logistique + TF-IDF | 0,7796 | <2 minutes |
### Modèles avancés basés sur des embeddings (entraînés sur 200 000 tweets, accélérés GPU sur Google Colab)
| Modèle | Précision | Temps d'entraînement|
|------------------------|-----------|---------------------|
| Word2Vec + LSTM | 0,797 | 57 secondes |
| GloVe (300d) + LSTM | 0,792 | 47 secondes |
### Modèle de pointe (entraîné sur 100 000 tweets en raison des limites RAM)
| Modèle | Précision | Temps d'entraînement|
|---------|-----------|---------------------|
| BERT | 0,882 | 1,3 minutes |
---
Ce résumé met en lumière les compromis entre précision des modèles et temps d’entraînement. Si les modèles classiques sont plus rapides à entraîner, les modèles avancés utilisant des embeddings et LSTM offrent une meilleure précision, avec BERT qui atteint la meilleure précision malgré un jeu de données plus restreint.
L’écart de précision entre BERT et les modèles à embeddings peut s’expliquer principalement par la capacité bidirectionnelle de BERT à comprendre le contexte.

View file

@ -22,7 +22,7 @@ Manual cleanup or specific generation of requirements files will be needed to sl
### Classical Models (trained on 200,000 tweets)
| Model | Precision | Training Time |
|-----------------------------|-----------|-----------------|
|------------------------------|-----------|-----------------|
| Random Forest + BoW | 0.7798 | ~5 hours |
| Logistic Regression + TF-IDF | 0.7796 | <2 minutes |

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB