top of page

MNIST Express - 7/8 - Du notebook à la mini-app instrumentée

  • 13 mars
  • 5 min de lecture

Dernière mise à jour : 22 mars

Lien vers le repo du projet : https://github.com/vohorgeez/MNIST-Express


Dans les anciennes versions, tout se passait dans un notebook. C'est l'endroit idéal pour explorer une idée : on charge des données, on teste un algorithme, on trace quelques graphiques, et on voit si quelque chose d'intéressant se produit. Mais un notebook, ce n'est pas une application. Un notebook sert à répondre à une question simple :

Est-ce que cette idée fonctionne ?

Une application, elle, doit répondre à une autre question :

Que vaut cette idée face à de vrais êtres humains ?

Il était important que le projet MNIST Express quitte le laboratoire et devienne une mini-application interactive, capable de recevoir un dessin, produire une prédiction, et surtout mesurer ce qui se passe quand quelqu'un s'en sert.



Quitter le notebook


Un notebook, par définition, mélange tout :

  • chargement des données

  • entraînement du modèle

  • tests

  • visualisation

  • parfois même l'interface utilisateur


C'est pratique pour expérimenter et visualiser, mais c'est un petit enfer à maintenir. La première transformation majeure projet est donc inévitablement de séparer les responsabilités. Comme dans un vrai projet, oui ! L'entraînement du modèle devient donc un script à part, reproductible à souhait :

python run_train.py

Ce script :

  1. charge le dataset MNIST

  2. découpe les données en train/set

  3. benchmarke plusieurs variantes du k-NN

  4. choisit la meilleure configuration

  5. sauvegarde le modèle entraîné


Le résultat est un fichier :

artifacts/models/model_knn_best.joblib

Autrement dit, le modèle est maintenant un artefact. Une fois entraîné, il peut être chargé et utilisé par une application sans avoir besoin de réentraîner quoi que ce soit (et heureusement).


C'est une distinction essentielle :

  • entraîner un modèle fabrique l'outil 🔧

  • inférer avec un modèle utilise cet outil 👨🏻‍🔧


L'application n'apprend pas en direct, mais consomme un modèle déjà construit (et encore une fois, heureusement !).


Brancher une interface utilisateur


Une fois le modèle prêt, il doit être utilisable, non ? L'application utilise Streamlit, une petite bibliothèque Python qui permet de construire une interface web très rapidement (et de manière très, très simple, il faut le dire).


Les GOAT
Les GOAT

L'utilisateur voit simplement un canvas noir sur lequel il peut dessiner un chiffre. Quand il clique sur Prédire, l'application :

  1. récupère l'image dessinée

  2. la transforme en matrice de pixels

  3. la prépare pour le modèle

  4. demande une prédiction

  5. affiche le résultat


Le modèle ne reçoit jamais le dessin brut. Il reçoit une matrice de 784 valeurs numériques, correspondant à une image 28x28 pixels, exactement comme dans le dataset MNIST. Comme nous l'avons déjà dit dans nos précédents articles, ce détail peut sembler banal ou superflu, mais il cache en réalité le vrai défi du projet.


Le vrai problème : l'entrée utilisateur


Dans un notebook, les données sont propres :

  • toutes les images ont la même taille

  • les chiffres sont centrés

  • les intensités sont cohérentes


Un utilisateur humain, lui, dessine n'importe comment (oui). Le chiffre peut très bien être :

  • trop petit

  • trop grand

  • mal centré

  • dessiné dans un coin

  • inexistant (aussi, oui)


Avant même d'envoyer quoi que ce soit au modèle, l'application doit donc reconstruire une image "MNIST-compatible".


Le pipeline de prétraitement fait donc plusieurs choses que nous détaillons dans cet article. Ce processus transforme un dessin chaotique en quelque chose que le modèle peut comprendre. Le modèle, lui, ne change pas. En revanche, on s'assure de lui envoyer des données réalistes qu'il peut comprendre.


Une prédiction doit être compréhensible


Une application qui affiche simplement :

Prédiction : 8

est techniquement correcte... mais très pauvre.

L'interface de MNIST-Express affiche donc plusieurs informations :

  • la classe prédite

  • le niveau de confiance du modèle

  • les trois classes les plus probables

  • le temps d'inférence

  • l'image prétraitée réellement envoyée au modèle.


Cela permet de voir non seulement ce que le modèle pense, mais aussi à quel point il hésite. Par exemple, un chiffre peut être interprété comme :

8 : 62%
3 : 28%
9 : 10%

Ce genre d'information rend le comportement du modèle beaucoup plus lisible.


Observer l'application en train de vivre


C'est ici que le projet devient intéressant. Dans un notebook, on mesure généralement :

  • l'accuracy

  • les matrices de confusion

  • quelques métriques statistiques


Mais une fois que l'application est utilisée, on se retrouve dans une autre réalité... celle de la vraie vie du vrai monde véritable justement. Sur les premières versions de MNIST Express, notre modèle de l'époque, entraîné alors sur load_digits() (une version du dataset MNIST avec des images en 8x8 au lieu de 28x28), donnait de bons résultats en notebook... Mais était catastrophique en utilisation réelle.


D'où l'idée de mieux confronter le modèle à l'utilisation réelle, et donc de se poser les bonnes questions :

  • combien de prédictions sont faites ?

  • combien de temps prend une prédiction ?

  • les utilisateurs pensent-ils que le résultat est correct ?


Le projet ajoute donc une instrumentation minimale. A chaque prédiction, l'application enregistre :

  • le nombre total de prédictions

  • le temps d'inférence

  • la date de la dernière utilisation


Les utilisateurs peuvent aussi signaler si la prédiction était correcte ou non. Ces informations permettent de calculer :

  • le temps moyen d'inférence

  • le taux d'erreur perçu par les utilisateurs


Toutes ces statistiques sont stockées dans un fichier simple :

artifacts/monitoring/usage_stats.json

Ce n'est pas un système industriel sophistiqué, mais c'est déjà une étape fondamentale : ne plus se contenter de tester un modèle sur un dataset fermé, mais observer comment il se comporte VRAIMENT en conditions réelles.


Centraliser les décisions du système


A mesure que le projet grandit, on se retrouve avec de plus en plus de paramètres :

  • nombre de voisins du k-NN

  • taille des batchs d'inférence

  • algorithme de recherche (brute, kd_tree, ball_tree)

  • activation du monitoring

  • emplacement des modèles sauvegardés


Plutôt que de disperser ces valeurs dans le code, elles sont regroupées dans une configuration unique. En clair : on range sa chambre ! 😉 La configuration devient ainsi un centre de contrôle du projet, où l'on peut modifier le comportement global de l'application sans réécrire plusieurs fichiers.


Ajouter quelques garde-fous


Enfin, le projet introduit quelques tests automatiques. Ils vérifient notamment :

  • que les images ont la bonne forme

  • que les transformations de prétraitement produisent bien du 28x28

  • que le pipeline d'inférence renvoie les structures attendues


Ces tests ne couvrent pas tout. Mais ils verrouillent les hypothèses les plus fragiles du système. C'est souvent suffisant pour éviter les bugs les plus absurdes.


Le petit truc en plus


A première vue, donc, le modèle ne bouge pas et est toujours le même : un simple k-Nearest Neighbors. Mais le projet, lui, a changé de nature.


Au début, il s'agissait surtout de comprendre :

  • comment fonctionne l'algorithme

  • comment représenter les images

  • comment mesurer la performance


Aujourd'hui, le projet sait :

  • entraîner un modèle

  • sauvegarder ce modèle

  • charger ce modèle dans une application

  • transformer une entrée utilisateur imprévisible

  • produire une prédiction compréhensible

  • observer l'usage réel de l'application


Il ne s'agit plus de construire un modèle mais un petit système autour de celui-ci !


Commentaires


RETROUVEZ-MOI

  • X
  • GitHub
  • LinkedIn
  • Instagram
  • YouTube
bottom of page