Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- .gcloudignore +5 -0
- .gitattributes +3 -0
- Livrables.md +46 -0
- Project_description.md +31 -0
- README.md +6 -7
- README_API.md +61 -0
- api/Dockerfile +28 -0
- api/main.py +104 -0
- api/requirements.txt +7 -0
- app/Dockerfile +21 -0
- app/Screenshot_streamlit_app.png +3 -0
- app/app.py +104 -0
- app/requirements.txt +3 -0
- guide_projet_8.md +523 -0
- mlflow.db +3 -0
- models/best_unet_model.keras +3 -0
- mon_avancement.md +38 -0
- notebooks/notebook_entrainement.ipynb +0 -0
- notebooks/notebook_exploratoire.ipynb +0 -0
- requirements.txt +12 -0
- resume_execution_notebook.md +41 -0
- tests/test_api.py +79 -0
.gcloudignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.git
|
| 2 |
+
.github
|
| 3 |
+
.venv
|
| 4 |
+
__pycache__
|
| 5 |
+
notebooks/
|
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
app/Screenshot_streamlit_app.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
mlflow.db filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
models/best_unet_model.keras filter=lfs diff=lfs merge=lfs -text
|
Livrables.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Livrables
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
Les scripts développés sur un notebook permettant l’exécution du pipeline complet :
|
| 5 |
+
Ce livrable vous servira à présenter le caractère “industrialisable” de votre travail en particulier le générateur de données.
|
| 6 |
+
Une API (Flask ou FastAPI) déployée sur le Cloud (Azure, Heroku, PythonAnywhere ou toute autre solution), pour exposer votre modèle entraîné et qui recevra en entrée une image et retournera le mask prédit (les segments identifiés par votre modèle) :
|
| 7 |
+
Ce livrable permettra à Laura d’utiliser facilement votre modèle.
|
| 8 |
+
Une application (Flask, Streamlit) de présentation des résultats qui consomme l’API de prédiction, déployée sur le Cloud (Azure, Heroku, PythonAnywhere ou toute autre solution). Cette application sera l’interface pour tester l’API et intégrera les fonctionnalités suivantes : affichage de la liste des id des images disponibles, lancement de la prédiction du mask pour l’id sélectionné par appel à l’API, et affichage de l’image réelle, du mask réel et du mask prédit :
|
| 9 |
+
Ce livrable permettra d’illustrer votre travail auprès de vos collègues
|
| 10 |
+
Une note technique de 10 pages environ contenant une présentation des différentes approches et une synthèse de l’état de l’art, la présentation plus détaillée du modèle et de l’architecture retenue, une synthèse des résultats obtenus (incluant les gains obtenus avec les approches d’augmentation des données) et une conclusion avec des pistes d’amélioration envisageables :
|
| 11 |
+
Ce livrable vous servira à présenter votre démarche technique à vos collègues.
|
| 12 |
+
Un support de présentation (type Power Point) de votre démarche méthodologique (30 slides maximum) :
|
| 13 |
+
Ce livrable vous permettra de présenter vos résultats à Laura.
|
| 14 |
+
Déposez sur la plateforme, dans un dossier zip nommé “Titre_du_projet_nom_prénom”, votre livrable nommé comme suit : Nom_Prénom_n° du livrable_nom du livrable_date de démarrage du projet. Cela donnera :
|
| 15 |
+
|
| 16 |
+
Nom_Prénom_1_scripts_mmaaaa
|
| 17 |
+
Nom_Prénom_2_API_mmaaaa
|
| 18 |
+
Nom_Prénom_3_application_Flask_mmaaaa
|
| 19 |
+
Nom_Prénom_4_note_technique_mmaaaa
|
| 20 |
+
Nom_Prénom_5_presentation_mmaaaa
|
| 21 |
+
Par exemple, votre premier livrable peut être nommé comme suit : Dupont_Jean_1_scripts_012024.
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Soutenance:
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
Pendant la soutenance, l’évaluateur ne jouera aucun rôle en particulier. Vous lui présenterez l’ensemble de votre travail.
|
| 29 |
+
|
| 30 |
+
Présentation (20 minutes)
|
| 31 |
+
Présentation du contexte, des objectifs, des principes de segmentation et des mesures de performance qui seront utilisées pour comparer les modèles (5 minutes)
|
| 32 |
+
Présentation des différents modèles, simulations et comparaisons des modèles (10 minutes).
|
| 33 |
+
Mise en production d’un modèle (5 minutes) :
|
| 34 |
+
Architecture API et application Web et démarche de mise en production sur le Cloud choisi par l’étudiant
|
| 35 |
+
démonstration de fonctionnement de l’application et de la prédiction de segmentation d’une image (mask).
|
| 36 |
+
Discussion (5 minutes)
|
| 37 |
+
L’évaluateur vous challengera sur vos choix.
|
| 38 |
+
Débriefing (5 minutes)
|
| 39 |
+
À la fin de la soutenance, vous pourrez débriefer ensemble.
|
| 40 |
+
Votre présentation devrait durer 20 minutes (+/- 5 minutes). Puisque le respect des durées des présentations est important en milieu professionnel, les présentations en dessous de 15 minutes ou au-dessus de 25 minutes peuvent être refusées.
|
| 41 |
+
|
| 42 |
+
Concernant la mise en production de l’API, plusieurs solutions s’offrent à vous, en particulier Azure webapp, AWS et Heroku. À vous de choisir la solution qui vous convient le mieux.
|
| 43 |
+
|
| 44 |
+
Dans le cadre de l’utilisation de Heroku, étant devenu payant depuis fin novembre 2022, les coûts liés à votre projet seront à votre charge. Vous êtes donc libre de vous investir financièrement si vous le souhaitez, mais vous n’avez aucune obligation de le faire pour réaliser ce projet.
|
| 45 |
+
|
| 46 |
+
Quelque soit la solution Cloud choisie, l'étudiant et l'évaluateur veilleront à enregistrer pendant la soutenance la démo de l'application en production, ce qui permettra au jury de visionner cette démo, sans que l'étudiant n'ait à maintenir son application sur le Cloud. Maintenir l’application dans le Cloud pourrait en effet engendrer des coûts.
|
Project_description.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Projet_8
|
| 2 |
+
|
| 3 |
+
Future Vision Transport est une entreprise qui conçoit des systèmes embarqués de vision par ordinateur pour les véhicules autonomes.
|
| 4 |
+
|
| 5 |
+
Vous êtes l’un des ingénieurs IA au sein de l’équipe R&D de cette entreprise. Votre équipe est composée d’ingénieurs aux profils variés. Chacun des membres de l’équipe est spécialisé sur une des parties du système embarqué de vision par ordinateur.
|
| 6 |
+
|
| 7 |
+
Voici les différentes parties du système :
|
| 8 |
+
|
| 9 |
+
acquisition des images en temps réel
|
| 10 |
+
traitement des images
|
| 11 |
+
segmentation des images (c’est vous !)
|
| 12 |
+
système de décision
|
| 13 |
+
Vous travaillez sur la partie de segmentation des images (3) qui est alimentée par le bloc de traitement des images (2) et qui alimente le système de décision (4).
|
| 14 |
+
|
| 15 |
+
Votre rôle est de concevoir un premier modèle de segmentation d’images qui devra s’intégrer facilement dans la chaîne complète du système embarqué.
|
| 16 |
+
|
| 17 |
+
Lors d’une première phase de cadrage, vous avez récolté les avis de Franck et Laura, qui travaillent sur les parties avant et après votre intervention :
|
| 18 |
+
|
| 19 |
+
Franck, en charge du traitement des images (2) :
|
| 20 |
+
|
| 21 |
+
Le jeu de données que Franck utilise est disponible à ce lien, ou en téléchargement direct à ces liens : 1 ou 2, (images segmentées et annotées de caméras embarquées). On a uniquement besoin des 8 catégories principales (et non pas des 32 sous-catégories)
|
| 22 |
+
Laura, en charge du système de décision (4)
|
| 23 |
+
|
| 24 |
+
Souhaite une API simple à utiliser.
|
| 25 |
+
L’API prend en entrée une image et renvoie la segmentation de l’image de l’algo.
|
| 26 |
+
|
| 27 |
+
Pour récapituler, vous avez dressé un plan d’action, avec les points suivants :
|
| 28 |
+
|
| 29 |
+
entraîner un modèle de segmentation des images sur les 8 catégories principales. Keras est le framework de travail commun à toute l’équipe. Attention aux contraintes de Franck !
|
| 30 |
+
concevoir une API de prédiction (Flask ou FastAPI) qui sera utilisée par Laura et la déployer sur le Cloud (Azure, Heroku, PythonAnywhere ou toute autre solution). Cette API prend en entrée une image et renvoie le mask prédit (segments prédits de l’image).
|
| 31 |
+
concevoir une application web (Flask, Streamlit) de présentation des résultats et la déployer sur le Cloud (Azure, Heroku, PythonAnywhere ou toute autre solution). Cette application sera l’interface pour tester l’API et afficher les images et masks.
|
README.md
CHANGED
|
@@ -1,11 +1,10 @@
|
|
| 1 |
---
|
| 2 |
-
title: Projet 8
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
-
|
|
|
|
| 9 |
---
|
| 10 |
-
|
| 11 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Projet 8 API
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
app_port: 7860
|
| 9 |
+
dockerfile: api/Dockerfile
|
| 10 |
---
|
|
|
|
|
|
README_API.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Ce dossier, P7, est le projet 7 avant que je ne fasse le docker init. J'ai cloné le projet qui s'appelle maintenant Projet_7 dans lequel je fais le docker init.
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
Build l'image Docker:
|
| 5 |
+
|
| 6 |
+
docker build \
|
| 7 |
+
-t predictionsentiments \
|
| 8 |
+
-f Projet_7_API/Dockerfile Projet_7_API
|
| 9 |
+
|
| 10 |
+
Runner l'image Docker:
|
| 11 |
+
docker run -p 8000:8000 --name predictionsentiments_container predictionsentiments
|
| 12 |
+
|
| 13 |
+
POur accéder à la documentation:
|
| 14 |
+
http://0.0.0.0:8000/docs
|
| 15 |
+
|
| 16 |
+
# AZURE PORTAL : Pour vérifier où mes images docker sont:
|
| 17 |
+
container registries dans la barre de recherche Azure
|
| 18 |
+
Services < dépôts
|
| 19 |
+
predictionsentimentsregistry est mon registry situé dans mes Dépôts Azure
|
| 20 |
+
L'application est déployée via le 'app service'
|
| 21 |
+
|
| 22 |
+
# MLflow
|
| 23 |
+
Pour lancer MLFlow il faut taper 'mlflow server'
|
| 24 |
+
Ensuite, aller à http://127.0.0.1:5000/
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
# Lancer l'application
|
| 28 |
+
cd Projet_8_API
|
| 29 |
+
uvicorn main:app --host 0.0.0.0 --port 8000
|
| 30 |
+
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# Feeling classifier
|
| 35 |
+
|
| 36 |
+
Pour lancer l'API
|
| 37 |
+
|
| 38 |
+
fastapi dev main.py
|
| 39 |
+
|
| 40 |
+
Ouvir http://127.0.0.1:8000/docs
|
| 41 |
+
|
| 42 |
+
A faire:
|
| 43 |
+
|
| 44 |
+
- Installer Docker Desktop
|
| 45 |
+
- Faire un Dockerfile avec l'api dedans et qui marche
|
| 46 |
+
- Creer un Github
|
| 47 |
+
- Creer un Github workflow pour build l'image
|
| 48 |
+
- Creer un compte Azure, creer une azure app
|
| 49 |
+
- Deployer l'image de Github sur Azure app,
|
| 50 |
+
- Avoir l'API qui fonctionne sur Azure APP
|
| 51 |
+
|
| 52 |
+
# Fonctionnement de main.py
|
| 53 |
+
|
| 54 |
+
main.py :
|
| 55 |
+
Le modèle est chargé dynamiquement au lancement de l'API via lifespan pour une meilleure gestion de la mémoire.
|
| 56 |
+
Le point de terminaison /segmentation a été mis à jour : il convertit désormais l'image entrante, la redimensionne en 256x512, la normalise, fait la prédiction via notre modèle Keras, et génère un masque coloré basé sur les couleurs Cityscapes.
|
| 57 |
+
L'image du masque est finalement renvoyée via StreamingResponse au format PNG, ce qui est le standard le plus optimisé pour les API d'images avec FastAPI.
|
| 58 |
+
Un point de terminaison /health a été ajouté pour vérifier si l'API est saine et le modèle bien chargé.
|
| 59 |
+
Attention : Comme mentionné dans le Walkthrough, bien que notre modèle soit léger, TensorFlow reste une librairie très lourde en RAM. S'il s'avère que l'API crashe par manque de mémoire sur Render (limite de 512 Mo), il faudra envisager de convertir notre modèle en format TFLite ou ONNX.
|
| 60 |
+
|
| 61 |
+
Si un jour j'ai un serveur plus puissant et je souhaite changer de modèle il suffira de changer la variable MODEL_PATH = 'best_mobilenet_unet_model.keras' dans le main.py
|
api/Dockerfile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 4 |
+
ENV PYTHONUNBUFFERED=1
|
| 5 |
+
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
|
| 8 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 9 |
+
build-essential \
|
| 10 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
COPY api/requirements.txt .
|
| 13 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 14 |
+
|
| 15 |
+
COPY api/ api/
|
| 16 |
+
COPY models/ models/
|
| 17 |
+
|
| 18 |
+
WORKDIR /app/api
|
| 19 |
+
|
| 20 |
+
# --- Configuration du port (Décommenter selon le Cloud choisi) ---
|
| 21 |
+
|
| 22 |
+
# Pour Google Cloud Run (ou local standard) :
|
| 23 |
+
# EXPOSE 8000
|
| 24 |
+
# CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
| 25 |
+
|
| 26 |
+
# Pour Hugging Face Spaces (Port 7860 par défaut) :
|
| 27 |
+
EXPOSE 7860
|
| 28 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
api/main.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import io
|
| 3 |
+
import logging
|
| 4 |
+
from contextlib import asynccontextmanager
|
| 5 |
+
|
| 6 |
+
from fastapi import FastAPI, UploadFile, File
|
| 7 |
+
from fastapi.responses import StreamingResponse
|
| 8 |
+
from PIL import Image
|
| 9 |
+
import numpy as np
|
| 10 |
+
|
| 11 |
+
# Setup logger
|
| 12 |
+
logging.basicConfig(
|
| 13 |
+
level=logging.INFO,
|
| 14 |
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
| 15 |
+
)
|
| 16 |
+
logger = logging.getLogger(__name__)
|
| 17 |
+
|
| 18 |
+
# Note : Le code pour Azure Log Analytics peut être configuré ici
|
| 19 |
+
# en utilisant azure-monitor-opentelemetry (si la clé de connexion est fournie via env)
|
| 20 |
+
# try:
|
| 21 |
+
# from azure.monitor.opentelemetry import configure_azure_monitor
|
| 22 |
+
# if "APPLICATIONINSIGHTS_CONNECTION_STRING" in os.environ:
|
| 23 |
+
# configure_azure_monitor()
|
| 24 |
+
# except ImportError:
|
| 25 |
+
# pass
|
| 26 |
+
|
| 27 |
+
CLASSES = ['void', 'flat', 'construction', 'object', 'nature', 'sky', 'human', 'vehicle']
|
| 28 |
+
COLORS = [
|
| 29 |
+
[0, 0, 0], [128, 64, 128], [70, 70, 70], [153, 153, 153],
|
| 30 |
+
[107, 142, 35], [70, 130, 180], [220, 20, 60], [0, 0, 142]
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
# Variables globales pour l'état de l'application
|
| 34 |
+
model = None
|
| 35 |
+
MODEL_PATH = os.path.join(os.path.dirname(__file__), '../models/best_unet_model.keras')
|
| 36 |
+
|
| 37 |
+
@asynccontextmanager
|
| 38 |
+
async def lifespan(app: FastAPI):
|
| 39 |
+
global model
|
| 40 |
+
if os.path.exists(MODEL_PATH):
|
| 41 |
+
try:
|
| 42 |
+
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
|
| 43 |
+
import tensorflow as tf
|
| 44 |
+
logger.info("⏳ Chargement du modèle Keras...")
|
| 45 |
+
model = tf.keras.models.load_model(MODEL_PATH, compile=False)
|
| 46 |
+
logger.info("✅ Modèle chargé avec succès !")
|
| 47 |
+
except Exception as e:
|
| 48 |
+
model = None
|
| 49 |
+
logger.error(f"❌ Erreur critique de TensorFlow : {e}")
|
| 50 |
+
else:
|
| 51 |
+
logger.warning(f"⚠️ Modèle introuvable ({MODEL_PATH}). L'API tourne en 'MOCK MODE'.")
|
| 52 |
+
yield
|
| 53 |
+
# Nettoyage si nécessaire
|
| 54 |
+
model = None
|
| 55 |
+
|
| 56 |
+
app = FastAPI(
|
| 57 |
+
title="Cityscapes Segmentation API",
|
| 58 |
+
description="API de segmentation sémantique pour Future Vision Transport",
|
| 59 |
+
version="1.0",
|
| 60 |
+
lifespan=lifespan
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
@app.get("/")
|
| 64 |
+
async def root():
|
| 65 |
+
return {"message": "Service back-end de prédiction Actif."}
|
| 66 |
+
|
| 67 |
+
@app.get("/health")
|
| 68 |
+
def health_check():
|
| 69 |
+
return {"status": "ok", "model_active": model is not None}
|
| 70 |
+
|
| 71 |
+
@app.post("/segmentation")
|
| 72 |
+
async def segmentation(file: UploadFile = File(...)):
|
| 73 |
+
# Lire l'image envoyée
|
| 74 |
+
image_bytes = await file.read()
|
| 75 |
+
image = Image.open(io.BytesIO(image_bytes)).convert('RGB')
|
| 76 |
+
|
| 77 |
+
IMG_HEIGHT, IMG_WIDTH = 256, 512
|
| 78 |
+
img_resized = image.resize((IMG_WIDTH, IMG_HEIGHT))
|
| 79 |
+
|
| 80 |
+
if model is not None:
|
| 81 |
+
# Prétraitement de l'image
|
| 82 |
+
img_array = np.array(img_resized) / 255.0
|
| 83 |
+
img_batch = np.expand_dims(img_array, axis=0)
|
| 84 |
+
|
| 85 |
+
# Prédiction
|
| 86 |
+
prediction = model.predict(img_batch, verbose=0)[0]
|
| 87 |
+
mask_classes = np.argmax(prediction, axis=-1).astype(np.uint8)
|
| 88 |
+
else:
|
| 89 |
+
# Mock Mode : générer un masque vide si aucun modèle n'est chargé
|
| 90 |
+
mask_classes = np.zeros((IMG_HEIGHT, IMG_WIDTH), dtype=np.uint8)
|
| 91 |
+
|
| 92 |
+
# Colorisation du masque
|
| 93 |
+
mask_colored = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8)
|
| 94 |
+
for class_id, color in enumerate(COLORS):
|
| 95 |
+
mask_colored[mask_classes == class_id] = color
|
| 96 |
+
|
| 97 |
+
mask_img = Image.fromarray(mask_colored)
|
| 98 |
+
|
| 99 |
+
# Convertir l'image en bytes pour la réponse
|
| 100 |
+
img_io = io.BytesIO()
|
| 101 |
+
mask_img.save(img_io, format="PNG")
|
| 102 |
+
img_io.seek(0)
|
| 103 |
+
|
| 104 |
+
return StreamingResponse(img_io, media_type="image/png")
|
api/requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi==0.128.8
|
| 2 |
+
uvicorn==0.39.0
|
| 3 |
+
python-multipart==0.0.20
|
| 4 |
+
tensorflow-cpu==2.14.0
|
| 5 |
+
numpy
|
| 6 |
+
pillow
|
| 7 |
+
azure-monitor-opentelemetry
|
app/Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# On installe les requirements de l'application
|
| 6 |
+
COPY app/requirements.txt .
|
| 7 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 8 |
+
|
| 9 |
+
# On copie le code de l'application et la donnée (images de test)
|
| 10 |
+
COPY app/ app/
|
| 11 |
+
COPY data/ data/
|
| 12 |
+
|
| 13 |
+
# --- Configuration du port (Décommenter selon le Cloud choisi) ---
|
| 14 |
+
|
| 15 |
+
# Pour Google Cloud Run (ou local standard) :
|
| 16 |
+
# EXPOSE 8501
|
| 17 |
+
# CMD ["streamlit", "run", "app/app.py", "--server.port", "8501", "--server.address", "0.0.0.0"]
|
| 18 |
+
|
| 19 |
+
# Pour Hugging Face Spaces (Port 7860 par défaut) :
|
| 20 |
+
EXPOSE 7860
|
| 21 |
+
CMD ["streamlit", "run", "app/app.py", "--server.port", "7860", "--server.address", "0.0.0.0"]
|
app/Screenshot_streamlit_app.png
ADDED
|
Git LFS Details
|
app/app.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import requests
|
| 3 |
+
import glob
|
| 4 |
+
import os
|
| 5 |
+
from PIL import Image
|
| 6 |
+
import base64
|
| 7 |
+
import io
|
| 8 |
+
|
| 9 |
+
# --- 1. Configuration UI ---
|
| 10 |
+
st.set_page_config(page_title="Future Vision Transport - Démos", layout="wide", page_icon="🚗")
|
| 11 |
+
|
| 12 |
+
st.title("🚗 Segmentation Sémantique Embarquée")
|
| 13 |
+
st.markdown("Interface d'Inférence interagissant en direct avec l'API FastAPI construite lors de la Phase 5.")
|
| 14 |
+
|
| 15 |
+
# Cible du Microservice API
|
| 16 |
+
# --- Configuration du port local (Décommenter selon le Cloud choisi) ---
|
| 17 |
+
# Si tu testes pour GCP (Port 8000) :
|
| 18 |
+
# API_URL = os.getenv("API_URL", "http://localhost:8000")
|
| 19 |
+
# Si tu testes pour Hugging Face (Port 7860) :
|
| 20 |
+
API_URL = os.getenv("API_URL", "http://localhost:7860")
|
| 21 |
+
|
| 22 |
+
# --- 2. Fonctions Méthodologiques ---
|
| 23 |
+
@st.cache_data
|
| 24 |
+
def load_available_images():
|
| 25 |
+
"""
|
| 26 |
+
Scanne intelligemment le dossier de Test pour remonter toute la flotte d'images réelles (Phase 6.1).
|
| 27 |
+
Utilise le cache de Streamlit pour ne pas faire mouliner le disque dur à chaque clic !
|
| 28 |
+
"""
|
| 29 |
+
img_dir = "data/P8_Cityscapes_leftImg8bit_trainvaltest/leftImg8bit/test/"
|
| 30 |
+
if not os.path.exists(img_dir):
|
| 31 |
+
return []
|
| 32 |
+
|
| 33 |
+
image_paths = sorted(glob.glob(os.path.join(img_dir, "**/*_leftImg8bit.png"), recursive=True))
|
| 34 |
+
return image_paths
|
| 35 |
+
|
| 36 |
+
images_list = load_available_images()
|
| 37 |
+
|
| 38 |
+
if not images_list:
|
| 39 |
+
st.error("⚠️ **Stop !** Dossier des images introuvable. J'ai cherché dans `data/P8_Cityscapes_leftImg8bit_trainvaltest`. Êtes-vous sûr d'avoir lancé `streamlit run app/app.py` depuis la racine de `Projet_8` ?")
|
| 40 |
+
st.stop()
|
| 41 |
+
|
| 42 |
+
# --- 3. Barre de Menu (Sidebar) ---
|
| 43 |
+
st.sidebar.header("🕹 Tableau de Bord")
|
| 44 |
+
st.sidebar.markdown("Sélectionnez l'image issue des caméras embarquées pour la transmettre au réseau de neurones.")
|
| 45 |
+
|
| 46 |
+
# On allège visuellement la liste déroulante en n'affichant que le nom du fichier
|
| 47 |
+
images_names = [os.path.basename(p) for p in images_list]
|
| 48 |
+
selected_filename = st.sidebar.selectbox("Fichier à analyser :", images_names)
|
| 49 |
+
selected_path = images_list[images_names.index(selected_filename)]
|
| 50 |
+
|
| 51 |
+
# Algorithme pour reconstruire le chemin du "Ground Truth Color" (Masque Parfait) de Cityscapes
|
| 52 |
+
city_name = selected_filename.split('_')[0]
|
| 53 |
+
gt_color_filename = selected_filename.replace('_leftImg8bit.png', '_gtFine_color.png')
|
| 54 |
+
gt_color_path = os.path.join("data/P8_Cityscapes_gtFine_trainvaltest/gtFine/test/", city_name, gt_color_filename)
|
| 55 |
+
|
| 56 |
+
# --- 4. Le Client REST ---
|
| 57 |
+
if st.sidebar.button("🤖 Lancer l'Inférence de l'API"):
|
| 58 |
+
st.markdown("---")
|
| 59 |
+
|
| 60 |
+
with st.spinner("⏳ Transmission sécurisée à l'API FastAPI en cours..."):
|
| 61 |
+
try:
|
| 62 |
+
# Sérialisation : On capture l'image en données binaires pour le flux HTTP sortant
|
| 63 |
+
with open(selected_path, "rb") as image_file:
|
| 64 |
+
# payload 'multipart/form-data'
|
| 65 |
+
files = {"file": (selected_filename, image_file, "image/png")}
|
| 66 |
+
response = requests.post(f"{API_URL}/segmentation", files=files)
|
| 67 |
+
|
| 68 |
+
# Si le serveur nous envoie un Code HTTP 200 (Succès Total)
|
| 69 |
+
if response.status_code == 200:
|
| 70 |
+
# L'API nous renvoie désormais directement l'image au format PNG ! (StreamingResponse)
|
| 71 |
+
predicted_mask = Image.open(io.BytesIO(response.content))
|
| 72 |
+
|
| 73 |
+
st.success("✅ Triangulation serveur réussie ! Prédiction récupérée depuis l'API.")
|
| 74 |
+
|
| 75 |
+
# --- VISUALISATION DES 3 COMPOSANTES (Phase 6.1 Guideline) ---
|
| 76 |
+
col1, col2, col3 = st.columns(3)
|
| 77 |
+
|
| 78 |
+
# A. La Réalité Capturée
|
| 79 |
+
col1.subheader("📸 Caméra (leftImg8bit)")
|
| 80 |
+
col1.image(Image.open(selected_path), use_container_width=True)
|
| 81 |
+
|
| 82 |
+
# B. Le Masque Parfait fourni par Cityscapes
|
| 83 |
+
col2.subheader("🎯 Masque Vérité (Attendu)")
|
| 84 |
+
if os.path.exists(gt_color_path):
|
| 85 |
+
col2.image(Image.open(gt_color_path), use_container_width=True)
|
| 86 |
+
else:
|
| 87 |
+
col2.warning("Non fourni (Normal pour le Test-Set de compétition).")
|
| 88 |
+
|
| 89 |
+
# C. L'Intelligence Artificielle en Action
|
| 90 |
+
col3.subheader("🧠 Prédiction FastAPI (Notre IA)")
|
| 91 |
+
col3.image(predicted_mask, use_container_width=True)
|
| 92 |
+
|
| 93 |
+
# Affichage des classes (Codées en dur ici car l'API renvoie directement une image pour optimiser)
|
| 94 |
+
CLASSES = ['void', 'flat', 'construction', 'object', 'nature', 'sky', 'human', 'vehicle']
|
| 95 |
+
with st.expander("Consulter la palette de couleurs sémantiques 🎨"):
|
| 96 |
+
st.write(", ".join(CLASSES))
|
| 97 |
+
|
| 98 |
+
else:
|
| 99 |
+
st.error(f"❌ Erreur Serveur (HTTP {response.status_code}) : Vous devez vérifier ce que l'API imprime dans son propre terminal.")
|
| 100 |
+
|
| 101 |
+
except requests.exceptions.ConnectionError:
|
| 102 |
+
st.error(f"💥 ERREUR CRITIQUE : L'API est injoignable sur l'URL ciblée (`{API_URL}`).")
|
| 103 |
+
# st.info("👉 Si vous testez en local pour GCP, lancez le serveur FastAPI via : `cd api && uvicorn main:app --port 8000` et vérifiez l'API_URL.")
|
| 104 |
+
st.info("👉 Si vous testez en local pour Hugging Face, lancez le serveur FastAPI via : `cd api && uvicorn main:app --port 7860` et vérifiez l'API_URL.")
|
app/requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.50.0
|
| 2 |
+
requests==2.32.5
|
| 3 |
+
pillow==11.3.0
|
guide_projet_8.md
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚗 Guide Complet — Projet 8 : Déploiement d'un Modèle de Segmentation d'Images
|
| 2 |
+
|
| 3 |
+
> **Contexte** : Tu es ingénieur IA chez *Future Vision Transport*. Tu conçois le module de **segmentation d'images** dans une chaîne de vision embarquée pour véhicules autonomes. Le dataset est **Cityscapes**, le framework imposé est **Keras**, et tu dois livrer un modèle + API + application web déployés sur le Cloud.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Phase 0 — Mise en place de l'environnement
|
| 8 |
+
|
| 9 |
+
### 0.1 Créer un environnement virtuel Python dédié
|
| 10 |
+
```bash
|
| 11 |
+
python -m venv venv_projet8
|
| 12 |
+
source venv_projet8/bin/activate
|
| 13 |
+
pip install tensorflow keras numpy pandas matplotlib opencv-python albumentations flask fastapi uvicorn streamlit
|
| 14 |
+
```
|
| 15 |
+
- **Why** : Isoler les dépendances évite les conflits entre projets. C'est une pratique professionnelle standard.
|
| 16 |
+
- **Notions** : *virtual environments*, gestion de dépendances avec `pip`, `requirements.txt`.
|
| 17 |
+
|
| 18 |
+
### 0.2 Structurer le répertoire de travail
|
| 19 |
+
```
|
| 20 |
+
Projet_8/
|
| 21 |
+
├── data/ # Données Cityscapes (déjà présent)
|
| 22 |
+
├── notebooks/ # Notebooks d'exploration et d'entraînement
|
| 23 |
+
├── src/ # Code source réutilisable (data generator, model, utils)
|
| 24 |
+
├── api/ # Code de l'API Flask/FastAPI
|
| 25 |
+
├── app/ # Code de l'application web Streamlit/Flask
|
| 26 |
+
├── models/ # Modèles sauvegardés (.h5, .keras)
|
| 27 |
+
├── docs/ # Note technique + supports de présentation
|
| 28 |
+
└── requirements.txt
|
| 29 |
+
```
|
| 30 |
+
- **Why** : Une structure claire rend le projet "industrialisable" (critère évalué dans les livrables) et facilite la collaboration.
|
| 31 |
+
- **Notions** : *project scaffolding*, bonnes pratiques d'organisation de projets ML.
|
| 32 |
+
|
| 33 |
+
### 0.3 Initialiser un dépôt Git
|
| 34 |
+
```bash
|
| 35 |
+
git init
|
| 36 |
+
echo "venv_projet8/\ndata/\nmodels/*.h5\n__pycache__/" > .gitignore
|
| 37 |
+
```
|
| 38 |
+
- **Why** : Le versionnement est indispensable pour tracer l'évolution du code, revenir en arrière, et montrer ta démarche.
|
| 39 |
+
- **Notions** : *Git*, versionnement, `.gitignore`.
|
| 40 |
+
|
| 41 |
+
---
|
| 42 |
+
|
| 43 |
+
## Phase 1 — Comprendre les données Cityscapes
|
| 44 |
+
|
| 45 |
+
### 1.1 Télécharger le jeu de données complet
|
| 46 |
+
> ⚠️ **Observation** : Ton dossier [data/](file:///Users/j/Documents/OC_Inge_IA/Projet_8/data) contient actuellement uniquement les **annotations gtFine** (masks de segmentation), pas les images brutes. Tu dois télécharger les images `leftImg8bit` depuis [cityscapes-dataset.com](https://www.cityscapes-dataset.com/).
|
| 47 |
+
|
| 48 |
+
- **Why** : Ton modèle prend en entrée les **images caméra** (leftImg8bit) et doit prédire les **masks de segmentation** (gtFine). Sans les images, tu ne peux pas entraîner.
|
| 49 |
+
- **Notions** : *supervised learning*, notion d'images d'entrée vs. ground truth / labels.
|
| 50 |
+
|
| 51 |
+
### 1.2 Comprendre la structure des fichiers
|
| 52 |
+
Pour chaque image, Cityscapes fournit :
|
| 53 |
+
|
| 54 |
+
| Fichier | Description |
|
| 55 |
+
|---------|-------------|
|
| 56 |
+
| `*_leftImg8bit.png` | Image caméra brute (1024×2048) |
|
| 57 |
+
| `*_gtFine_labelIds.png` | Mask avec label ID par pixel (0-33) |
|
| 58 |
+
| `*_gtFine_color.png` | Mask coloré (visualisation) |
|
| 59 |
+
| `*_gtFine_instanceIds.png` | Mask d'instances |
|
| 60 |
+
| `*_gtFine_polygons.json` | Annotations polygonales |
|
| 61 |
+
|
| 62 |
+
- **Why** : Comprendre ce que chaque fichier représente est crucial avant de construire ton pipeline de données. Tu utiliseras principalement `leftImg8bit` + `labelIds`.
|
| 63 |
+
- **Notions** : *semantic segmentation*, *instance segmentation*, label maps, correspondance pixel-classe.
|
| 64 |
+
|
| 65 |
+
### 1.3 Comprendre les 8 catégories principales vs les 32 sous-catégories
|
| 66 |
+
|
| 67 |
+
Le projet demande de travailler avec les **8 catégories principales** :
|
| 68 |
+
|
| 69 |
+
| ID Cat. | Catégorie | Sous-catégories incluses |
|
| 70 |
+
|---------|-----------|-------------------------|
|
| 71 |
+
| 0 | **void** | unlabeled, ego vehicle, rectification border, out of roi, static, dynamic |
|
| 72 |
+
| 1 | **flat** | road, sidewalk, parking, rail track |
|
| 73 |
+
| 2 | **construction** | building, wall, fence, guard rail, bridge, tunnel |
|
| 74 |
+
| 3 | **object** | pole, polegroup, traffic light, traffic sign |
|
| 75 |
+
| 4 | **nature** | vegetation, terrain |
|
| 76 |
+
| 5 | **sky** | sky |
|
| 77 |
+
| 6 | **human** | person, rider |
|
| 78 |
+
| 7 | **vehicle** | car, truck, bus, caravan, trailer, train, motorcycle, bicycle |
|
| 79 |
+
|
| 80 |
+
- **Why** : C'est une contrainte explicite du projet (Franck). Tu dois mapper les 32 sous-catégories vers ces 8. Cela simplifie le problème et rend le modèle plus robuste.
|
| 81 |
+
- **Notions** : *label remapping*, *category grouping*, dictionnaire de correspondance Cityscapes.
|
| 82 |
+
|
| 83 |
+
### 1.4 Explorer visuellement les données (EDA)
|
| 84 |
+
Dans un notebook, afficher côte à côte :
|
| 85 |
+
- L'image brute
|
| 86 |
+
- Le mask coloré (`gtFine_color`)
|
| 87 |
+
- Le mask labelIds remappé vers les 8 catégories
|
| 88 |
+
- Distribution des classes par image (histogrammes)
|
| 89 |
+
|
| 90 |
+
- **Why** : L'EDA permet de détecter le *class imbalance* (par exemple, "road" et "building" dominent, "rider" est rare). Cela guidera tes choix d'augmentation de données et de loss function.
|
| 91 |
+
- **Notions** : *Exploratory Data Analysis*, *class imbalance*, visualisation de masks de segmentation, `matplotlib.imshow()`.
|
| 92 |
+
|
| 93 |
+
---
|
| 94 |
+
|
| 95 |
+
## Phase 2 — État de l'Art et Recherche Bibliographique
|
| 96 |
+
|
| 97 |
+
### 2.1 Étudier les architectures de segmentation sémantique
|
| 98 |
+
|
| 99 |
+
Architectures incontournables à comprendre et comparer :
|
| 100 |
+
|
| 101 |
+
| Architecture | Principe clé | Avantage |
|
| 102 |
+
|-------------|--------------|----------|
|
| 103 |
+
| **FCN** (Fully Convolutional Network) | Remplace les couches FC par des convolutions | Premier modèle end-to-end pour la segmentation |
|
| 104 |
+
| **U-Net** | Encoder-decoder avec skip connections | Excellent pour les détails fins, idéal pour les données limitées |
|
| 105 |
+
| **SegNet** | Encoder-decoder avec pooling indices | Plus léger en mémoire que U-Net |
|
| 106 |
+
| **DeepLab v3+** | Atrous convolutions + ASPP + encoder-decoder | State-of-the-art, capture multi-échelle |
|
| 107 |
+
| **PSPNet** | Pyramid Pooling Module | Capture le contexte global |
|
| 108 |
+
|
| 109 |
+
- **Why** : La note technique demande une "présentation des différentes approches et une synthèse de l'état de l'art". Tu dois comprendre les forces/faiblesses de chaque architecture pour justifier ton choix.
|
| 110 |
+
- **Notions** : *encoder-decoder architecture*, *skip connections*, *atrous/dilated convolutions*, *feature pyramid*, *receptive field*.
|
| 111 |
+
|
| 112 |
+
### 2.2 Étudier les métriques de segmentation
|
| 113 |
+
|
| 114 |
+
| Métrique | Formule/Description |
|
| 115 |
+
|----------|-------------------|
|
| 116 |
+
| **Pixel Accuracy** | % de pixels correctement classifiés |
|
| 117 |
+
| **Mean IoU (mIoU)** | Intersection over Union moyenné sur toutes les classes |
|
| 118 |
+
| **Dice Coefficient** | 2×TP / (2×TP + FP + FN) |
|
| 119 |
+
| **Per-class IoU** | IoU calculé pour chaque classe individuellement |
|
| 120 |
+
|
| 121 |
+
- **Why** : Le pixel accuracy est trompeur sur des données déséquilibrées. Le mIoU est la métrique standard en segmentation sémantique et sera celle que l'évaluateur attend.
|
| 122 |
+
- **Notions** : *IoU (Intersection over Union)*, *Dice score*, *confusion matrix* pixel-level, *mean IoU*.
|
| 123 |
+
|
| 124 |
+
### 2.3 Étudier les loss functions adaptées
|
| 125 |
+
|
| 126 |
+
- **Categorical Cross-Entropy** : Standard, mais sensible au class imbalance
|
| 127 |
+
- **Weighted Cross-Entropy** : Pondération inversement proportionnelle à la fréquence de la classe
|
| 128 |
+
- **Dice Loss** : Optimise directement le Dice Coefficient
|
| 129 |
+
- **Focal Loss** : Réduit la contribution des exemples bien classifiés
|
| 130 |
+
- **Combinaison** : Dice Loss + Cross-Entropy souvent utilisée en pratique
|
| 131 |
+
|
| 132 |
+
- **Why** : Le choix de la loss function a un impact direct sur les performances, surtout avec des classes déséquilibrées (road vs rider).
|
| 133 |
+
- **Notions** : *loss functions*, *class weighting*, *focal loss*, *dice loss*.
|
| 134 |
+
|
| 135 |
+
---
|
| 136 |
+
|
| 137 |
+
## Phase 3 — Pipeline de Données
|
| 138 |
+
|
| 139 |
+
### 3.1 Créer un Data Generator Keras
|
| 140 |
+
|
| 141 |
+
```python
|
| 142 |
+
class CityscapesGenerator(tf.keras.utils.Sequence):
|
| 143 |
+
def __init__(self, image_paths, mask_paths, batch_size, img_size, augment=False):
|
| 144 |
+
...
|
| 145 |
+
def __getitem__(self, idx):
|
| 146 |
+
# Charge batch d'images + masks
|
| 147 |
+
# Remapping 32 → 8 catégories
|
| 148 |
+
# Resize
|
| 149 |
+
# Augmentation (si train)
|
| 150 |
+
# One-hot encoding du mask
|
| 151 |
+
...
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
- **Why** : Les images Cityscapes (1024×2048) sont trop volumineuses pour tout charger en RAM. Un generator charge les données par batch à la volée. C'est un critère du livrable ("caractère industrialisable").
|
| 155 |
+
- **Notions** : `tf.keras.utils.Sequence`, *batch loading*, *lazy loading*, *one-hot encoding* de masks, *yield* vs *return*.
|
| 156 |
+
|
| 157 |
+
### 3.2 Implémenter le label remapping (32 → 8 catégories)
|
| 158 |
+
|
| 159 |
+
```python
|
| 160 |
+
# Dictionnaire de mapping utilisant la documentation Cityscapes
|
| 161 |
+
LABEL_TO_CATEGORY = {
|
| 162 |
+
0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, # void
|
| 163 |
+
7: 1, 8: 1, 9: 1, 10: 1, # flat
|
| 164 |
+
11: 2, 12: 2, 13: 2, 14: 2, 15: 2, 16: 2, # construction
|
| 165 |
+
17: 3, 18: 3, 19: 3, 20: 3, # object
|
| 166 |
+
21: 4, 22: 4, # nature
|
| 167 |
+
23: 5, # sky
|
| 168 |
+
24: 6, 25: 6, # human
|
| 169 |
+
26: 7, 27: 7, 28: 7, 29: 7, 30: 7, 31: 7, 32: 7, 33: 7 # vehicle
|
| 170 |
+
}
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
- **Why** : Franck (le collègue en charge du traitement d'images) a spécifié que seules les 8 catégories principales sont nécessaires. Ce remapping doit être fait dans le pipeline de données, pas manuellement.
|
| 174 |
+
- **Notions** : *label encoding*, *numpy vectorized mapping* (`np.vectorize` ou table de lookup), correspondance Cityscapes.
|
| 175 |
+
|
| 176 |
+
### 3.3 Implémenter l'augmentation de données
|
| 177 |
+
|
| 178 |
+
Techniques à appliquer :
|
| 179 |
+
- **Flip horizontal** (le plus commun pour la conduite)
|
| 180 |
+
- **Rotation légère** (±10°)
|
| 181 |
+
- **Variations de luminosité/contraste**
|
| 182 |
+
- **Random crop + resize**
|
| 183 |
+
- **Ajout de bruit gaussien**
|
| 184 |
+
|
| 185 |
+
> ⚠️ L'augmentation doit être appliquée **identiquement** à l'image ET au mask !
|
| 186 |
+
|
| 187 |
+
- **Why** : Le livrable demande explicitement des "gains obtenus avec les approches d'augmentation des données". L'augmentation permet de régulariser le modèle et d'augmenter virtuellement la taille du dataset.
|
| 188 |
+
- **Notions** : *data augmentation*, bibliothèque `albumentations` (applique les mêmes transformations géométriques à l'image et au mask), *overfitting prevention*.
|
| 189 |
+
|
| 190 |
+
### 3.4 Gérer le redimensionnement des images
|
| 191 |
+
|
| 192 |
+
Réduire les images de 1024×2048 à une taille gérable :
|
| 193 |
+
- **256×512** : bon compromis vitesse/qualité pour les premiers tests
|
| 194 |
+
- **512×1024** : meilleure résolution si ressources GPU suffisantes
|
| 195 |
+
|
| 196 |
+
- **Why** : Les images originales sont très grandes. Entraîner en pleine résolution nécessite un GPU puissant (32+ Go VRAM). Le redimensionnement est un compromis nécessaire.
|
| 197 |
+
- **Notions** : *image resizing*, interpolation bilinéaire vs nearest-neighbor (pour les masks !), *aspect ratio preservation*.
|
| 198 |
+
|
| 199 |
+
---
|
| 200 |
+
|
| 201 |
+
## Phase 4 — Entraînement du Modèle
|
| 202 |
+
|
| 203 |
+
### 4.1 Commencer avec un modèle simple (baseline)
|
| 204 |
+
|
| 205 |
+
Entraîner un U-Net simple depuis zéro comme baseline :
|
| 206 |
+
- Encoder : blocs Conv2D + BatchNorm + ReLU + MaxPool
|
| 207 |
+
- Decoder : UpSampling2D + Concatenate (skip connections) + Conv2D
|
| 208 |
+
- Sortie : Conv2D(8, (1,1), activation='softmax')
|
| 209 |
+
|
| 210 |
+
- **Why** : Un modèle simple permet de valider que tout le pipeline fonctionne (data loading, training loop, évaluation) avant d'investir du temps dans des architectures plus complexes.
|
| 211 |
+
- **Notions** : *U-Net architecture*, *encoder-decoder*, *skip connections*, *softmax pour classification multi-classe pixel-level*.
|
| 212 |
+
|
| 213 |
+
### 4.2 Utiliser le Transfer Learning
|
| 214 |
+
|
| 215 |
+
Utiliser un backbone pré-entraîné sur ImageNet :
|
| 216 |
+
- **MobileNetV2** : léger, idéal pour l'embarqué
|
| 217 |
+
- **ResNet50** : bon compromis performance/poids
|
| 218 |
+
- **EfficientNet** : état de l'art en efficacité
|
| 219 |
+
|
| 220 |
+
```python
|
| 221 |
+
base_model = tf.keras.applications.MobileNetV2(input_shape=(256, 512, 3), include_top=False, weights='imagenet')
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
- **Why** : Le transfer learning permet d'obtenir de bien meilleures performances avec moins de données et moins de temps d'entraînement. Les features bas-niveau (contours, textures) apprises sur ImageNet sont transférables.
|
| 225 |
+
- **Notions** : *transfer learning*, *feature extraction vs fine-tuning*, *backbone freezing/unfreezing*, `keras.applications`.
|
| 226 |
+
|
| 227 |
+
### 4.3 Implémenter un modèle avancé (DeepLab v3+ ou similaire)
|
| 228 |
+
|
| 229 |
+
Options :
|
| 230 |
+
- Utiliser `segmentation_models` library : `pip install segmentation-models`
|
| 231 |
+
- Implémenter DeepLab v3+ manuellement avec Keras
|
| 232 |
+
- Utiliser TensorFlow Hub pour charger un modèle pré-entraîné
|
| 233 |
+
|
| 234 |
+
- **Why** : La comparaison entre un modèle simple (baseline) et un modèle avancé est attendue dans la note technique. DeepLab v3+ est souvent le meilleur choix pour Cityscapes.
|
| 235 |
+
- **Notions** : *ASPP (Atrous Spatial Pyramid Pooling)*, *dilated convolutions*, *multi-scale feature extraction*.
|
| 236 |
+
|
| 237 |
+
### 4.4 Configurer l'entraînement
|
| 238 |
+
|
| 239 |
+
```python
|
| 240 |
+
model.compile(
|
| 241 |
+
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
|
| 242 |
+
loss='categorical_crossentropy', # ou dice_loss + CE combinée
|
| 243 |
+
metrics=['accuracy', MeanIoU(num_classes=8)]
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
callbacks = [
|
| 247 |
+
tf.keras.callbacks.ModelCheckpoint('best_model.keras', save_best_only=True, monitor='val_mean_iou', mode='max'),
|
| 248 |
+
tf.keras.callbacks.EarlyStopping(patience=10, monitor='val_mean_iou', mode='max'),
|
| 249 |
+
tf.keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=5),
|
| 250 |
+
tf.keras.callbacks.TensorBoard(log_dir='./logs')
|
| 251 |
+
]
|
| 252 |
+
```
|
| 253 |
+
|
| 254 |
+
- **Why** : Les callbacks automatisent la sauvegarde du meilleur modèle, l'arrêt précoce (évite l'overfitting), et l'ajustement du learning rate. C'est une démarche professionnelle.
|
| 255 |
+
- **Notions** : *callbacks Keras*, *early stopping*, *learning rate scheduling*, *model checkpointing*, *TensorBoard*.
|
| 256 |
+
|
| 257 |
+
### 4.5 Entraîner et comparer les modèles
|
| 258 |
+
|
| 259 |
+
Pour chaque modèle, documenter :
|
| 260 |
+
- Architecture utilisée
|
| 261 |
+
- Hyperparamètres (lr, batch size, epochs, augmentation)
|
| 262 |
+
- Courbes train/val loss et mIoU
|
| 263 |
+
- Per-class IoU sur le validation set
|
| 264 |
+
- Prédictions visuelles (image / ground truth / prédiction)
|
| 265 |
+
|
| 266 |
+
- **Why** : La soutenance demande la "comparaison des modèles" (10 min dédiées). Tu dois montrer une démarche itérative et justifier le modèle final retenu.
|
| 267 |
+
- **Notions** : *hyperparameter tuning*, *learning curves analysis*, *overfitting vs underfitting diagnosis*, *model selection*.
|
| 268 |
+
|
| 269 |
+
### 4.6 Évaluer sur le jeu de test
|
| 270 |
+
|
| 271 |
+
Calculer les métriques finales sur le test set (Berlin, Bielefeld, Bonn, Leverkusen, Mainz, Munich) :
|
| 272 |
+
- mIoU global
|
| 273 |
+
- IoU par classe
|
| 274 |
+
- Matrice de confusion pixel-level
|
| 275 |
+
- Visualisations qualitatives
|
| 276 |
+
|
| 277 |
+
- **Why** : L'évaluation sur le test set (jamais vu pendant l'entraînement) donne une estimation non biaisée des performances. C'est la norme en ML.
|
| 278 |
+
- **Notions** : *train/val/test split*, *generalization*, *qualitative vs quantitative evaluation*.
|
| 279 |
+
|
| 280 |
+
---
|
| 281 |
+
|
| 282 |
+
## Phase 5 — Développement de l'API de Prédiction
|
| 283 |
+
|
| 284 |
+
### 5.1 Choisir entre Flask et FastAPI
|
| 285 |
+
|
| 286 |
+
| Critère | Flask | FastAPI |
|
| 287 |
+
|---------|-------|---------|
|
| 288 |
+
| Facilité | ✅ Plus simple | ⚠️ Async concepts |
|
| 289 |
+
| Performance | ⚠️ Synchrone | ✅ Asynchrone natif |
|
| 290 |
+
| Documentation auto | ❌ | ✅ Swagger/OpenAPI inclus |
|
| 291 |
+
| Typing | ❌ | ✅ Pydantic intégré |
|
| 292 |
+
|
| 293 |
+
> **Recommandation** : FastAPI — la doc auto est un vrai plus pour Laura et la soutenance.
|
| 294 |
+
|
| 295 |
+
- **Why** : Laura veut une "API simple à utiliser". FastAPI génère automatiquement une documentation interactive (Swagger UI) qui permet de tester l'API sans code client.
|
| 296 |
+
- **Notions** : *REST API*, *endpoints*, *HTTP methods* (POST), *content types* (multipart/form-data, JSON).
|
| 297 |
+
|
| 298 |
+
### 5.2 Concevoir l'endpoint de prédiction
|
| 299 |
+
|
| 300 |
+
```python
|
| 301 |
+
from fastapi import FastAPI, File, UploadFile
|
| 302 |
+
import numpy as np
|
| 303 |
+
from PIL import Image
|
| 304 |
+
import io
|
| 305 |
+
|
| 306 |
+
app = FastAPI(title="Cityscapes Segmentation API")
|
| 307 |
+
|
| 308 |
+
# Charger le modèle au démarrage
|
| 309 |
+
model = tf.keras.models.load_model('best_model.keras')
|
| 310 |
+
|
| 311 |
+
@app.post("/predict")
|
| 312 |
+
async def predict(file: UploadFile = File(...)):
|
| 313 |
+
# 1. Lire l'image uploadée
|
| 314 |
+
image = Image.open(io.BytesIO(await file.read()))
|
| 315 |
+
# 2. Prétraiter (resize, normalisation)
|
| 316 |
+
# 3. Prédire le mask
|
| 317 |
+
# 4. Post-traiter (argmax, resize au format original)
|
| 318 |
+
# 5. Retourner le mask (encodé en base64 ou en JSON)
|
| 319 |
+
return {"mask": mask_as_list, "classes": class_names}
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
- **Why** : Cet endpoint est le cœur du livrable 2. Il prend une image et retourne le mask prédit. Il sera consommé par l'application web (livrable 3).
|
| 323 |
+
- **Notions** : *API endpoint design*, *image I/O* (bytes → PIL → numpy), *model inference pipeline*, *base64 encoding*.
|
| 324 |
+
|
| 325 |
+
### 5.3 Ajouter des endpoints utilitaires
|
| 326 |
+
|
| 327 |
+
- `GET /` : Infos sur l'API (version, modèle utilisé, classes)
|
| 328 |
+
- `GET /health` : Health check
|
| 329 |
+
- `GET /classes` : Liste des 8 catégories et leurs couleurs
|
| 330 |
+
|
| 331 |
+
- **Why** : Des endpoints d'information facilitent l'intégration et le monitoring. C'est une bonne pratique d'architecture API.
|
| 332 |
+
- **Notions** : *API design patterns*, *health checks*, *API versioning*.
|
| 333 |
+
|
| 334 |
+
### 5.4 Tester l'API en local
|
| 335 |
+
|
| 336 |
+
```bash
|
| 337 |
+
uvicorn api.main:app --reload --host 0.0.0.0 --port 8000
|
| 338 |
+
# Tester sur http://localhost:8000/docs (Swagger UI)
|
| 339 |
+
```
|
| 340 |
+
|
| 341 |
+
- **Why** : Toujours tester en local avant de déployer. La Swagger UI permet de tester visuellement l'upload d'image et la réponse.
|
| 342 |
+
- **Notions** : *local development server*, *API testing*, *Swagger/OpenAPI*.
|
| 343 |
+
|
| 344 |
+
---
|
| 345 |
+
|
| 346 |
+
## Phase 6 — Développement de l'Application Web
|
| 347 |
+
|
| 348 |
+
### 6.1 Concevoir l'application Streamlit (ou Flask)
|
| 349 |
+
|
| 350 |
+
L'application doit permettre :
|
| 351 |
+
1. **Afficher la liste des IDs** des images disponibles (test set)
|
| 352 |
+
2. **Sélectionner un ID** → lancer la prédiction via l'API
|
| 353 |
+
3. **Afficher 3 images côte à côte** : image réelle | mask réel | mask prédit
|
| 354 |
+
|
| 355 |
+
```python
|
| 356 |
+
import streamlit as st
|
| 357 |
+
import requests
|
| 358 |
+
|
| 359 |
+
st.title("🚗 Segmentation d'Images - Future Vision Transport")
|
| 360 |
+
|
| 361 |
+
# Liste des images disponibles
|
| 362 |
+
image_ids = get_available_image_ids()
|
| 363 |
+
selected_id = st.selectbox("Sélectionner une image :", image_ids)
|
| 364 |
+
|
| 365 |
+
if st.button("Lancer la prédiction"):
|
| 366 |
+
# Appel à l'API
|
| 367 |
+
response = requests.post(API_URL + "/predict", files={"file": image_bytes})
|
| 368 |
+
predicted_mask = decode_response(response.json())
|
| 369 |
+
|
| 370 |
+
col1, col2, col3 = st.columns(3)
|
| 371 |
+
col1.image(original_image, caption="Image réelle")
|
| 372 |
+
col2.image(ground_truth_mask, caption="Mask réel")
|
| 373 |
+
col3.image(predicted_mask, caption="Mask prédit")
|
| 374 |
+
```
|
| 375 |
+
|
| 376 |
+
- **Why** : Cette application est l'interface de démonstration (livrable 3). Elle doit consommer l'API, pas faire la prédiction directement — cela prouve que l'architecture est bien découplée.
|
| 377 |
+
- **Notions** : *Streamlit*, *API consumption* (`requests`), *image display*, *UI/UX pour une démo*, *client-server architecture*.
|
| 378 |
+
|
| 379 |
+
### 6.2 Ajouter des fonctionnalités bonus
|
| 380 |
+
|
| 381 |
+
- Palette de couleurs pour les classes (légende)
|
| 382 |
+
- Overlay du mask sur l'image originale (transparence)
|
| 383 |
+
- Métriques affichées (IoU par classe vs ground truth)
|
| 384 |
+
- Upload d'une image personnelle (pas du dataset)
|
| 385 |
+
|
| 386 |
+
- **Why** : Ces fonctionnalités impressionnent l'évaluateur et montrent une maîtrise au-delà du minimum requis.
|
| 387 |
+
- **Notions** : *image compositing*, *alpha blending*, *color mapping*.
|
| 388 |
+
|
| 389 |
+
---
|
| 390 |
+
|
| 391 |
+
## Phase 7 — Déploiement sur le Cloud
|
| 392 |
+
|
| 393 |
+
### 7.1 Choisir une plateforme Cloud
|
| 394 |
+
|
| 395 |
+
| Plateforme | Coût | Difficulté | GPU |
|
| 396 |
+
|-----------|------|-----------|-----|
|
| 397 |
+
| **Render** | Gratuit (limité) | ⭐⭐ | ❌ |
|
| 398 |
+
| **Railway** | Gratuit ($5 crédit) | ⭐⭐ | ❌ |
|
| 399 |
+
| **Azure Web App** | Free tier disponible | ⭐⭐⭐ | Option payante |
|
| 400 |
+
| **Google Cloud Run** | Free tier généreux | ⭐⭐⭐ | ❌ |
|
| 401 |
+
| **PythonAnywhere** | Gratuit (très limité) | ⭐ | ❌ |
|
| 402 |
+
| **AWS (EC2/Lambda)** | Free tier 12 mois | ⭐⭐⭐⭐ | Option payante |
|
| 403 |
+
|
| 404 |
+
- **Why** : Le déploiement Cloud est obligatoire pour la soutenance. Tu dois faire une démo en live et l'enregistrer. Choisis en fonction de ton budget et de ta familiarité.
|
| 405 |
+
- **Notions** : *cloud deployment*, *PaaS vs IaaS*, *containerization concepts*.
|
| 406 |
+
|
| 407 |
+
### 7.2 Containeriser avec Docker
|
| 408 |
+
|
| 409 |
+
```dockerfile
|
| 410 |
+
# API
|
| 411 |
+
FROM python:3.10-slim
|
| 412 |
+
WORKDIR /app
|
| 413 |
+
COPY requirements.txt .
|
| 414 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 415 |
+
COPY . .
|
| 416 |
+
EXPOSE 8000
|
| 417 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
| 418 |
+
```
|
| 419 |
+
|
| 420 |
+
- **Why** : Docker garantit que l'environnement est identique en local et en production. La plupart des plateformes Cloud supportent Docker nativement.
|
| 421 |
+
- **Notions** : *Docker*, *Dockerfile*, *containers vs VMs*, *image build*, *port mapping*.
|
| 422 |
+
|
| 423 |
+
### 7.3 Déployer l'API et l'application
|
| 424 |
+
|
| 425 |
+
Deux déploiements séparés :
|
| 426 |
+
1. **API** : déploiement du service de prédiction
|
| 427 |
+
2. **App web** : déploiement de l'interface Streamlit (pointe vers l'URL de l'API)
|
| 428 |
+
|
| 429 |
+
- **Why** : Séparer l'API de l'application web est une architecture microservices. Cela prouve la modularité de ton système.
|
| 430 |
+
- **Notions** : *microservices architecture*, *environment variables* pour l'URL de l'API, *CORS configuration*.
|
| 431 |
+
|
| 432 |
+
### 7.4 Enregistrer la démo pendant la soutenance
|
| 433 |
+
|
| 434 |
+
> ⚠️ **Important** : Le sujet précise que l'évaluateur et l'étudiant doivent **enregistrer la démo** pendant la soutenance pour que le jury puisse la visionner. Tu pourras ensuite couper le service Cloud pour éviter les coûts.
|
| 435 |
+
|
| 436 |
+
- **Why** : C'est une exigence explicite du sujet. Prépare un outil d'enregistrement d'écran (OBS, QuickTime, etc.).
|
| 437 |
+
- **Notions** : *screen recording*, préparation de démo.
|
| 438 |
+
|
| 439 |
+
---
|
| 440 |
+
|
| 441 |
+
## Phase 8 — Rédaction de la Note Technique (~10 pages)
|
| 442 |
+
|
| 443 |
+
### 8.1 Structure recommandée
|
| 444 |
+
|
| 445 |
+
| Section | Contenu | ~Pages |
|
| 446 |
+
|---------|---------|--------|
|
| 447 |
+
| **1. Introduction** | Contexte (Future Vision Transport), objectifs, contraintes | 1 |
|
| 448 |
+
| **2. État de l'Art** | Revue des architectures (FCN, U-Net, SegNet, DeepLab), métriques | 2 |
|
| 449 |
+
| **3. Dataset et Préparation** | Cityscapes, 8 catégories, EDA, augmentation | 1.5 |
|
| 450 |
+
| **4. Modèle Retenu** | Architecture détaillée, hyperparamètres, justification du choix | 2 |
|
| 451 |
+
| **5. Résultats** | Comparaison des modèles, mIoU, courbes, gains de l'augmentation | 2 |
|
| 452 |
+
| **6. Mise en Production** | Architecture API + App, déploiement Cloud | 1 |
|
| 453 |
+
| **7. Conclusion** | Bilan, limites, pistes d'amélioration | 0.5 |
|
| 454 |
+
|
| 455 |
+
- **Why** : La note technique est un livrable clé (livrable 4). Elle doit démontrer ta rigueur scientifique et ta capacité à synthétiser.
|
| 456 |
+
- **Notions** : *technical writing*, *scientific methodology*, *results presentation*.
|
| 457 |
+
|
| 458 |
+
### 8.2 Points clés à inclure
|
| 459 |
+
|
| 460 |
+
- **Comparaison explicite** des modèles (tableau mIoU)
|
| 461 |
+
- **Gains de l'augmentation** : comparer modèle sans augmentation vs avec
|
| 462 |
+
- **Visualisations** : courbes de loss, exemples de prédictions, matrice de confusion
|
| 463 |
+
- **Pistes d'amélioration** : Real-time inference, quantization, pruning, TensorRT
|
| 464 |
+
|
| 465 |
+
- **Why** : Ces éléments sont explicitement demandés dans le descriptif des livrables.
|
| 466 |
+
- **Notions** : *ablation study*, *model comparison*, *scientific rigor*.
|
| 467 |
+
|
| 468 |
+
---
|
| 469 |
+
|
| 470 |
+
## Phase 9 — Support de Présentation (max 30 slides)
|
| 471 |
+
|
| 472 |
+
### 9.1 Structure calquée sur le timing de soutenance
|
| 473 |
+
|
| 474 |
+
| Bloc | Durée | Slides | Contenu |
|
| 475 |
+
|------|-------|--------|---------|
|
| 476 |
+
| **Contexte & Objectifs** | 5 min | 5-7 | Entreprise, chaîne de vision, ton rôle, métriques |
|
| 477 |
+
| **Modèles & Résultats** | 10 min | 12-15 | Architectures, comparaisons, visualisations, augmentation |
|
| 478 |
+
| **Mise en Production** | 5 min | 5-8 | Architecture API/App, déploiement, démo live |
|
| 479 |
+
|
| 480 |
+
- **Why** : La soutenance est strictement chronométrée (20 min ±5). Une présentation trop courte (<15 min) ou trop longue (>25 min) peut être refusée.
|
| 481 |
+
- **Notions** : *presentation skills*, *storytelling technique*, *demo preparation*.
|
| 482 |
+
|
| 483 |
+
### 9.2 Préparer la démo live
|
| 484 |
+
|
| 485 |
+
Préparer un script de démo :
|
| 486 |
+
1. Ouvrir l'application web déployée
|
| 487 |
+
2. Sélectionner une image du test set
|
| 488 |
+
3. Lancer la prédiction
|
| 489 |
+
4. Montrer image / mask réel / mask prédit côte à côte
|
| 490 |
+
5. Éventuellement tester avec une image uploadée
|
| 491 |
+
|
| 492 |
+
> Tester la démo **plusieurs fois avant** la soutenance !
|
| 493 |
+
|
| 494 |
+
- **Why** : La démo est le moment fort de la soutenance. Un bug en live est très pénalisant. Prépare aussi des screenshots de backup au cas où le Cloud tombe.
|
| 495 |
+
- **Notions** : *demo rehearsal*, *fallback plan*.
|
| 496 |
+
|
| 497 |
+
---
|
| 498 |
+
|
| 499 |
+
## ✅ Checklist des Livrables
|
| 500 |
+
|
| 501 |
+
| # | Livrable | Format | Nommage |
|
| 502 |
+
|---|----------|--------|---------|
|
| 503 |
+
| 1 | **Scripts/Notebooks** (pipeline complet) | `.ipynb` / `.py` | `Nom_Prénom_1_scripts_mmaaaa` |
|
| 504 |
+
| 2 | **API de prédiction** (déployée sur le Cloud) | Flask/FastAPI | `Nom_Prénom_2_API_mmaaaa` |
|
| 505 |
+
| 3 | **Application web** (déployée sur le Cloud) | Streamlit/Flask | `Nom_Prénom_3_application_Flask_mmaaaa` |
|
| 506 |
+
| 4 | **Note technique** (~10 pages) | PDF | `Nom_Prénom_4_note_technique_mmaaaa` |
|
| 507 |
+
| 5 | **Présentation** (max 30 slides) | PPTX/PDF | `Nom_Prénom_5_presentation_mmaaaa` |
|
| 508 |
+
|
| 509 |
+
> ⚠️ Tout doit être dans un **zip** nommé `Titre_du_projet_nom_prénom`.
|
| 510 |
+
|
| 511 |
+
---
|
| 512 |
+
|
| 513 |
+
## 🎯 Notions Clés à Maîtriser (Résumé)
|
| 514 |
+
|
| 515 |
+
| Domaine | Notions |
|
| 516 |
+
|---------|---------|
|
| 517 |
+
| **Computer Vision** | Semantic segmentation, encoder-decoder, skip connections, dilated convolutions |
|
| 518 |
+
| **Deep Learning** | Transfer learning, data augmentation, loss functions (Dice, Focal, CE), callbacks |
|
| 519 |
+
| **Keras/TensorFlow** | `tf.keras.utils.Sequence`, `keras.applications`, custom training loops, `MeanIoU` |
|
| 520 |
+
| **MLOps** | Docker, API REST, déploiement Cloud, CI/CD basique |
|
| 521 |
+
| **Data Engineering** | Data generators, label remapping, preprocessing pipelines |
|
| 522 |
+
| **Évaluation** | mIoU, Dice, pixel accuracy, confusion matrix, per-class metrics |
|
| 523 |
+
| **Communication** | Note technique, présentation de résultats, démo live |
|
mlflow.db
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5880996b1fff46c501244aad7a321f7fbaa951c11ea0a62eba1561b8e5d77cfc
|
| 3 |
+
size 626688
|
models/best_unet_model.keras
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ec48de7f43bdb66bc5106f78b7c1de12561593e4d56ae3487c49956d622d17cf
|
| 3 |
+
size 23481417
|
mon_avancement.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Avancement au 20/03/2026
|
| 2 |
+
|
| 3 |
+
## Objectif actuel
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
## Avancement
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
## Prochaines étapes
|
| 10 |
+
|
| 11 |
+
Remapper les 32 sous catégories de Cityscapes en 8 catégories
|
| 12 |
+
|
| 13 |
+
Faire une EDA:
|
| 14 |
+
Dans un notebook, afficher côte à côte :
|
| 15 |
+
- L'image brute
|
| 16 |
+
- Le mask coloré (gtFine_color)
|
| 17 |
+
- Le mask labelIds remappé vers les 8 catégories
|
| 18 |
+
- Distribution des classes par image (histogrammes)
|
| 19 |
+
|
| 20 |
+
Faire tourner le google colab https://colab.research.google.com/drive/1jZ2tdEyJ2xaERUCwyQ5juwPJrEyIAtBN?usp=sharing#scrollTo=O9oKKrUPQD9s
|
| 21 |
+
L'objectif est juste d'obtenir un modèle où la segmentation est correcte.
|
| 22 |
+
Mettre le modèle dans l'api et le déployer.
|
| 23 |
+
|
| 24 |
+
Faire des tests unitaires dans le projet 7
|
| 25 |
+
Mettre de l'argent pour upgrade le projet 7 et faire tourner l'app, ou essayer dans render
|
| 26 |
+
|
| 27 |
+
Entrainement du modèle
|
| 28 |
+
Affichage des résultats
|
| 29 |
+
Il faut que j'ai la segmentation et que mon modèle puisse renvoyer les images.
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
Parler de l'augmentation des données. Quand j'ai mes images de voiture, je les pivote, etc pour augementer mes images pour l'entrainement.
|
| 33 |
+
Parler du modèle. Expliquer ce qu'est le U-net. Qu'estce que qui se passe et comment ça se passe.
|
| 34 |
+
Comment j'ai pu faire avec le transfer-learning
|
| 35 |
+
Parler des différents modèles et comparer leurs résultats.
|
| 36 |
+
Insister sur le github. Comment j'ai fait mon github, comment je le link avec render. Comment je le link avec streamlit et vérifier les résultats visuellement.
|
| 37 |
+
Parler du CI/CD
|
| 38 |
+
Parler de la FASTAPPi
|
notebooks/notebook_entrainement.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
notebooks/notebook_exploratoire.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
requirements.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
python-multipart
|
| 4 |
+
tensorflow
|
| 5 |
+
pandas
|
| 6 |
+
numpy
|
| 7 |
+
pillow
|
| 8 |
+
streamlit
|
| 9 |
+
albumentations
|
| 10 |
+
matplotlib
|
| 11 |
+
scipy
|
| 12 |
+
azure-monitor-opentelemetry
|
resume_execution_notebook.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Synthèse de l'Exécution du Notebook (MobileNetV2-UNet)
|
| 2 |
+
|
| 3 |
+
Ce document résume les actions effectuées par Gemini CLI pour adapter et exécuter localement le notebook Colab trouvé sur internet (`Copie_de_p08_david_scanu_notebook_MobileNetV2_UNet.ipynb`), ainsi que les résultats produits par cette exécution.
|
| 4 |
+
|
| 5 |
+
## 1. Contexte et Objectif
|
| 6 |
+
|
| 7 |
+
Le but était de prendre un notebook Google Colab conçu pour l'entraînement d'un modèle de segmentation sémantique (architecture MobileNetV2-UNet sur le jeu de données Cityscapes) et de l'exécuter de bout en bout sur l'environnement local.
|
| 8 |
+
|
| 9 |
+
## 2. Fichiers de configuration et d'adaptation créés
|
| 10 |
+
|
| 11 |
+
Pour rendre le notebook exécutable localement (hors de l'environnement Google Colab) et de manière automatisée, Gemini CLI a généré plusieurs scripts Python "patch" pour modifier le code du notebook à la volée :
|
| 12 |
+
|
| 13 |
+
* **`fix_colab.py`** : A commenté toutes les lignes spécifiques à Google Colab (comme `import google.colab` ou `drive.mount`), qui auraient provoqué des erreurs en local.
|
| 14 |
+
* **`patch_notebook.py`** : A remplacé le long processus de téléchargement du véritable jeu de données Cityscapes par une fonction générant un **jeu de données fictif (mock)**. Cela a permis de tester l'intégralité du pipeline (de l'entraînement à l'évaluation) très rapidement sans devoir télécharger des gigaoctets de données.
|
| 15 |
+
* **`fix_epochs.py`** : A probablement réduit le nombre d'époques d'entraînement (epochs) pour accélérer l'exécution du test de bout en bout.
|
| 16 |
+
* **`fix_paths.py` / `patch_train.py` / `patch_image.py`** : Ont servi à corriger les chemins de fichiers et d'autres petits détails d'implémentation pour s'assurer que le notebook puisse tourner correctement sur votre machine.
|
| 17 |
+
* **`notebook_script.py`** : Le code Python final, extrait du notebook, prêt à être exécuté.
|
| 18 |
+
|
| 19 |
+
## 3. Résultats et Fichiers Générés (Outputs)
|
| 20 |
+
|
| 21 |
+
L'exécution du notebook a généré une arborescence complète de résultats, principalement documentée dans le fichier `outputs_summary.md` :
|
| 22 |
+
|
| 23 |
+
* **Modèles Entraînés (`content/experiments/.../models/`) :**
|
| 24 |
+
* `cityscapes_segmentation_model.keras` : Le modèle complet de fin d'entraînement.
|
| 25 |
+
* `checkpoints/best_model.keras` : Le meilleur modèle sauvegardé en cours de route (celui à utiliser pour les prédictions).
|
| 26 |
+
* `class_mapping.json` : Le dictionnaire qui associe les classes prédites (IDs) à leurs couleurs et noms (ex: "route", "voiture").
|
| 27 |
+
* **Métriques et Évaluation (`content/experiments/.../results/`) :**
|
| 28 |
+
* Des fichiers CSV et JSON contenant l'IoU par classe, la matrice de confusion, le rapport de classification et l'historique d'entraînement.
|
| 29 |
+
* **Visualisations (`content/experiments/.../visualizations/`) :**
|
| 30 |
+
* Des graphiques de l'historique d'entraînement (Loss/Accuracy), la matrice de confusion visuelle, et surtout des images comparant l'image originale, le vrai masque, et la prédiction du modèle.
|
| 31 |
+
* **Suivi MLflow (`mlruns/`) :**
|
| 32 |
+
* Une base de données locale MLflow stockant les hyperparamètres de l'expérience et ses métriques.
|
| 33 |
+
|
| 34 |
+
## 4. Prochaines Étapes : Déploiement
|
| 35 |
+
|
| 36 |
+
Gemini a également généré un fichier `Next_step.md` qui explique comment exploiter ces résultats dans la suite de votre projet :
|
| 37 |
+
|
| 38 |
+
* **Intégration Streamlit :** Il fournit le code (boilerplate) nécessaire pour charger le `best_model.keras` et le `class_mapping.json` dans votre application `app.py`. Le modèle est capable de prendre une image uploadée et de renvoyer un masque segmenté coloré.
|
| 39 |
+
* **Déploiement sur Render :** Des mises en garde importantes y sont précisées :
|
| 40 |
+
* Gérer la taille du modèle (76 MB), qui passe sur Git classique mais qui, s'il était plus gros, nécessiterait Git LFS.
|
| 41 |
+
* Faire attention à la limite de RAM (512 MB) du niveau gratuit de Render, TensorFlow étant très gourmand. Il est conseillé d'utiliser `tensorflow-cpu` dans le `requirements.txt` pour éviter les crashs "Out of Memory" (OOM).
|
tests/test_api.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import urllib.parse
|
| 3 |
+
import pandas as pd
|
| 4 |
+
|
| 5 |
+
# 1. Define your base API URL
|
| 6 |
+
base_url = "https://predictionsentiments-azepf7eme8dvftaa.francecentral-01.azurewebsites.net/feeling_predictions/"
|
| 7 |
+
# URL locale pour MLFlow
|
| 8 |
+
#base_url = "http://127.0.0.1:8000/feeling_predictions/"
|
| 9 |
+
|
| 10 |
+
# 2. Define a list of 10 different sentences (mixing happy and sad to test your model)
|
| 11 |
+
sentences = [
|
| 12 |
+
"I just got a promotion at work and I am thrilled!",
|
| 13 |
+
"My dog passed away yesterday and my heart is broken.",
|
| 14 |
+
"The weather is absolutely beautiful today, I love it.",
|
| 15 |
+
"I failed my math exam despite studying all night.",
|
| 16 |
+
"We had a wonderful family dinner with lots of laughs.",
|
| 17 |
+
"I feel so lonely and isolated in this new city.",
|
| 18 |
+
"Winning the championship was the best moment of my life.",
|
| 19 |
+
"My flight got canceled and I missed my best friend's wedding.",
|
| 20 |
+
"I am enjoying a nice, relaxing cup of coffee by the window.",
|
| 21 |
+
"Everything is going wrong today and I just want to cry."
|
| 22 |
+
]
|
| 23 |
+
|
| 24 |
+
# 3. Initialize an empty list to store all the results
|
| 25 |
+
all_results = []
|
| 26 |
+
|
| 27 |
+
headers = {"accept": "application/json"}
|
| 28 |
+
|
| 29 |
+
print("Starting predictions...\n")
|
| 30 |
+
|
| 31 |
+
# 4. Loop through each sentence in the list
|
| 32 |
+
for text in sentences:
|
| 33 |
+
# URL-encode the sentence safely
|
| 34 |
+
encoded_text = urllib.parse.quote(text)
|
| 35 |
+
full_url = f"{base_url}{encoded_text}"
|
| 36 |
+
|
| 37 |
+
try:
|
| 38 |
+
# Call the API
|
| 39 |
+
response = requests.get(full_url, headers=headers)
|
| 40 |
+
response.raise_for_status() # Check for HTTP errors like 500 or 404
|
| 41 |
+
|
| 42 |
+
# Parse the JSON response {"text": "...", "feeling_result": "..."}
|
| 43 |
+
api_data = response.json()
|
| 44 |
+
|
| 45 |
+
# Extract the prediction
|
| 46 |
+
prediction = api_data.get("feeling_result")
|
| 47 |
+
|
| 48 |
+
# Store the success result in our list as a dictionary
|
| 49 |
+
all_results.append({
|
| 50 |
+
"sentence": text,
|
| 51 |
+
"prediction": prediction,
|
| 52 |
+
"status": "success"
|
| 53 |
+
})
|
| 54 |
+
|
| 55 |
+
print(f"Processed: '{text[:30]}...' -> {prediction.upper()}")
|
| 56 |
+
|
| 57 |
+
except requests.exceptions.RequestException as e:
|
| 58 |
+
print(f"Error processing '{text[:30]}...': {e}")
|
| 59 |
+
|
| 60 |
+
# If there is an error, we still store it so we don't lose track
|
| 61 |
+
all_results.append({
|
| 62 |
+
"sentence": text,
|
| 63 |
+
"prediction": "ERROR",
|
| 64 |
+
"status": str(e)
|
| 65 |
+
})
|
| 66 |
+
|
| 67 |
+
print("\n--- All 10 requests completed! ---")
|
| 68 |
+
|
| 69 |
+
# 5. Display and save the stored results
|
| 70 |
+
# Converting the list of dictionaries into a Pandas DataFrame is the easiest way to handle the data
|
| 71 |
+
df_results = pd.DataFrame(all_results)
|
| 72 |
+
|
| 73 |
+
print("\nHere is your final data table:")
|
| 74 |
+
print(df_results)
|
| 75 |
+
|
| 76 |
+
# Save the results to a CSV file in your folder
|
| 77 |
+
csv_filename = "api_predictions_results.csv"
|
| 78 |
+
df_results.to_csv(csv_filename, index=False, encoding="utf-8")
|
| 79 |
+
print(f"\nResults have been successfully saved to '{csv_filename}'.")
|