Apprendre à deviner la lettre suivante
Un modèle de langage s’entraîne par prédiction du jeton suivant : il devine le prochain jeton, voit le bon, puis ajuste ses nombres pour mieux deviner. Cette seule boucle, répétée sur une énorme quantité de texte, est tout l’entraînement.
Ici, vous entraînez un tout petit modèle sur une courte phrase en quelques secondes, et vous voyez ses devinettes s’améliorer.
À la fin de cette leçon
Remarque
Je peux entraîner un tout petit modèle à prédire le caractère suivant et lire sa perte.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Prédiction du jeton suivant | deviner le jeton qui vient après | |
| Cible | le jeton qui vient vraiment après | a après m |
| Probabilité | la part qu’un modèle donne à un jeton, de 0 à 1 | |
| Perte | la surprise du modèle devant la cible | |
| Pas d’entraînement | une petite poussée sur chaque score | |
| Taux d’apprentissage | la taille de chaque poussée | 2.0 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Chaque place d’un texte est une question
Prenez la phrase « ma mamie aime ma maman ». À chaque place, la question est : que vient-il ensuite ? La réponse, la cible, est le caractère suivant du texte.
22 caractères donnent donc 21 questions, sans aucun étiquetage à la main. Voilà pourquoi un modèle peut apprendre de n’importe quel texte.
Exemple travaillé : les six premières questions
| Place | Ce jeton | Cible |
|---|---|---|
| 0 | m | a |
| 1 | a | espace |
| 2 | espace | m |
| 3 | m | a |
| 4 | a | m |
| 5 | m | i |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Notre modèle regarde un jeton en arrière
Notre tout petit modèle est une table de scores, avec une ligne par jeton et une colonne par jeton suivant possible. Le softmax change une ligne en probabilités. Six caractères font une table de 36 scores.
Ce modèle ne voit que le jeton juste avant. Un transformeur voit tous les jetons d’avant grâce à l’attention masquée. La façon de l’entraîner est la même.
La perte mesure la surprise
La perte d’une question vaut moins le log de la probabilité donnée à la cible. Une bonne réponse sûre ne coûte presque rien. Une petite probabilité sur la cible coûte cher.
Au départ, chaque score vaut 0, donc chacun des 6 caractères reçoit un sixième, environ 0,17. La perte vaut alors 1,79 à chaque question.
Exemple travaillé : probabilité sur la cible et perte
| Probabilité sur la cible | Perte |
|---|---|
| 1,0 | 0 |
| 0,9 | 0,11 |
| 0,5 | 0,69 |
| un sixième, environ 0,17 | 1,79 |
| 0,1 | 2,30 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un pas d’entraînement pousse les scores
Pour chaque question, regardez la ligne de ce jeton. Chaque score y baisse de sa probabilité. Le score de la cible monte en plus de 1.
Additionnez ces poussées sur les 21 questions et divisez par 21. Multipliez par le taux d’apprentissage et appliquez-les. C’est un pas d’entraînement. Répétez-le, et la perte baisse.
Entraîner le petit modèle sur 50 pas
import math
texte = "ma mamie aime ma maman"
car = sorted(set(texte)) # le vocabulaire : espace, a, e, i, m, n
n = len(car)
paires = len(texte) - 1 # 21 paires de ce jeton et du suivant
table = [[0.0] * n for i in range(n)] # chaque score part de 0
taux = 2.0
for pas in range(51):
poussee = [[0.0] * n for i in range(n)]
perte = 0
for k in range(paires):
a = car.index(texte[k]) # ce jeton
b = car.index(texte[k + 1]) # le bon jeton suivant
puiss = [math.exp(s) for s in table[a]]
total = sum(puiss)
perte = perte - math.log(puiss[b] / total)
for j in range(n):
poussee[a][j] += puiss[j] / total - (1 if j == b else 0)
if pas % 10 == 0:
print("pas", pas, "perte", round(perte / paires, 3))
for a in range(n):
for j in range(n):
table[a][j] -= taux * poussee[a][j] / paires
puiss = [math.exp(s) for s in table[car.index("m")]]
print("après m :", [(car[j], round(puiss[j] / sum(puiss), 2)) for j in range(n)])
# Mettez taux à 0.5, puis cliquez sur Exécuter. La perte baisse-t-elle plus vite ou moins vite ?
Exemple travaillé : la perte au fil de l’entraînement
| Pas | 0 | 10 | 20 | 30 | 40 | 50 |
|---|---|---|---|---|---|---|
| Perte | 1,792 | 1,118 | 0,991 | 0,937 | 0,908 | 0,890 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur fréquente
Attention
Beaucoup pensent qu’un modèle garde les phrases lues et va les y chercher. Le nôtre garde 36 scores et aucun texte. Il a appris qu’un m est le plus souvent suivi d’un a. Un modèle garde des régularités en nombres, et c’est aussi pourquoi il peut se tromper avec assurance.
Écrire le script d’entraînement dans le labo
docker exec jupyter sh -c "printf '%s\n' 'import numpy as np' 'texte = \"ma mamie aime ma maman\"' 'car = sorted(set(texte))' 'ids = np.array([car.index(c) for c in texte])' 'x, y = ids[:-1], ids[1:]' 'W = np.random.default_rng(0).normal(0, 0.1, (6, 6))' 'for pas in range(101):' ' p = np.exp(W[x]) / np.exp(W[x]).sum(axis=1, keepdims=True)' ' if pas % 25 == 0:' ' print(\"pas\", pas, \"perte\", round(-np.log(p[np.arange(21), y]).mean(), 3))' ' p[np.arange(21), y] -= 1' ' np.add.at(W, x, -2.0 * p / 21)' 'p = np.exp(W[4]) / np.exp(W[4]).sum()' 'print(\"apres m :\", dict(zip(car, p.round(2).tolist())))' > steps/tiny.py"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.
Entraîner depuis des scores au hasard
docker exec jupyter python3 steps/tiny.pyAffiché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.