Bibliothèque de machine learning

Modèles développés from scratch

Distinguer vaches, chèvres et moutons à partir des pixels de leurs images. Comprendre les modèles développés en Rust et expliquer leurs décisions.

Découvrir le projet

Trois espèces, une progression dans les modèles

Vache
Chèvre
Mouton
  1. 1. Préparer ce que le modèle voitLa photo, ses pixels, son étiquette.
  2. 2. Comprendre les trois modèlesLinéaire : additionner. RBF : comparer à des centres. MLP : apprendre des transformations.
  3. 3. Examiner leurs décisionsFrontières 2D, expériences d’images et confusions, puis test des photos.

1 / 3 · Des photos aux modèles

Vache, chèvre ou mouton : apprendre à distinguer les trois

Une photo arrive dans l’application. Le but est de choisir entre vache, chèvre et mouton à partir de ses pixels. Pour comprendre ce qui rend cette reconnaissance possible — et ce qui lui résiste — le projet construit une bibliothèque de modèles from scratch en Rust, pilotée depuis Python.

Un problème concret pour comprendre les calculs

Nous allons préparer les images, comprendre une combinaison linéaire, ajouter des repères locaux avec le RBF, puis des couches apprises avec le MLP. Ensuite, les expériences de chaque modèle permettront de relier ses décisions à son fonctionnement. À la fin, vous pourrez classer des photos du dataset.

Cliquez dans l’image pour lire ses trois valeurs

Chargement des pixels…

Le curseur permet la même exploration au clavier. On lit R, G, B, puis le pixel suivant : 40 × 40 × 3 = 4 800 coordonnées. Les valeurs normalisées utilisent le minimum et le maximum de toute la photo.

1 / 3 · Des photos aux modèles

Une chèvre devient 4 800 nombres

Nous reconnaissons une tête, des pattes ou une silhouette. Le modèle reçoit seulement une image de 40 × 40 pixels, avec trois valeurs par pixel : rouge, vert et bleu. En lisant les lignes successivement, on obtient un vecteur de 4 800 coordonnées. La position de chaque valeur reste fixe.

Pourquoi imposer le même format ?

Un modèle attend toujours le même nombre d’entrées. Convertir les images en RGB évite qu’une photo en niveaux de gris ou avec transparence produise une autre longueur. La normalisation min–max de chaque image ramène ses valeurs entre 0 et 1. Le redimensionnement économise du calcul, mais des détails utiles, comme la texture du pelage, deviennent difficiles à distinguer.

Même entrée, une cible pour apprendre

Vache
Chèvre
Mouton
Vache → [+1, −1, −1]
Chèvre → [−1, +1, −1]
Mouton → [−1, −1, +1]
  1. 1. Apprendre sur les images étiquetéesLes corrections modifient les poids ; la photo ne change pas.
  2. 2. Figer le modèleLa prédiction d’évaluation n’ajuste aucun paramètre.
  3. 3. Comparer classe prédite et classe attendueUne photo donne une seule réponse, même si trois scores sont calculés.

1 / 3 · Des photos aux modèles

Montrer la photo, puis corriger la réponse

Les dossiers associent chaque image à une classe. Pour une chèvre, la cible est [−1, +1, −1], dans l’ordre vache, chèvre, mouton. Le modèle calcule trois scores ; l’indice du plus grand détermine sa réponse. Apprendre consiste à ajuster les paramètres pour rapprocher ces scores de la cible.

Pourquoi une bonne réponse ne suffit-elle pas ?

Un modèle peut réussir sur les photos déjà utilisées et échouer sur une nouvelle pose. Il faut donc séparer les images d’apprentissage de celles d’évaluation, et garder une photo et ses variantes augmentées dans le même groupe. Nous distinguerons les résultats annoncés dans le rapport des diagnostics recalculés sur les sauvegardes.

Un pixel contribue ; il ne décide pas seul

Vache
Chèvre
Mouton
z chèvre = b + w₁x₁ + … + w₄₈₀₀x₄₈₀₀
score chèvre = tanh(z chèvre)

Contribution de ce canal : 0,5 × 0,8 = 0,4. Les 4 799 autres valeurs et le biais participent aussi.

Valeurs manipulables pour comprendre l’équation ; ce poids n’est pas extrait d’une prédiction. Chaque classe possède ses propres poids.

2 / 3 · Comprendre les modèles

Un poids par valeur de pixel, trois scores à départager

Le modèle linéaire attribue à chaque valeur RGB un poids pour chacune des trois classes. Il multiplie les pixels par ces poids, additionne les contributions et ajoute un biais. Un poids positif pousse le score vers le haut ; un poids négatif le réduit. La sortie tanh borne le résultat entre −1 et +1.

Pourquoi commencer par lui ?

On peut suivre toute sa décision : 4 800 contributions et un biais par classe, soit 14 403 paramètres. Mais il combine directement les pixels, sans apprendre de représentation intermédiaire. Dans une vue à deux coordonnées, la frontière entre deux classes est une droite. En 4 800 dimensions, c’est un hyperplan : ajouter des pixels ne change pas la nature linéaire de la séparation.

Corriger le score chèvre d’un exemple simplifié

x = 0,8 · w = 0,5 · biais = 0
z = 0,4 · cible chèvre = +1
  1. 1. Erreur : 1 − 0,4 = 0,6La sortie affine est trop basse pour cette classe.
  2. 2. Δw = α × 0,8 × 0,6Correction : 0,048. Le biais reçoit aussi α × 0,6.
  3. 3. Nouveau poids : 0,548La contribution de ce pixel passe de 0,4 à 0,438.

Un seul canal est représenté pour suivre le calcul. Dans l’image, les 4 800 poids sont corrigés, pour chacune des trois sorties.

2 / 3 · Comprendre les modèles

Une erreur indique dans quel sens déplacer les poids

Imaginons une chèvre dont le score chèvre est trop faible. Les pixels actifs reçoivent une correction qui augmente ce score lors d’une prochaine présentation. Le taux d’apprentissage α règle l’amplitude du changement : trop grand, il peut faire osciller les paramètres ; trop petit, l’apprentissage avance lentement.

Ce que fait précisément cette implémentation

L’entraînement linéaire corrige la sortie affine z avec w ← w + α × x × (cible − z), sans dérivée de tanh. La prédiction applique ensuite tanh. Cette fonction est croissante : elle conserve l’ordre des scores tant que l’arrondi ne les rend pas égaux. Une baisse de l’erreur numérique ne prouve donc pas, à elle seule, une meilleure classification.

Un centre, une zone de réponse

Vache
Chèvre
Mouton
Centre · distance 010
Réponse Rust : 0,961
Gaussienne usuelle : 0,88

Trait plein : exp(−distance⁴ / γ²), formule du code Rust. Pointillés : exp(−γ × distance²). Ici la position varie sur un axe pour rendre la distance visible ; γ n’a pas la même interprétation dans les deux formules.

Plusieurs centres produisent plusieurs réponses, combinées par les poids de sortie. Les photos illustrent le problème visé ; cette courbe explique un mécanisme, sans constituer un RBF entraîné sur leurs pixels.

2 / 3 · Comprendre les modèles

Remplacer la droite unique par des zones de proximité

Si plusieurs groupes d’images d’une même espèce sont éloignés, une seule séparation linéaire peut être insuffisante. Le principe RBF consiste à placer des centres, mesurer la distance à chacun, puis combiner leurs réponses locales. Pour des images, un centre serait un vecteur moyen de pixels : pas forcément une photo réelle ni un animal reconnaissable.

Pourquoi utiliser K-means avant la sortie linéaire ?

K-means alterne affectation au centre le plus proche et déplacement du centre vers la moyenne de son groupe. Il construit des repères sans utiliser les étiquettes. Les poids de sortie apprennent ensuite à combiner ces proximités pour classer. La bibliothèque fournie travaille toutefois avec des points (x, y) : elle ne possède pas une version image opérationnelle.

Comparer la portée de deux fonctions locales

Centre · distance 010
Réponse Rust : 0,961
Gaussienne usuelle : 0,88

Trait plein : exp(−distance⁴ / γ²), formule du code Rust. Pointillés : exp(−γ × distance²). Ici la position varie sur un axe pour rendre la distance visible ; γ n’a pas la même interprétation dans les deux formules.

2 / 3 · Comprendre les modèles

Un centre trop local ne reconnaît plus ses voisins

Une activation forte signifie que l’entrée ressemble au centre selon la distance choisie. Des zones très étroites isolent les exemples ; des zones très larges donnent des réponses proches pour des entrées différentes. Le nombre de centres et leur portée déterminent la finesse de la séparation.

Pourquoi la formule exacte compte-t-elle ?

Le principe usuel utilise exp(−γ × distance²). Le code Rust fourni élève encore la distance au carré et calcule exp(−distance⁴ / γ²) : la transition n’a donc pas exactement la même forme. Le visuel permet de comparer les deux. L’atelier 2D utilise la forme gaussienne usuelle ; ses résultats décrivent cette adaptation pédagogique.

4 800 valeurs → 50 → 50 → 3 scores

4 80050 · tanh50 · tanh3 · tanh

Quelques neurones sont dessinés pour garder les connexions lisibles. Chaque connexion correspond à un poids appris ; les biais ne sont pas représentés.

  1. Pixels : positions et couleursLa photo entre sans description de « corne » ou de « laine ».
  2. Couches cachées : combinaisons apprisesLes activations peuvent combiner des contrastes. Leur attribuer un concept précis demanderait de les analyser.
  3. Sortie : vache, chèvre, moutonLe score le plus élevé choisit une classe.

2 / 3 · Comprendre les modèles

Apprendre les transformations au lieu de fixer la distance

Le MLP conserve les sommes pondérées du modèle linéaire, mais insère des couches cachées et une activation tanh après chaque couche. Les neurones ne comparent plus seulement la photo à des centres : ils apprennent des combinaisons de pixels, puis des combinaisons de ces réponses.

Pourquoi les activations sont-elles indispensables ?

Deux couches linéaires successives se réduisent à une seule transformation linéaire. Tanh empêche cette réduction et permet des frontières plus complexes. La sauvegarde testable suit 4 800 → 50 → 50 → 3, soit 242 753 paramètres. Chaque neurone est connecté à toute l’entrée précédente : il n’existe ici aucun filtre convolutionnel qui reconnaîtrait automatiquement une même forme à différentes positions.

Quand tanh sature, le gradient diminue

activation = tanh(1) = 0,7616
dérivée = 1 − activation² = 0,41997
−5+5

Autour de zéro, la dérivée approche 1. Vers ±5, elle approche 0. Dans le MLP, cette dérivée multiplie l’erreur transmise aux couches précédentes.

δ sortie = (score − cible) × (1 − score²)
w ← w − α × activation précédente × δ

2 / 3 · Comprendre les modèles

Faire remonter l’erreur jusqu’aux pixels

À la sortie, on compare les trois scores à la cible. La rétropropagation calcule ensuite la contribution de chaque poids à cette erreur, couche après couche en sens inverse. La correction d’un poids combine l’activation qui le traverse, l’erreur transmise et le taux d’apprentissage.

Pourquoi plus de couches ne garantit-il pas de meilleurs résultats ?

La dérivée de tanh vaut 1 − activation². Quand une activation approche −1 ou +1, cette dérivée devient petite : l’erreur se transmet peu. Le visuel montre ce phénomène de saturation. Il peut freiner l’apprentissage ; établir qu’il explique un essai précis demanderait de mesurer ses gradients. Les pixels peu détaillés et la diversité limitée des exemples restent d’autres hypothèses.

Une décision traverse les mêmes étapes

  1. Python · donnéesImage RGB → normalisation → vecteur → cible.
  2. ctypes · interface CTransmettre les nombres, leur longueur et les paramètres, sans changer leur ordre.
  3. Rust · calculs explicitesPrédiction → erreur → mise à jour → sauvegarde JSON.
  4. Python · expérienceLire les sorties, calculer les classes, tracer la matrice.

Pourquoi cette séparation ? Les opérations numériques sont réutilisables ; les scripts d’expérimentation peuvent changer sans réécrire les modèles.

2 / 3 · Comprendre les modèles

Rust calcule ; Python prépare, mesure et affiche

Les modèles sont écrits en Rust pour construire explicitement leurs paramètres, leur prédiction et leur apprentissage. Une interface C expose ces opérations à Python via ctypes. Python rassemble les images, prépare les tableaux, lance les essais et trace les résultats ; Streamlit rend ces fonctions manipulables.

Pourquoi sauvegarder les poids ?

Une sauvegarde permet de rejouer une décision sans réentraîner. C’est ce que fait l’atelier final : il charge deux jeux de poids du projet et calcule leurs sorties dans le navigateur. Avant de les utiliser, nous allons comparer les modèles sur une géométrie lisible, puis examiner ce que les expériences d’images permettent réellement de conclure.

Du cas géométrique aux confusions d’animaux

● Classe A · ■ Classe B

37 / 64 exemples correctement classés · erreur quadratique / 2 : 0,4991.

La droite privilégie une partie des quadrants et laisse des erreurs dans les autres. Aucune droite ne peut résoudre tout XOR.

Conditions de cette expérience 2D

Mesures recalculées avec le moteur pédagogique de l’atelier. 64 points déterministes, graine 1701, 1 000 passes, α = 0,025, huit neurones cachés ou centres. Pour le RBF, centres fixés par 12 itérations K-means, γ = 4, résolution régularisée à 0,0001 : les appels suivants ne réentraînent pas ses centres. Mesure sur l’apprentissage, une seule initialisation. Les fonds représentent la classe prédite ; les formes indiquent les étiquettes.

Les adaptations linéaire et RBF diffèrent du code Rust sur l’apprentissage et l’activation, comme expliqué dans les chapitres précédents.

Relier les sorties à une photo

Chèvre du dataset
Étiquette : Chèvre
  • Vache0,17
  • Chèvre-0,318
  • Mouton-0,592

Réponse : Vache. Le plus grand score désigne une autre espèce : le modèle se trompe sur cette photo. Ces scores sont calculés avec la sauvegarde, sans réentraînement ; ils ne sont pas des probabilités.

Chaque score résulte d’une somme de contributions des pixels. L’erreur indique que cette somme favorise une autre classe ; elle ne permet pas, seule, de désigner une partie de l’animal responsable.

Linéaire · diagnostic de la sauvegarde

Lignes : espèce du dossier · colonnes : prédiction
Réel ↓ / Prédit →VacheChèvreMouton
Vache4315712
Chèvre27417749
Mouton54017684

38,4 % de réponses conformes aux étiquettes sur 1 800 images. Rappel mouton : 10,5 %.

La sauvegarde choisit souvent vache. Elle retrouve une grande partie des vaches, mais manque beaucoup de moutons. Le score global cache ce déséquilibre.

Diagnostic sur des dossiers qui recoupent les données d’origine ; ce pourcentage n’est pas une performance sur des images inédites.

3 / 3 · Interpréter les expériences

Séparer deux groupes ne suffit pas à reconnaître une espèce

Sur deux groupes séparables, une droite peut donner la bonne réponse. Avec XOR, les exemples d’une même classe occupent deux coins opposés : aucune droite ne les sépare tous. Ces cas isolent la capacité du modèle. Une photo ajoute des milliers de coordonnées, mais la frontière du modèle reste linéaire.

Que disent les essais sur les animaux ?

Le rapport décrit des performances faibles et un apprentissage peu satisfaisant, même en augmentant les itérations. Le diagnostic ci-contre rejoue une sauvegarde sur les dossiers d’images disponibles : la matrice montre quelles espèces sont confondues. Un pixel de prairie peut contribuer au score comme un pixel de l’animal ; cela explique une sensibilité possible au cadrage, sans prouver que le fond cause chaque erreur.

RBF : ce que les points permettent de vérifier

● Classe A · ■ Classe B

62 / 64 exemples correctement classés · erreur quadratique / 2 : 0,1112.

Les zones de proximité rendent la frontière courbe. Avec huit centres, quelques exemples restent du mauvais côté : une capacité non linéaire ne garantit pas une séparation parfaite.

Conditions de cette expérience 2D

Mesures recalculées avec le moteur pédagogique de l’atelier. 64 points déterministes, graine 1701, 1 000 passes, α = 0,025, huit neurones cachés ou centres. Pour le RBF, centres fixés par 12 itérations K-means, γ = 4, résolution régularisée à 0,0001 : les appels suivants ne réentraînent pas ses centres. Mesure sur l’apprentissage, une seule initialisation. Les fonds représentent la classe prédite ; les formes indiquent les étiquettes.

Les adaptations linéaire et RBF diffèrent du code Rust sur l’apprentissage et l’activation, comme expliqué dans les chapitres précédents.

Images d’animaux : entraînement non abouti

Prochaine étape nécessaire : des centres à 4 800 dimensions et une évaluation dédiée. Aucune prédiction RBF d’animal n’est inventée dans l’atelier.

Voir aussi la figure RBF du rapport

3 / 3 · Interpréter les expériences

Des frontières locales en 2D, un passage aux images inachevé

Les centres donnent au RBF plusieurs zones de réponse : il peut traiter une organisation XOR ou des groupes imbriqués sans demander une droite unique. Le visuel montre une expérience recalculée sur des points. Le résultat dépend de la couverture des données par les centres, de leur portée et de la résolution des poids de sortie.

Pourquoi ne pas afficher une accuracy sur les chèvres ?

Le rapport indique que l’entraînement RBF sur les images n’a pas abouti. La structure Point du code conserve uniquement x et y, alors qu’une image contient 4 800 valeurs. Il faudrait généraliser les centres, les distances et l’interface, puis entraîner et évaluer cette version. Une réussite sur deux coordonnées ne suffit pas à annoncer une reconnaissance d’animaux.

Les essais MLP annoncés dans le rapport

  • 128 → 64 · 1 000 · α 0,00131 %
  • 80 → 80 · 1 000 · α 0,00133 %
  • 128 → 64 · 1 000 · α 0,0138 %
  • 50 → 50 · 2 000 000 · α 0,0000944 %

Couches cachées · nombre de mises à jour · taux d’apprentissage. Résultats rapportés, avec plusieurs réglages modifiés entre essais ; pas de classement à conditions identiques.

Relier ces résultats à la frontière du MLP
● Classe A · ■ Classe B

64 / 64 exemples correctement classés · erreur quadratique / 2 : 0,001.

Les activations cachées permettent une séparation courbe. Cet essai réussit sur ces points ; cela ne démontre pas la même réussite sur des photos.

Conditions de cette expérience 2D

Mesures recalculées avec le moteur pédagogique de l’atelier. 64 points déterministes, graine 1701, 1 000 passes, α = 0,025, huit neurones cachés ou centres. Pour le RBF, centres fixés par 12 itérations K-means, γ = 4, résolution régularisée à 0,0001 : les appels suivants ne réentraînent pas ses centres. Mesure sur l’apprentissage, une seule initialisation. Les fonds représentent la classe prédite ; les formes indiquent les étiquettes.

Les adaptations linéaire et RBF diffèrent du code Rust sur l’apprentissage et l’activation, comme expliqué dans les chapitres précédents.

Relier les sorties à une photo

Chèvre du dataset
Étiquette : Chèvre
  • Vache-0,359
  • Chèvre-0,179
  • Mouton-0,487

Réponse : Chèvre. Le plus grand score correspond à l’étiquette. Ces scores sont calculés avec la sauvegarde, sans réentraînement ; ils ne sont pas des probabilités.

Les couches cachées transforment les mêmes pixels avant les trois scores. Une réponse différente du linéaire montre l’effet de cette transformation, sans prouver que le réseau a reconnu une corne ou de la laine.

MLP · diagnostic de la sauvegarde

Lignes : espèce du dossier · colonnes : prédiction
Réel ↓ / Prédit →VacheChèvreMouton
Vache254103143
Chèvre122207171
Mouton294189317

43,2 % de réponses conformes aux étiquettes sur 1 800 images. Rappel mouton : 39,6 %.

La sauvegarde répartit davantage ses réponses entre les trois espèces. Elle retrouve plus de moutons que le linéaire, mais moins de vaches : le compromis change, pas seulement le pourcentage global.

Diagnostic sur des dossiers qui recoupent les données d’origine ; ce pourcentage n’est pas une performance sur des images inédites.

3 / 3 · Interpréter les expériences

Une frontière plus flexible ; des images encore difficiles

Le MLP peut combiner plusieurs séparations pour traiter XOR. Sur les animaux, le rapport annonce 31 %, 33 %, 38 %, puis 44 % selon les configurations. Le meilleur essai cité utilise deux couches de 50 neurones, deux millions de mises à jour et un taux d’apprentissage de 0,00009. Ce sont des mises à jour sur un exemple tiré au hasard, pas deux millions de passages complets du dataset.

Pourquoi rester à 44 % malgré cette capacité ?

La capacité mathématique ne remplace ni les détails de l’image ni un apprentissage bien réglé. Le rapport avance la faible résolution, le manque de diversité et les hyperparamètres comme hypothèses. Plusieurs réglages changent entre les essais : on ne peut pas attribuer le gain aux seules couches de 50 neurones. La sauvegarde disponible pour l’atelier porte un taux de 0,0001 ; elle n’est donc pas présentée comme le checkpoint exact de l’essai à 44 %.

Comparer les mêmes images, lire chaque espèce

Linéaire · diagnostic de la sauvegarde

Lignes : espèce du dossier · colonnes : prédiction
Réel ↓ / Prédit →VacheChèvreMouton
Vache4315712
Chèvre27417749
Mouton54017684

38,4 % de réponses conformes aux étiquettes sur 1 800 images. Rappel mouton : 10,5 %.

La sauvegarde choisit souvent vache. Elle retrouve une grande partie des vaches, mais manque beaucoup de moutons. Le score global cache ce déséquilibre.

Diagnostic sur des dossiers qui recoupent les données d’origine ; ce pourcentage n’est pas une performance sur des images inédites.

1 800 fichiers : 500 vaches, 500 chèvres, 800 moutons. 1 031 fichiers identiques sont aussi présents dans les dossiers d’origine. Toujours prédire mouton donnerait 44,4 % sur cette sélection déséquilibrée.

Le MLP doit donc être jugé aussi par ses rappels par espèce, et non uniquement par son score global.

Télécharger les mesures et leur protocole

3 / 3 · Interpréter les expériences

Une matrice de confusion explique plus qu’un pourcentage

Chaque ligne correspond à l’espèce du dossier ; chaque colonne à la réponse du modèle. La diagonale compte les bonnes réponses. Une ligne mouton dispersée entre chèvre et vache révèle les confusions que le score global masque. Comparez les deux sauvegardes sur exactement les mêmes images.

Pourquoi distinguer diagnostic et généralisation ?

Les dossiers dits de test contiennent des fichiers également présents dans le dataset d’origine. Les poids ne conservent pas la liste des images ayant servi à apprendre : leur indépendance ne peut pas être garantie. Ces matrices servent à inspecter les sauvegardes, pas à mesurer leur réussite sur des photos inédites. Le rapport signale aussi un ancien bug d’accuracy : ici, les labels sont relus depuis les dossiers et comparés à un unique argmax par image.

Une expérience à explorer dans ce chapitre.

3 / 3 · Interpréter les expériences

Une même photo, deux modèles, des décisions à comparer

Choisissez une vache, une chèvre ou un mouton du dataset, puis lancez une prédiction avec le modèle linéaire ou le MLP sauvegardé. Les trois sorties sont des scores tanh, pas des probabilités. Regardez quelle classe arrive en tête et si elle correspond à l’étiquette de la photo.

Ce que vous pouvez faire

Gardez la même image et changez de modèle. Essayez ensuite le miroir horizontal : vous reconnaissez toujours l’espèce, mais les pixels changent de position pour le réseau. Comparez les scores, y compris quand la réponse reste identique. Réinitialisez le miroir pour revenir à l’entrée d’origine. L’autre onglet conserve les expériences 2D : testez une droite, XOR, puis des cercles avec le linéaire, le RBF et le MLP.