# Entraîner, annoter, évaluer le modèle CRNN Ce document explique comment lancer chacune des trois étapes : annoter de nouvelles images, ré-entraîner le modèle, évaluer sa précision. Les scripts utilisés vivent dans le dépôt d'origine (`annotator/`, `train/` à la racine), pas dans `yely_ai_module/` — ce dossier livrable embarque une copie de référence de `train/` pour la reproductibilité (voir §"Où ça vit" plus bas). ## Vue d'ensemble du pipeline data → modèle ``` 1. Annoter annotator/annotate.py │ (dessine le rectangle LCD + saisit prix/volume/prix_litre) ▼ annotator/annotations/annotations.json (+ crops dans annotations/crops/) │ 2. Découper en lignes annotator/split_lines.py │ (1 image = 3 valeurs → 3 sous-images mono-ligne) ▼ annotator/dataset_lines/{train,val}/ │ 3. Convertir au format doctr train/prepare_doctr_dataset.py ▼ annotator/dataset_doctr/{train,val}/ (images/ + labels.json) │ 4. Entraîner train/finetune_doctr.py ▼ train/models/crnn_fuel_pump_best.pt (+ history.json) │ 5. Évaluer train/test_model.py ``` ## 1. Annoter de nouvelles images ```bash cd annotator python annotate.py --images ../images/ # nouvelles photos python annotate.py --images ../images/ --resume # reprendre où on s'était arrêté python annotate.py --review # voir un résumé des annotations existantes ``` Pour chaque image : dessiner un rectangle autour de l'écran LCD (clic + glisser), puis saisir dans le terminal les valeurs affichées (prix, volume, prix du litre). Voir `annotator/README.md` pour le détail des raccourcis. **Ce qui compte le plus pour améliorer le modèle** (voir `docs/LIMITATIONS.md`) : annoter des photos avec des **valeurs différentes** de celles déjà présentes, pas juste plus de photos des mêmes tickets. Utiliser `yely_ai_module/tools/check_seen_image.py` pour vérifier qu'une photo candidate n'est pas déjà (quasi-)présente dans le jeu annoté : ```bash python yely_ai_module/tools/check_seen_image.py chemin/vers/nouvelle_photo.jpg ``` ## 2. Régénérer le dataset d'entraînement **Toujours dans cet ordre**, et **jamais pendant qu'un entraînement tourne** (le chargement des images se fait à la volée pendant l'entraînement — les régénérer en même temps fait planter le script avec un `FileNotFoundError`, vécu concrètement pendant cette session) : ```bash cd annotator python split_lines.py # annotations.json -> dataset_lines/ cd ../train python prepare_doctr_dataset.py # dataset_lines/ -> dataset_doctr/ ``` Les deux scripts nettoient maintenant leur dossier de sortie avant de régénérer (corrigé cette session — une même image pouvait sinon se retrouver à la fois en train et en val d'un run à l'autre, faussant l'évaluation). **Vérification recommandée avant d'entraîner** : ouvrir `annotator/dataset_lines/review/review_sheet.jpg`, qui montre un échantillon d'images découpées à côté du label attendu — permet de repérer un mauvais découpage avant de perdre du temps à entraîner dessus. ## 3. Lancer l'entraînement ```bash cd train python finetune_doctr.py # 80 epochs par défaut python finetune_doctr.py --epochs 60 --patience 12 python finetune_doctr.py --from-scratch # si pas de connexion internet (pas de poids pré-entraînés) ``` - Le modèle est sauvegardé dans `train/models/crnn_fuel_pump_best.pt` à chaque fois que la perte de validation s'améliore (pas seulement à la fin) — s'arrêter en cours de route (Ctrl+C, crash) ne perd donc pas tout. - `--patience N` : arrête l'entraînement si la perte de validation ne s'améliore plus pendant N epochs (évite de continuer à surapprendre inutilement). - Sur CPU (pas de GPU disponible), compter ~1.5-3 min/epoch sur le jeu de données actuel (~270 échantillons train). Un entraînement complet peut donc prendre plusieurs heures — lancer en arrière-plan (`... &` ou un terminal dédié) plutôt qu'en bloquant. - À la fin (ou à l'arrêt anticipé), `train/models/history.json` contient la courbe complète (perte/accuracy par epoch) — sert de preuve pour le rapport de test (§15 du cahier des charges). ## 4. Évaluer le modèle ```bash cd train python test_model.py # évalue sur tout le set de validation python test_model.py --image ../images/pompe.jpg # teste une seule image ``` `test_model.py` calcule, sur le jeu de validation : - **Exact-match accuracy** : proportion de lignes lues à 100% correctement (c'est la métrique citée dans `docs/LIMITATIONS.md`). - **CER** (Character Error Rate) et **WER** : à quel point une prédiction fausse est "proche" de la bonne réponse (ex. "10000" lu "1O000" a un CER faible même si l'exact-match échoue) — plus informatif que l'accuracy seule pour juger si le modèle progresse. - La liste des erreurs (jusqu'à 15 affichées), utile pour repérer des motifs d'erreur récurrents (un chiffre systématiquement confondu, une ligne toujours mal découpée...). ## Où ça vit (dépôt d'origine vs dossier livrable) - **Annotation et préparation du dataset** (`annotator/`) : uniquement à la racine du dépôt d'origine, pas dupliqué dans `yely_ai_module/` (le jeu de ~155 images sources n'est volontairement pas inclus dans le livrable). - **Entraînement** (`train/`) : présent aux deux endroits. La copie dans `yely_ai_module/train/` est une référence pour la reproductibilité (documentation technique) ; pour ré-entraîner réellement, utiliser la version à la racine du dépôt, qui a accès à `annotator/dataset_doctr/`. - **Script `train/infer.py`** : ancien script d'inférence autonome (antérieur à `yely_ai_module/app/recognizer.py`), gardé pour test rapide en ligne de commande. Avait le même bug de découpage à 2 lignes (au lieu de 3) que celui corrigé dans `preprocessing.py` — corrigé aussi. Pour tester le pipeline réellement livré (avec règles métier, API), utiliser `yely_ai_module/app/`, pas ce script. ## Si le modèle donne de mauvais résultats en test manuel Avant de conclure "le modèle n'est pas bon", vérifier dans l'ordre : 1. **Quel script a été utilisé ?** `train/infer.py` et `yely_ai_module/app/` ont des logiques de détection d'écran différentes (`detect_lcd_region` vs `detect_screen_region`) — l'un peut réussir là où l'autre échoue sur la même photo. 2. **La zone détectée est-elle la bonne ?** Les deux scripts peuvent confondre le bandeau de marque ("Shell FuelSave...") avec l'écran LCD sur certaines photos (voir `docs/PREPROCESSING.md`) — sauvegarder et regarder le crop intermédiaire avant d'incriminer le CRNN. 3. **La photo est-elle inédite ou proche du jeu d'entraînement ?** (`tools/check_seen_image.py`) — le modèle est attendu comme moins bon sur des valeurs qu'il n'a jamais vues, c'est documenté et mesuré (`docs/LIMITATIONS.md`), pas une surprise. 4. **Quelle précision réelle attendre ?** 70.2% exact-match sur les lignes de validation (`train/models/v2/history.json`) — donc environ 3 lectures sur 10 avec au moins un caractère faux sont *attendues* à ce stade, pas un signe que quelque chose est cassé.