diff --git a/README.fr.md b/README.fr.md new file mode 100644 index 0000000..86ea4c9 --- /dev/null +++ b/README.fr.md @@ -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** \ No newline at end of file diff --git a/README.md b/README.md index ada6dff..1accfaa 100644 --- a/README.md +++ b/README.md @@ -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** diff --git a/deployment/README.fr.md b/deployment/README.fr.md new file mode 100644 index 0000000..c4b67d2 --- /dev/null +++ b/deployment/README.fr.md @@ -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). + +--- diff --git a/deployment/README.md b/deployment/README.md index 202b079..bde374f 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -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). + +--- diff --git a/docs/blog.fr.md b/docs/blog.fr.md new file mode 100644 index 0000000..e8fed94 --- /dev/null +++ b/docs/blog.fr.md @@ -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 ! \ No newline at end of file diff --git a/docs/blog.md b/docs/blog.md index e8fed94..f2eac36 100644 --- a/docs/blog.md +++ b/docs/blog.md @@ -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 ! \ No newline at end of file +💬 Thank you for reading! + + + + + + + +Demander à ChatGPT diff --git a/docs/diagram.drawio b/docs/diagram.drawio index 25afb79..3fe871f 100644 --- a/docs/diagram.drawio +++ b/docs/diagram.drawio @@ -83,7 +83,7 @@ - + diff --git a/docs/diagram.drawio.png b/docs/diagram.drawio.png index 34e1716..9e4d6c5 100644 Binary files a/docs/diagram.drawio.png and b/docs/diagram.drawio.png differ diff --git a/notebooks/README.fr.md b/notebooks/README.fr.md new file mode 100644 index 0000000..c59b4fc --- /dev/null +++ b/notebooks/README.fr.md @@ -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. diff --git a/notebooks/README.md b/notebooks/README.md index f55baa9..b58630e 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -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 | diff --git a/presentation/git_cd_docker.png b/presentation/git_cd_docker.png new file mode 100644 index 0000000..b954482 Binary files /dev/null and b/presentation/git_cd_docker.png differ diff --git a/presentation/git_ci_tests.png b/presentation/git_ci_tests.png new file mode 100644 index 0000000..20a3b41 Binary files /dev/null and b/presentation/git_ci_tests.png differ diff --git a/presentation/git_commands.png b/presentation/git_commands.png new file mode 100644 index 0000000..19b6c66 Binary files /dev/null and b/presentation/git_commands.png differ