title: AvisSense
emoji: 🎬
colorFrom: blue
colorTo: red
sdk: docker
app_port: 7860
pinned: false
short_description: Analyse de sentiment d'avis cinema en francais.
🎬 AvisSense — Analyse de sentiment d'avis cinéma (français)
Projet de fin de module M106 — Intro ML/DL · Sujet : analyse de sentiment d'avis en français (secteur culture)
Classification binaire d'avis de cinéma : on colle un avis en français, le modèle prédit positif ou négatif avec un score de confiance (la consigne du sujet, mot pour mot). Un DistilCamemBERT est fine-tuné (transfer learning) sur le dataset Allociné, servi par une API FastAPI avec un petit front intégré, le tout déployé sur Hugging Face Spaces — toute la chaîne, sans usine à gaz.
Le repo inclut un front minimal (HTML/CSS/JS, servi par l'API à
GET /) qui garantit la chaîne complète déployée. Un front plus complet est développé séparément par un autre membre de l'équipe : il consommera les mêmes endpoints (contrat d'API en section 8, Swagger sur/docs, CORS déjà activé).
POST /predict {"text": "Ce film est incroyable"}
-> {"label": "positif", "confidence": 0.94, ...}
1. Objectif
- Fine-tuner un modèle de langage pré-entraîné (transfer learning) pour classer des avis de cinéma en français : positif / négatif.
- Trancher la décision architect : geler le modèle et n'entraîner qu'une tête, ou fine-tuner entièrement ? (section 4)
- Servir le modèle via une API REST (FastAPI) + un petit front « colle un avis → résultat ».
- Déployer publiquement la chaîne complète sur Hugging Face Spaces.
Conformité aux consignes du module
| Consigne | Où c'est respecté |
|---|---|
| Orienté Deep Learning (fine-tuning d'un modèle de texte) | scripts/train.py — DistilCamemBERT fine-tuné sur Allociné |
| Modèle servi avec FastAPI | api/main.py — POST /predict |
| Petit front | frontend/ — servi par l'API à GET / |
| Déployé (HF Spaces ou équivalent) | Dockerfile — Space Docker, section 9 |
| Secteur ≠ démos du cours | culture / cinéma |
| Décisions défendues (modèle, stratégie de transfer) | sections 3 et 4 — les deux stratégies sont implémentées et comparées |
| Livrables | code + README (ce fichier) + lien du modèle déployé (section 9) + vidéo ≤ 10 min sur Teams avant vendredi 13h |
2. Dataset — Allociné (avis ciné uniquement)
- Source :
allocinesur le Hugging Face Hub. - Contenu : ~200 000 avis de films en français, étiquetés
0(négatif) ou1(positif), labels dérivés des notes utilisateurs (donc fiables). Classes équilibrées ~50/50. - Splits fournis : train (160k) / validation (20k) / test (20k) — déjà séparés, aucun risque de fuite de données.
- Colonnes :
review(texte) etlabel. - Sous-échantillonnage : entraînement sur 8 000 avis (validation 2 000, test 2 000) pour rester rapide (~20 min sur GPU Colab gratuit). Le modèle pré-entraîné connaît déjà le français : il n'apprend que la frontière positif/négatif, et la performance sature vite — les 160k complets n'apporteraient que 1-2 points de F1 pour 20× plus de calcul. Ajustable via
--max-train.
3. Modèle — DistilCamemBERT
| Modèle | Langue | Paramètres | Vitesse | Performance FR |
|---|---|---|---|---|
| CamemBERT-base | Français | 110M | 1× | Excellente |
| DistilCamemBERT ✅ | Français | 68M | ~2× | ~97 % de CamemBERT |
| DistilBERT multilingual | 104 langues | 134M | ~1.5× | Moyenne (capacité diluée) |
Choix : cmarkea/distilcamembert-base — spécialisé français (un modèle multilingue dilue sa capacité sur 104 langues et tokenise moins bien le français), 2× plus rapide que CamemBERT à l'entraînement comme en inférence, pour une perte négligeable sur une tâche binaire. Crucial pour servir le modèle sur le CPU gratuit de Spaces.
4. Décision Architect — geler le backbone ou fine-tuner ?
C'est l'arbitrage central du projet, et les deux stratégies sont implémentées dans train.py pour le trancher avec des chiffres plutôt qu'une intuition :
| Stratégie A — fine-tuning complet (défaut) | Stratégie B — backbone gelé (--freeze-backbone) |
|
|---|---|---|
| Paramètres entraînés | 68M (100 %) | |
| Learning rate | 2e-5 (faible : on ajuste sans détruire) | 1e-3 (la tête part de zéro) |
| Coût d'entraînement | ~20 min (GPU Colab) | ~3× plus rapide, envisageable sur CPU |
| F1 attendu (test) | ≈ 0.93–0.96 | ≈ 0.85–0.89 |
# Stratégie A — fine-tuning complet
python scripts/train.py
# Stratégie B — backbone gelé, tête seule
python scripts/train.py --freeze-backbone --learning-rate 1e-3
Chaque run écrit ses métriques dans model/sentiment_model/training_metrics.json (avec le champ strategy) : comparer les deux JSON donne l'arbitrage chiffré.
Décision retenue : fine-tuning complet. Le gel du backbone utilise les features génériques du pré-entraînement (masked language modeling), qui ne sont pas optimisées pour discriminer le sentiment ; laisser le backbone s'adapter au vocabulaire des critiques de cinéma gagne ~5-8 points de F1. Le surcoût de calcul (20 min de Colab gratuit) est négligeable, et le coût d'inférence est identique dans les deux cas — l'argument "économie" du gel ne vaut que pour l'entraînement. Le gel resterait pertinent avec très peu de données (< 1 000 exemples, risque d'overfitting) ou sans aucun GPU disponible — ce n'est pas notre cas.
5. Architecture du projet
AvisSense/
├── api/
│ └── main.py # API FastAPI : front sur /, /info, /health, POST /predict
├── frontend/
│ ├── index.html # Front minimal : zone de texte + bouton + résultat
│ ├── style.css # Style (label coloré + barre de confiance)
│ └── script.js # fetch() vers POST /predict
├── model/
│ └── sentiment_model/ # Modèle fine-tuné (créé par train.py, non versionné)
├── scripts/
│ ├── train.py # Entraînement : les 2 stratégies (gel / fine-tuning)
│ ├── predict.py # Prédiction en CLI (inférence manuelle détaillée)
│ ├── evaluate.py # Évaluation test set + analyse des erreurs
│ └── push_model_to_hub.py # Upload du modèle sur le HF Hub
├── Dockerfile # Déploiement Hugging Face Spaces (Docker)
├── requirements.txt # Dépendances Python
├── .gitignore
└── README.md
Rôle des dossiers :
scripts/— cycle de vie du modèle (entraînement, évaluation, publication). Séparé de l'API : on peut ré-entraîner sans toucher au serveur.api/— le serveur. Charge le modèle une fois au démarrage (lifespan), expose les endpoints, sert le front, CORS activé pour le futur front externe.frontend/— le petit front demandé par les consignes : fichiers statiques servis par FastAPI, aucune dépendance supplémentaire. Remplaçable par le front complet de l'équipe sans toucher à l'API.model/— artefact produit par l'entraînement. Ignoré par git (270 Mo) : les poids sont stockés sur le HF Hub, le code sur GitHub.
6. Installation
git clone https://github.com/VOTRE_PSEUDO/AvisSense.git
cd AvisSense
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Linux / macOS
pip install -r requirements.txt
7. Entraînement et évaluation
# Entraînement standard (fine-tuning complet, 8000 avis, 2 époques)
python scripts/train.py
# Comparaison avec le backbone gelé (décision architect, section 4)
python scripts/train.py --freeze-backbone --learning-rate 1e-3
# Version rapide pour tester le pipeline sur CPU (~10 min)
python scripts/train.py --max-train 2000 --max-val 500 --max-test 500 --epochs 1
Sur Google Colab (GPU gratuit, recommandé) : uploader le dossier, puis :
!pip install -q transformers datasets accelerate scikit-learn
!python scripts/train.py
Le script affiche l'exploration du dataset, la progression, puis les métriques finales sur le test set avec matrice de confusion, et sauvegarde le modèle dans model/sentiment_model/.
# Prédiction en CLI (--verbose : tokens, logits, softmax ; sans argument : mode interactif)
python scripts/predict.py "Ce film est un chef-d'œuvre absolu !"
# Évaluation détaillée : rapport par classe, matrice de confusion,
# et les erreurs où le modèle était le plus confiant (analyse des limites)
python scripts/evaluate.py --max-test 2000 --show-errors 5
8. Lancement : API + front
uvicorn api.main:app --reload
# Front : http://127.0.0.1:8000
# Swagger : http://127.0.0.1:8000/docs
| Méthode | Route | Description |
|---|---|---|
| GET | / |
Le front minimal : coller un avis → prédiction + confiance |
| GET | /info |
Informations sur l'API (JSON) |
| GET | /health |
{"status": "ok", "model_loaded": true, ...} |
| POST | /predict |
Prédiction de sentiment |
POST /predict — requête :
{ "text": "Ce film est incroyable" }
Réponse 200 :
{
"label": "positif",
"confidence": 0.9812,
"probabilities": { "négatif": 0.0188, "positif": 0.9812 },
"processing_time_ms": 47.3
}
Erreurs :
| Code | Cas | Corps |
|---|---|---|
| 400 | texte vide / espaces | {"detail": "Le texte ne peut pas être vide."} |
| 400 | texte > 5000 caractères | {"detail": "Texte trop long (...)"} |
| 422 | JSON malformé ou champ text manquant |
détail Pydantic automatique |
| 503 | modèle pas encore chargé | {"detail": "Modèle en cours de chargement."} |
Exemples d'appel :
# PowerShell
Invoke-RestMethod -Uri http://127.0.0.1:8000/predict -Method Post `
-ContentType "application/json" -Body '{"text": "Ce film est incroyable"}'
# curl
curl -X POST http://127.0.0.1:8000/predict \
-H "Content-Type: application/json" \
-d '{"text": "Ce film est incroyable"}'
CORS est activé (allow_origins=["*"]) pour que le frontend hébergé ailleurs puisse appeler l'API ; à restreindre au domaine réel en production.
9. Déploiement sur Hugging Face Spaces
Deux artefacts, deux destinations : les poids sur le Hub, l'API sur un Space Docker (qui télécharge les poids au démarrage).
Étape A — Pousser le modèle sur le Hub
huggingface-cli login # token "Write" depuis Settings > Access Tokens
python scripts/push_model_to_hub.py --repo VOTRE_PSEUDO/avissense-distilcamembert
Étape B — Créer le Space
- https://huggingface.co/new-space : SDK Docker, hardware CPU basic (gratuit).
- Dans Settings > Variables du Space :
MODEL_ID = VOTRE_PSEUDO/avissense-distilcamembert - Pousser le code :
git remote add space https://huggingface.co/spaces/VOTRE_PSEUDO/avissense
git push space main
La chaîne complète est alors visible à l'URL publique du Space : https://VOTRE_PSEUDO-avissense.hf.space/ ouvre directement le front « colle un avis → résultat » (et /docs la documentation de l'API). C'est ce lien qu'on remet comme livrable « modèle déployé ». La première requête après un réveil du Space est lente (téléchargement + chargement du modèle) ; /health permet de vérifier que le modèle est prêt — à faire avant la démo vidéo.
10. Limites
- Domaine ciné : entraîné sur des avis de films — les performances baissent hors domaine (produits, restaurants) car le vocabulaire diffère (« lourd » est négatif pour un film, neutre pour un plat).
- Binaire : pas de classe « neutre » — un avis mitigé est forcé dans un camp, généralement avec une confiance plus faible (~0.5-0.7).
- Ironie / sarcasme : « Bravo, 2 h de ma vie perdues » contient des mots positifs avec un sens négatif.
- Confiance non calibrée : le score softmax est souvent sur-confiant ; 0.94 ne signifie pas « 94 % de chances d'avoir raison » au sens statistique.
- Troncature à 256 tokens : la fin des très longs avis est ignorée.
11. Améliorations futures
- Classe neutre (3 classes, autre dataset).
- Entraînement sur les 160k avis complets (+1-2 points de F1).
- Calibration du score de confiance (temperature scaling).
- Endpoint batch (
POST /predict/batch) pour analyser plusieurs avis en un appel. - Export ONNX + quantization int8 : inférence 2-4× plus rapide sur CPU.
- Monitoring : logger les prédictions à faible confiance pour ré-annotation (amélioration continue).
12. Interface Gradio optionnelle
En plus de l'API FastAPI et de son front statique, une interface Gradio est disponible :
python app.py
FastAPI, Gradio et le script de prediction utilisent tous src/inference.py.
Le modele est charge depuis model/sentiment_model/ en local, ou depuis le
repo Hugging Face indique par la variable d'environnement MODEL_ID.
Le rapport de fusion des branches est disponible dans
docs/merge_report.md.
Projet réalisé dans le cadre du module M106 — Intro ML/DL.