--- 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** : [`allocine`](https://huggingface.co/datasets/allocine) sur le Hugging Face Hub. - **Contenu** : ~200 000 avis de films en français, étiquetés `0` (négatif) ou `1` (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) et `label`. - **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`](https://huggingface.co/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 %) | ~0.6M (~1 %, la tête seule) | | 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 | ```bash # 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 ```bash 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 ```bash # 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 : ```python !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/`. ```bash # 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 ```bash 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 : ```json { "text": "Ce film est incroyable" } ``` Réponse `200` : ```json { "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 : ```bash # 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 ```bash 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 1. https://huggingface.co/new-space : SDK **Docker**, hardware **CPU basic (gratuit)**. 2. Dans **Settings > Variables** du Space : `MODEL_ID = VOTRE_PSEUDO/avissense-distilcamembert` 3. Pousser le code : ```bash 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 : ```bash 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.*