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 l'appli : https://mnist-express.streamlit.app/
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.pyCe script :
charge le dataset MNIST
découpe les données en train/set
benchmarke plusieurs variantes du k-NN
choisit la meilleure configuration
sauvegarde le modèle entraîné
Le résultat est un fichier :
artifacts/models/model_knn_best.joblibAutrement 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).

L'utilisateur voit simplement un canvas noir sur lequel il peut dessiner un chiffre. Quand il clique sur Prédire, l'application :
récupère l'image dessinée
la transforme en matrice de pixels
la prépare pour le modèle
demande une prédiction
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 : 8est 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.jsonCe 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