2026-08-03

28,9M de paramètres, 512Ko de RAM : un LLM sur un microcontrôleur à 8$

AIHardwareOpen Source🌍 Global

Faire tourner un modèle de langage suppose en général un GPU doté de dizaines, voire de centaines de gigaoctets de mémoire. Ce projet en fait tourner un sur un ESP32-S3 — un microcontrôleur à 8$ avec 512Ko de SRAM, 8Mo de PSRAM et 16Mo de flash — entièrement sur la puce, sans aucune connexion réseau, en diffusant le texte généré sur un petit écran connecté à environ 9,5 tokens par seconde.

Les petits modèles de langage sur microcontrôleur ne sont pas nouveaux, mais le précédent record tournait autour de 260 000 paramètres. Celui-ci en compte 28,9 millions — environ 100 fois plus — ce qui pose un problème évident : aucun des niveaux de mémoire de l'ESP32-S3 n'est ne serait-ce que proche de la taille nécessaire pour stocker un modèle de cette taille, sans même parler de le faire tourner.

D'abord, ce qu'est vraiment un « embedding »

Avant de comprendre l'astuce, il faut voir ce qu'elle remplace. Un modèle de langage ne travaille pas avec des mots — il travaille avec des nombres. La toute première chose qui arrive à une entrée, c'est une recherche : le modèle garde une table géante avec une ligne par token possible (le vocabulaire de ce modèle en compte 32 768), et chaque ligne est une courte liste de nombres — l'« embedding » du token — qui le représente partout ailleurs dans le réseau. C'est un dictionnaire, en somme : un token entre, une ligne sort, les 32 767 autres lignes sont ignorées.

Le problème, c'est où ce dictionnaire doit vivre. Dans un transformer classique, la table d'embeddings est une matrice de poids comme les autres, et les poids d'un réseau de neurones sont censés rester en mémoire rapide pendant toute la durée de vie du modèle, car en principe n'importe lequel pourrait compter à n'importe quelle étape. La table d'un petit modèle peut ne faire que quelques mégaoctets, mais elle grandit avec la taille du vocabulaire, et ça s'accumule vite : le vocabulaire de 256 000 tokens de Gemma 3n représente à lui seul plus d'un gigaoctet, entièrement chargé avant même que le modèle n'ait effectué la moindre étape de raisonnement — le blog développeurs de Google utilise exactement ce cadrage pour introduire le problème que PLE résout.

La solution : donner à chaque couche sa propre petite table, et la laisser vivre ailleurs, plus lentement

Les Per-Layer Embeddings (PLE) sont le changement architectural introduit par Google avec la famille Gemma 3n, présentée en avant-première à Google I/O et pleinement disponible en juin 2025. L'idée comporte deux volets, et c'est le second qu'on a tendance à survoler.

D'abord : plutôt qu'un seul embedding par token, recherché une fois à l'entrée puis transporté sans changement à travers toutes les couches, PLE donne à chaque couche du transformer sa propre petite table d'embeddings, séparée, pour ce même vocabulaire. Un token n'a donc pas droit à une seule recherche — il en a une par couche, et chaque couche mélange son propre petit vecteur dans le calcul au fil du passage du token, plutôt que de s'appuyer uniquement sur l'embedding d'entrée initial pour porter tout le sens.

Ensuite, et c'est la partie qui économise vraiment de la mémoire : comme chaque table par couche n'est jamais touchée que par une simple recherche de ligne — jamais multipliée, jamais intégrée à un calcul matriciel — elle n'a pas besoin de vivre dans la même mémoire rapide que les poids qui, eux, sont réellement calculés. Une analyse technique indépendante de Gemma 3n donne un chiffre : pour le modèle E2B de Gemma 3n, chacune des 30 couches reçoit un vecteur de 256 nombres par token, soit 30 × 256 = 7 680 valeurs — ce qui, quantifié en 4 bits, tient en un peu moins de 4Ko par token. À comparer au chargement d'une table complète de plus d'1Go, et on comprend pourquoi Google indique que les modèles E2B et E4B, malgré 5 et 8 milliards de paramètres bruts, tournent avec une empreinte mémoire proche d'un modèle de 2 et 4 milliards — le reste des paramètres, ce sont des embeddings qui sont diffusés quelques kilooctets à la fois plutôt que conservés en permanence dans la mémoire de l'accélérateur.

Pourquoi cela colle si bien à un microcontrôleur

Un ESP32-S3 n'a pas de séparation CPU/accélérateur comme un smartphone — mais il a exactement la séparation autour de laquelle PLE est conçu : une petite réserve de mémoire rapide (SRAM) taillée pour le calcul, et une réserve bien plus grande et bien plus lente (la flash), très bien adaptée à des lectures qui n'ont pas besoin d'être rapides. Ce projet applique la même idée à une échelle bien plus réduite : 6 couches au lieu de 30, un vecteur par couche de 128 nombres au lieu de 256, et un vocabulaire de 32 768 tokens. Cela représente environ 25M des 28,9M de paramètres du modèle vivant sous forme de table de correspondance en flash, tandis que seul le cœur de calcul reste en mémoire rapide :

Diagramme comparant un embedding de token traditionnel, lu une seule fois à l'entrée et nécessitant que la table complète tienne en RAM, aux Per-Layer Embeddings, qui lisent une petite tranche de la table depuis la flash à chacune des six couches du transformer
NiveauCapacitéContient
SRAM512KoLe cœur de calcul — actif à chaque token
PSRAM8MoTête de sortie et mémoire de travail
Flash16MoLa table d'embeddings par couche, d'environ 25M de paramètres

Au moment de l'inférence, générer un seul token signifie lire 6 lignes dans cette table en flash — une par couche — pour un total d'environ 450 octets, plutôt que quoi que ce soit qui s'approche de la structure complète de 25M de paramètres. L'auteur le formule sans détour : appliquer des « Per-Layer Embeddings à la Gemma à une hiérarchie SRAM/flash de microcontrôleur », et, à sa connaissance, la première fois que cette idée précise est adaptée à du matériel aussi contraint. C'est toute la raison pour laquelle un modèle 100 fois plus grand tient là où un modèle à 260 000 paramètres constituait auparavant le plafond — pas une astuce de compression plus maligne, mais le fait de déplacer 87% du nombre de paramètres vers un endroit qui n'a jamais eu besoin d'être rapide.

Ce qui tient réellement sur la puce

Après quantization en 4 bits, le modèle complet fait 14,9Mo, ce qui tient confortablement dans les 16Mo de flash de l'ESP32-S3 aux côtés du firmware. La génération tourne à environ 9,5 tokens/seconde de bout en bout (9,7 tok/s de calcul pur, selon les mesures du dépôt), avec une sortie écrite directement sur l'écran connecté — aucun aller-retour vers le cloud nulle part dans la boucle.

Le modèle est entraîné sur le jeu de données TinyStories de Microsoft, ce qui explique exactement ce que ça donne : de courtes histoires simples, à la structure raisonnablement cohérente. Il ne répond pas à des questions, ne suit pas d'instructions, n'écrit pas de code et ne connaît pas de faits — ce n'était pas l'objectif d'entraînement, et rien dans cette architecture ne visait une capacité générale. L'idée était de prouver qu'un modèle deux ordres de grandeur plus grand que le précédent record sur microcontrôleur pouvait tourner, de façon cohérente, sur du matériel aussi restreint.

Ce que contient le dépôt

Le dépôt GitHub fournit le pipeline complet plutôt qu'un simple binaire : le firmware ESP32 et les instructions de câblage, le code d'entraînement utilisé pour produire le modèle, le pipeline de quantization qui le ramène à 14,9Mo, et un compte-rendu de résultats couvrant les ablations et les mesures effectuées sur la puce derrière les chiffres ci-dessus. Il est sous licence MIT, donc l'ensemble — entraînement, quantization et firmware — est disponible pour être adapté à d'autres expérimentations sur matériel contraint.

Liens