Des nombres plus petits, un modèle plus petit
Quantifier un modèle, c’est garder chacun de ses nombres sur moins de bits, pour que le modèle prenne moins de mémoire. Le prix à payer : chaque nombre est arrondi.
C’est important, car l’arrondi sert à faire entrer un gros modèle sur une petite machine. Il faut savoir ce que cela coûte avant de se fier au résultat.
À la fin de cette leçon
Remarque
Je peux dire ce que l’on perd quand les nombres rétrécissent.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Quantifier | arrondir des nombres sur quelques paliers fixes |
| Niveau | une des valeurs sur lesquelles un nombre est arrondi |
| Échelle | la plus grande valeur absolue du tableau |
| Erreur | la distance entre un nombre et sa copie arrondie |
| Valeur aberrante | une valeur bien plus grande que les autres |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Moins de bits, moins de niveaux
Sur 8 bits, un nombre peut se poser sur 255 niveaux. Sur 4 bits, il n’y en a que 15, et sur 2 bits seulement 3. Chaque nombre est déplacé vers le niveau le plus proche.
Pour choisir les niveaux, divisez chaque valeur par l’échelle (la plus grande valeur absolue), multipliez par le niveau le plus haut, puis arrondissez. Pour retrouver le nombre, refaites les étapes à l’envers.
Huit nombres sur quatre précisions
| Bits | Niveaux | Entiers gardés | Pire erreur |
|---|---|---|---|
| 8 | 255 | 113 -49 8 78 -127 40 -6 99 | 0,0035 |
| 4 | 15 | 6 -3 0 4 -7 2 0 5 | 0,06 |
| 3 | 7 | 3 -1 0 2 -3 1 0 2 | 0,1 |
| 2 | 3 | 1 0 0 1 -1 0 0 1 | 0,35 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Là où l’arrondi fait le plus mal
Les petits nombres souffrent d’abord. À 4 bits, la valeur 0,06 est devenue 0 : elle est perdue. À 2 bits, quatre des huit nombres sont devenus 0.
Une seule valeur aberrante gêne tout le monde. Si une valeur vaut 9,0, l’échelle vaut 9,0 et les niveaux sont très espacés. Tous les petits nombres s’arrondissent alors à 0.
Une erreur courante
Attention
Beaucoup pensent que moins de bits rend tout un peu flou, de façon égale. Ce n’est pas le cas. Les petits nombres peuvent disparaître, et une valeur aberrante peut gâter tout le tableau. Mesurez toujours l’erreur avant de vous fier à un tableau quantifié.
Écrire l’assistant du modèle, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Les jetons d’une réponse, ou null si elle n’en donne pas.' 'function compter(donnees, nom) {' ' return donnees.usage && typeof donnees.usage[nom] === \"number\" ? donnees.usage[nom] : null;' '}' > model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire l’assistant du modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function demander(messages, echantillon, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"statut \" + res.status);' ' const d = await res.json();' ' return { texte: d.choices[0].message.content, entree: compter(d, \"prompt_tokens\"), sortie: compter(d, \"completion_tokens\"), enregistre: false };' ' } catch (erreur) {' ' console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' ' return { texte: echantillon, entree: null, sortie: null, enregistre: true };' ' }' '}' 'module.exports = { demander };' >> model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le quantificateur, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const valeurs = [0.8, -0.35, 0.06, 0.55, -0.9, 0.28, -0.04, 0.7];' 'function quantifier(liste, bits) {' ' const haut = Math.pow(2, bits - 1) - 1;' ' const echelle = Math.max(...liste.map(Math.abs));' ' return liste.map(function (v) { return Math.floor(v / echelle * haut + 0.5); });' '}' > quant.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le quantificateur, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function restaurer(q, liste, bits) {' ' const haut = Math.pow(2, bits - 1) - 1;' ' const echelle = Math.max(...liste.map(Math.abs));' ' return q.map(function (n) { return n * echelle / haut; });' '}' >> quant.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le quantificateur, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function pire(liste, bits) {' ' const retour = restaurer(quantifier(liste, bits), liste, bits);' ' let p = 0;' ' liste.forEach(function (v, i) { p = Math.max(p, Math.abs(v - retour[i])); });' ' return p;' '}' >> quant.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le quantificateur, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' for (const bits of [8, 4, 3, 2]) {' ' console.log(bits + \" bits : \" + quantifier(valeurs, bits).join(\" \") + \" | pire erreur \" + pire(valeurs, bits).toFixed(4));' ' }' ' console.log(\"avec un 9.0 en plus, 4 bits, pire erreur \" + pire(valeurs.concat([9.0]), 4).toFixed(4));' ' const q = \"En une phrase courte, que perd-on quand les nombres sont gardés sur moins de bits ?\";' ' const echantillon = \"Chaque nombre est arrondi à un palier proche : les petites différences entre les nombres se perdent.\";' ' const reponse = await demander([{ role: \"user\", content: q }], echantillon, 60);' ' console.log(\"le modèle dit : \" + reponse.texte);' '}' 'main();' >> quant.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer le quantificateur
docker exec -w /home/node/project agent node quant.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ce que le Run affiche
| Ligne | Ce qu’elle montre |
|---|---|
| 8 bits : ... pire erreur 0.0035 | Presque rien n’est perdu |
| 4 bits : 6 -3 0 4 -7 2 0 5 ... | Le 0.06 et le -0.04 sont devenus 0 |
| 2 bits : ... pire erreur 0.3500 | La plupart des petites valeurs ont disparu |
| avec un 9.0 en plus ... | Une valeur aberrante rend l’erreur grande |
| le modèle dit : ... | Une phrase. Les mots changent à chaque Run |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.