Entraînement et inférence
- Leçon 21
- 50 min
- Module 7 : Utiliser des modèles préentraînés
Leçon 21 sur 23 · L'apprentissage profond et les transformeurs expliquésEntraînement et inférence
1. Ce qu'est un réseau de neurones
2. Les tenseurs et PyTorch
3. Comment les réseaux de neurones apprennent
4. Diagnostiquer les problèmes d'entraînement
5. Représenter les séquences et le sens
6. Assembler un transformeur
7. Utiliser des modèles préentraînés
8. Sous le capot : les mathématiquesFacultatif
Ce que vous saurez faire
Utilisez un modèle de langage entraîné par quelqu’un d’autre. Vous chargez le petit modèle ouvert SmolLM2-135M et examinez ses parties à l’aide du bloc de transformeur du module 6. Ensuite, vous le regardez écrire un jeton à la fois. Vous pourrez alors expliquer ce que l’entraînement change et ce que l’inférence utilise seulement.
Carnet JupyterFaites cette leçon dans un carnet Jupyter
Le carnet (notebook) Jupyter charge SmolLM2-135M (environ 270 Mo à télécharger), affiche son architecture et ses formes. Il exécute la boucle de génération à la main et avec generate(). Il demande aussi au modèle d’acheminer 100 billets de soutien de validation. Environ 30 minutes; chaque cellule s’exécute en quelques secondes sur un processeur.
Le carnet est en anglais. Colab et Kaggle ouvrent votre propre copie. Il faut un compte Google ou Kaggle gratuit. Vous êtes bloqué ? Consultez le carnet de solution.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Le bloc de transformeur (attention, addition et normalisation, propagation avant, addition et normalisation), dans Les blocs du transformeur.
- Le masque causal, qui permet à chaque jeton de voir seulement les jetons précédents, dans Position et masquage.
- La prédiction du prochain jeton et pourquoi le préentraînement à grande échelle est hors de portée de Larkfield, dans Les objectifs d’entraînement.
model.eval()ettorch.no_grad(), dans Lots, époques et validation.- Facultatif : choix glouton, échantillonnage et température, dans Prédire la suite du cours Les LLM : comment ils fonctionnent et comment en choisir un.
Ce qu’il vous faut sur votre ordinateur
- Rien si vous utilisez le carnet.
- Pour travailler localement : l’environnement virtuel de Installer PyTorch, puis
pip install "transformers>=5,<6". Le modèle télécharge environ 270 Mo à la première utilisation et nécessite environ 600 Mo de mémoire.
Testé avec Python 3.12, PyTorch 2.14.1 et transformers 5.19.0 sur le processeur d’un Mac mini. Nous avons exécuté le carnet dans Jupyter sur macOS, pas dans Colab ou Kaggle.
Une nouvelle collègue qui a étudié pendant des années
Section intitulée « Une nouvelle collègue qui a étudié pendant des années »Larkfield embauche une nouvelle agente de soutien. Elle n’a pas appris à lire et à écrire en anglais chez Larkfield : elle a passé des années à l’école. Le premier jour, Grace lui présente brièvement les cinq équipes et lui montre quelques billets. Ensuite, elle lit chaque billet et répond. Elle utilise ses connaissances; répondre à un billet ne la change pas beaucoup.
Un modèle de langage préentraîné passe par les mêmes trois étapes : une scolarité longue et coûteuse, une courte présentation du travail, puis le travail quotidien.
Le problème : 4 700 billets ne suffisent pas pour apprendre une langue
Section intitulée « Le problème : 4 700 billets ne suffisent pas pour apprendre une langue »Dans le module 6, nous avons entraîné un tout petit transformeur à partir de zéro sur les billets de Larkfield. Il a atteint 94,2 % sur les billets de validation. C’est moins que le réseau à sac de mots (98,3 %) et le modèle TF-IDF du cours Apprentissage automatique : du problème à un modèle fiable (98,8 %). Il devait apprendre l’anglais et l’acheminement en même temps, à partir de 4 696 billets courts. Ces billets contiennent environ 280 000 jetons dans le vocabulaire de SmolLM2.
SmolLM2-135M, le modèle de ce module, a été préentraîné sur 2 000 milliards de jetons. C’est environ 7 millions de fois plus de texte, traité sur 64 grands processeurs graphiques (fiche du modèle). Larkfield ne peut pas faire cela, comme l’a montré Les objectifs d’entraînement. Mais Larkfield n’en a pas besoin : les créateurs ont publié les poids entraînés sous la licence Apache-2.0.
L’idée : préentraînement, adaptation, inférence
Section intitulée « L’idée : préentraînement, adaptation, inférence »La vie d’un modèle préentraîné comporte trois étapes. Le préentraînement et le réglage fin sont de l’entraînement : ils changent les poids.
Description textuelle du diagramme
Trois cases de gauche à droite, reliées par des flèches.
- Préentraînement. Le modèle lit 2 000 milliards de jetons de texte public et apprend à prédire le prochain jeton. Les 134,5 millions de poids changent. Cela a nécessité 64 grands processeurs graphiques, selon la fiche du modèle. Les créateurs du modèle s’en chargent.
- Adaptation, avec deux options. Le réglage fin ajoute de l’entraînement sur vos propres exemples : les poids changent un peu, à un coût bien moindre que le préentraînement. L’utilisation de requêtes place des instructions et des exemples dans le texte : les poids ne changent pas, et l’entrée s’allonge.
- Inférence. Le modèle lit un billet à la fois et effectue des propagations avant, un jeton après l’autre. Les poids sont figés. Chaque requête entraîne un coût, aussi longtemps que vous utilisez le modèle.
Le préentraînement et le réglage fin sont de l’entraînement : les poids changent. L’utilisation de requêtes et l’inférence laissent les poids tels quels.
Le préentraînement est la longue scolarité : prédire le prochain jeton sur une énorme quantité de texte. Le modèle apprend ainsi des mots, la grammaire et des faits. L’adaptation est la présentation d’un travail précis. Le réglage fin entraîne un peu plus les poids préentraînés sur des exemples étiquetés. L’utilisation de requêtes change seulement l’entrée. L’inférence est le travail quotidien : le mot vient d’« inférer », tirer une conclusion de ce qu’on sait déjà. À l’inférence, le réseau effectue seulement des propagations avant, en mode évaluation, sans gradients.
L’inférence n’est pas gratuite simplement parce que les poids sont fixes. L’entraînement a lieu une fois; l’inférence a lieu pour chaque billet, chaque jour. Les coûts de l’inférence mesure ce coût.
Découvrez SmolLM2-135M
Section intitulée « Découvrez SmolLM2-135M »Amira charge le modèle avec transformers de Hugging Face. Cette bibliothèque télécharge des modèles publiés et les construit sous forme de réseaux PyTorch. from_pretrained() télécharge les poids une fois et garde une copie sur le disque.
import torchfrom transformers import AutoModelForCausalLM, AutoTokenizer
MODEL = "HuggingFaceTB/SmolLM2-135M"REVISION = "93efa2f097d58c2a74874c7e644dbc9b0cee75a2" # the exact version of the files that the course usedtokenizer = AutoTokenizer.from_pretrained(MODEL, revision=REVISION)model = AutoModelForCausalLM.from_pretrained(MODEL, revision=REVISION, dtype=torch.float32)model.eval() # inference: switch off training-only behaviourPROMPT = ("Larkfield is an online shop for home and garden products. " "A customer wrote: my parcel is late. The support team")print("parameters:", sum(p.numel() for p in model.parameters()))La sortie :
parameters: 134515008AutoTokenizer charge le segmenteur en jetons du modèle. SmolLM2 découpe le texte en morceaux de mots tirés d’un vocabulaire de 49 152 jetons, plutôt qu’en mots minuscules comme dans le cours. AutoModelForCausalLM construit un modèle de langage causal, un décodeur qui prédit le prochain jeton avec un masque causal. revision= fixe une version des fichiers. Ainsi, le modèle reste le même, même si ses créateurs le mettent à jour. La dernière leçon de ce module explique pourquoi c’est important.
Lisez l’architecture à l’aide du schéma du bloc
Section intitulée « Lisez l’architecture à l’aide du schéma du bloc »print(model) énumère chaque partie dans l’ordre :
print(model)La sortie :
LlamaForCausalLM( (model): LlamaModel( (embed_tokens): Embedding(49152, 576) (layers): ModuleList( (0-29): 30 x LlamaDecoderLayer( (self_attn): LlamaAttention( (q_proj): Linear(in_features=576, out_features=576, bias=False) (k_proj): Linear(in_features=576, out_features=192, bias=False) (v_proj): Linear(in_features=576, out_features=192, bias=False) (o_proj): Linear(in_features=576, out_features=576, bias=False) ) (mlp): LlamaMLP( (gate_proj): Linear(in_features=576, out_features=1536, bias=False) (up_proj): Linear(in_features=576, out_features=1536, bias=False) (down_proj): Linear(in_features=1536, out_features=576, bias=False) (act_fn): SiLUActivation() ) (input_layernorm): LlamaRMSNorm((576,), eps=1e-05) (post_attention_layernorm): LlamaRMSNorm((576,), eps=1e-05) ) ) (norm): LlamaRMSNorm((576,), eps=1e-05) (rotary_emb): LlamaRotaryEmbedding() ) (lm_head): Linear(in_features=576, out_features=49152, bias=False))La liste semble longue, mais vous connaissez chaque partie. Le schéma les place dans l’ordre où une requête (prompt) de 25 jetons les traverse, avec la forme après chacune.
Description textuelle du diagramme
De haut en bas : les identifiants des jetons ont la forme (1, 25), soit un texte de 25 jetons. Le plongement (embedding) embed_tokens est une table de 49 152 lignes de 576 nombres; il donne la forme (1, 25, 576).
Vient ensuite un bloc, répété 30 fois. Il contient une normalisation de couche (input_layernorm, une RMSNorm), puis l’autoattention. Celle-ci comporte 9 têtes de requête et 3 têtes clé-valeur de 64 nombres chacune. Ensuite viennent une addition de l’entrée et du changement, puis une deuxième normalisation. Puis la couche à propagation avant va de 576 à 1 536 et revient à 576, avec l’activation SiLU. Une deuxième addition suit. La forme est (1, 25, 576) à l’entrée et reste la même à la sortie.
Après les 30 blocs vient une normalisation finale, toujours (1, 25, 576). Puis lm_head, une couche linéaire de 576 à 49 152, utilise la même matrice que embed_tokens. Elle donne les logits (scores avant normalisation), de forme (1, 25, 49 152). La dernière ligne passe par softmax pour donner le prochain jeton : « is » (est), avec 0,107.
Paramètres, comptés avec PyTorch : le plongement 49 152 × 576 = 28 311 552. Un bloc 3 540 096 : attention 884 736, propagation avant 2 654 208 et normalisations 1 152. Les 30 blocs ensemble 106 202 880. La normalisation finale 576. lm_head n’ajoute rien de nouveau, car il est lié au plongement. Total 134 515 008.
Lisez-le à partir du haut, comme le schéma du bloc dans Les blocs du transformeur :
embed_tokensest la table de plongements de Jetons et plongements : une ligne de 576 nombres pour chacun des 49 152 jetons.LlamaDecoderLayer× 30 est le bloc de transformeur, répété 30 fois. (« Llama » est la famille de modèles dont SmolLM2 reprend la conception.)self_attnest l’attention,mlpla couche à propagation avant, et les deux couchesLlamaRMSNormsont les normalisations. RMSNorm est une normalisation de couche plus simple : elle remet à l’échelle les nombres de chaque jeton sans soustraire leur moyenne. Les additions des connexions résiduelles sont dans le code du bloc;printne peut donc pas les afficher.- SmolLM2 normalise avant l’attention et avant la couche à propagation avant, pas après. Le tout petit transformeur du module 6 fait pareil (
norm_first=True); cela stabilise les empilements profonds. mlppossède trois matrices plutôt que deux.gate_projpasse par SiLU, une variante lisse de ReLU, puis multiplie la sortie deup_proj, nombre par nombre.down_projramène les 1 536 nombres à 576.rotary_embajoute les positions : des plongements positionnels rotatifs, mentionnés dans Position et masquage. Il fait tourner les requêtes et les clés selon un angle qui dépend de la position. Il n’a donc aucun poids à apprendre.lm_headtransforme les 576 nombres de chaque jeton en 49 152 scores, un par jeton du vocabulaire.
Deux détails sont nouveaux. D’abord, regardez les formes de k_proj et v_proj : 576 → 192, tandis que q_proj donne 576. Le modèle possède 9 têtes de requête de 64 nombres (9 × 64 = 576), mais seulement 3 têtes clé-valeur (3 × 64 = 192). C’est l’attention à requêtes groupées : chaque tête clé-valeur est partagée par un groupe de 3 têtes de requête. C’est comme 9 personnes qui écoutent et partagent 3 ensembles de notes. Cela économise des paramètres, et surtout de la mémoire pendant la génération, comme le montre la prochaine leçon.
Ensuite, lm_head utilise la même matrice que embed_tokens : ce sont des plongements liés. La table transforme un jeton en 576 nombres, puis transforme aussi 576 nombres en un score pour chaque jeton. PyTorch la stocke une seule fois. Le modèle possède donc 134,5 millions de paramètres, et non 162,8 millions.
Les jetons, les logits et le prochain jeton
Section intitulée « Les jetons, les logits et le prochain jeton »Faisons maintenant une propagation avant, en affichant les formes à chaque étape, comme d’habitude dans ce cours. show_shape() est une petite fonction utilitaire qui affiche un nom et une forme; les carnets de ce module l’utilisent aussi.
def show_shape(name, t): print(f"{name:<12} shape {tuple(t.shape)}")
inputs = tokenizer(PROMPT, return_tensors="pt")print(tokenizer.convert_ids_to_tokens(inputs["input_ids"][0]))show_shape("input_ids", inputs["input_ids"])with torch.no_grad(): # inference: no record for gradients logits = model(**inputs).logitsshow_shape("logits", logits)La sortie :
['L', 'ark', 'field', 'Ġis', 'Ġan', 'Ġonline', 'Ġshop', 'Ġfor', 'Ġhome', 'Ġand', 'Ġgarden', 'Ġproducts', '.', 'ĠA', 'Ġcustomer', 'Ġwrote', ':', 'Ġmy', 'Ġparcel', 'Ġis', 'Ġlate', '.', 'ĠThe', 'Ġsupport', 'Ġteam']input_ids shape (1, 25)logits shape (1, 25, 49152)PROMPT, dans load_smollm.py, contient « Larkfield is an online shop for home and garden products. A customer wrote : my parcel is late. The support team » (Larkfield est une boutique en ligne de produits pour la maison et le jardin. Un client a écrit : mon colis est en retard. L’équipe de soutien). Le segmenteur en jetons ne connaît pas « Larkfield »; il le découpe donc en L, ark et field. Ġ indique une espace avant un morceau. Les logits ont une ligne de 49 152 scores pour chaque position. Le masque causal permet en effet à chaque position de prédire le jeton suivant. Pour le prochain jeton après toute la requête, seule la dernière ligne compte. Softmax la transforme en probabilités.
Faites une prédiction avant de déplacer le curseur : SmolLM2 donne 10,7 % à « is ». Parmi ses 20 jetons les plus probables, cela représente 18,7 %. Que devient cette part à une température de 0,5?
Explorer
Le prochain jeton de SmolLM2, repondéré par la température
Prédisez d’abord : À une température de 1, « ·is » représente 18,7 % des 20 jetons les plus probables. Que devient cette part à une température de 0,5? Et de 2?
Texte jusqu’ici : Larkfield is an online shop for home and garden products. A customer wrote: my parcel is late. The support team
- Chance du jeton le plus probable
- 18,7 %
- Chance d’un autre jeton
- 81,3 %
- Jetons à 1 % ou plus
- 20
La fine marque foncée sur chaque barre est la part à la température 1, soit les probabilités propres du modèle. La dernière barre regroupe les 15 autres des 20 jetons les plus probables.
À la température 1,0, « ·is » a 18,7 % de chances, donc un autre mot vient dans environ 8 exécutions sur 10. À 1, ce sont les probabilités propres du modèle.
Description textuelle de l’explorateur
L’outil d’exploration affiche le texte « Larkfield is an online shop for home and garden products. A customer wrote : my parcel is late. The support team ». En dessous, des barres représentent les cinq prochains jetons les plus probables. Une autre barre représente les 15 autres parmi les 20 jetons les plus probables. Un curseur règle la température, de 0 à 2 par pas de 0,1.
À une température de 1 : is 18,7 %, has 15,6 %, will 8,2 %, at 6,1 %, said 5,9 %, les 15 autres ensemble 45,6 %; les 20 jetons ont tous au moins 1 %. À 0,5 : is 39,0 %, has 27,1 %, will 7,6 %, at 4,1 %, said 3,9 %, les 15 autres ensemble 18,2 %; 11 jetons ont au moins 1 %. À 0,3 : is 58,2 %, has 31,8 %. À 2 : is 10,4 %, has 9,5 %, will 6,9 %. À 0, is a 100 % : le choix est glouton.
SmolLM2 est bien moins certain ici qu’un grand modèle ne l’est pour « a refund » (un remboursement) dans le cours Les LLM : comment ils fonctionnent et comment en choisir un. Son meilleur jeton n’a que 10,7 %, et ses 20 jetons les plus probables ne représentent ensemble que 57,3 %. Plusieurs suites sont correctes en anglais : « is », « has » (a), « will » (marque le futur), « replied » (a répondu). Prédire la suite explique en détail la température et l’échantillonnage; cette leçon examine la boucle sous-jacente.
La boucle de génération, un jeton à la fois
Section intitulée « La boucle de génération, un jeton à la fois »La génération répète une étape : exécuter le modèle, choisir un jeton, l’ajouter au texte, puis recommencer. Priya écrit la boucle à la main, avec un choix glouton (toujours le jeton le plus probable). Elle exécute le modèle sur le texte entier à chaque étape :
ids = inputs["input_ids"]chosen = []with torch.no_grad(): for step in range(10): logits = model(input_ids=ids).logits[0, -1] # scores for the next token next_id = int(logits.argmax()) # greedy: the most likely token chosen.append(next_id) ids = torch.cat([ids, torch.tensor([[next_id]])], dim=1)logits[0, -1] prend la dernière ligne, et argmax() la position du score le plus élevé. torch.cat(..., dim=1) ajoute le nouveau jeton au texte. Le tableau montre chaque étape, avec les trois jetons les plus probables :
| Étape | Jetons d’entrée | Trois prochains jetons les plus probables | Choisi |
|---|---|---|---|
| 1 | 25 | ·is 10.7%, ·has 8.9%, ·will 4.7% | ·is |
| 2 | 26 | ·very 4.8%, ·not 3.3%, ·busy 3.3% | ·very |
| 3 | 27 | ·helpful 47.9%, ·good 5.1%, ·busy 4.7% | ·helpful |
| 4 | 28 | . 36.5%, ·and 23.6%, , 15.0% | . |
| 5 | 29 | ·I 18.2%, (new line) 12.9%, ·The 5.3% | ·I |
| 6 | 30 | ·am 13.4%, ·have 11.7%, ·would 7.5% | ·am |
| 7 | 31 | ·looking 9.3%, ·a 5.7%, ·very 4.3% | ·looking |
| 8 | 32 | ·for 67.0%, ·forward 13.7%, ·to 11.1% | ·for |
| 9 | 33 | ·a 38.9%, ·an 6.6%, ·the 4.8% | ·a |
| 10 | 34 | ·new 4.6%, ·good 2.8%, · 2.2% | ·new |
L’entrée s’allonge d’un jeton à chaque étape : 25, 26, 27 … 34. Certaines étapes sont certaines : après « very » (très), « helpful » (serviable) a 47,9 %. Après « looking » (chercher), « for » (pour) a 67,0 %. D’autres restent ouvertes : après « is », « very » n’a que 4,8 %. La fonction generate() de Hugging Face exécute la même boucle :
with torch.no_grad(): out = model.generate(**inputs, max_new_tokens=10, do_sample=False, pad_token_id=tokenizer.eos_token_id)new_ids = out[0, inputs["input_ids"].shape[1]:].tolist()print("generate():", repr(tokenizer.decode(new_ids)))print("same tokens as the loop:", new_ids == chosen)La sortie :
generate(): ' is very helpful. I am looking for a new'same tokens as the loop: Truedo_sample=False demande un choix glouton; pad_token_id désigne le jeton de remplissage, que SmolLM2 ne définit pas. Les jetons sont identiques. generate() est plus rapide, car il ne recalcule pas les jetons précédents à chaque étape. Il les conserve dans un cache KV, le sujet de la prochaine leçon.
Le choix glouton répète; l’échantillonnage varie
Section intitulée « Le choix glouton répète; l’échantillonnage varie »Quarante jetons choisis de façon gloutonne révèlent un problème :
' is very helpful. I am looking for a new parcel. I am looking for a new parcel. I am looking for a new parcel. I am looking for a new parcel. I am looking for'Quand une phrase apparaît deux fois, le texte répété rend cette même phrase encore plus probable. Le choix glouton la sélectionne alors chaque fois. L’échantillonnage tire chaque jeton au hasard, selon les probabilités, et la température les repondère d’abord. Voici trois échantillons à une température de 0,8, avec top_p=0.95 (garder les jetons les plus probables représentant 95 % de la probabilité). Les graines aléatoires sont 1, 2 et 3 :
1 ' is currently at my front door, and I am still waiting for their response.\n\nI am the only one with the'2 " is there to help you out. They've got all the right tips and tools to make it happen, and they know exactly"3 ' is there to help you with the delivery. They are really friendly and supportive. It takes 2-3 hours to get'Aucune répétition, et trois histoires différentes. Une graine aléatoire fixe les tirages aléatoires; la graine 1 redonne donc le même texte sur le même ordinateur. Aucune des trois ne répond au client. C’est le prochain problème.
Pourquoi un modèle de base s’éparpille
Section intitulée « Pourquoi un modèle de base s’éparpille »SmolLM2-135M est un modèle de base : il a seulement été préentraîné. Il a appris à continuer un texte, pas à suivre des instructions. Les assistants, comme les modèles conversationnels, ajoutent un entraînement aux instructions et d’autres entraînements après le préentraînement. Les étapes de l’entraînement, dans le cours Les LLM : comment ils fonctionnent et comment en choisir un, explique ces étapes.
Priya essaie d’adapter le modèle par des requêtes (prompting). Sa requête énumère les cinq équipes, puis un billet de validation, puis Team: (équipe). La bonne équipe est payment. SmolLM2 écrit :
' I am writing regarding order LK-714258. The electric kettle was listed at a specific price on the checkout page, but'Il écrit un autre billet. Priya cesse donc de lire le texte et examine plutôt les probabilités. Lequel des cinq noms d’équipe le modèle juge-t-il le plus probable après Team:? Sur les 100 premiers billets de validation, il donne la bonne réponse pour 44. Avec cinq exemples de billets dans la requête, un par équipe (requête avec quelques exemples, ou few-shot), il réussit 45 fois. Sans exemples, il envoie 73 billets à delivery; avec des exemples, seulement 2. Les exemples ont changé l’équipe qu’il favorise, pas sa capacité à lire.
La règle par mots-clés du module 1 classe correctement 57,3 % des billets de validation. TF-IDF avec une régression logistique atteint 98,8 %. Un modèle de base de 135 millions de paramètres avec une requête n’est pas un outil d’acheminement des billets. Pour rendre un modèle préentraîné efficace sur une tâche, vous faites son réglage fin avec des exemples étiquetés. L’étude de cas Faire le réglage fin d’un petit transformeur mesure ce que cela apporte.
Quand utiliser un modèle préentraîné et à quel coût
Section intitulée « Quand utiliser un modèle préentraîné et à quel coût »Utilisez un modèle préentraîné quand la tâche exige des capacités linguistiques que vos propres données ne peuvent pas lui enseigner. Cela comprend les textes longs ou variés, les étiquettes peu nombreuses ou la génération de texte. Partez du plus petit modèle qui fait le travail et adaptez-le. Essayez d’abord les requêtes, car elles coûtent peu; faites un réglage fin quand elles ne suffisent pas. Comparez-le à une référence de base simple sur les mêmes données de validation, comme dans le test d’acheminement ci-dessus.
N’en utilisez pas quand un modèle simple fait déjà le travail. Pour l’acheminement chez Larkfield, le modèle linéaire du cours Apprentissage automatique : du problème à un modèle fiable est meilleur, plus rapide et moins cher. Un modèle préentraîné consomme de la mémoire et du temps à chaque requête. Sa licence et ses données d’entraînement viennent aussi de quelqu’un d’autre. Les compromis sur les ressources, dans le cours Les LLM : comment ils fonctionnent et comment en choisir un, vous aide à choisir un modèle.
Exercice guidé : lire le modèle
Section intitulée « Exercice guidé : lire le modèle »- Ouvrez le carnet (notebook) Jupyter et exécutez les cellules de préparation. Vous devriez voir
parameters: 134515008(paramètres : 134 515 008). - Exécutez la section 1. Dans l’architecture affichée, trouvez
k_proj. Vous devriez voirout_features=192(192 caractéristiques de sortie). - Calculez à la main les paramètres d’attention d’un bloc à partir des quatre formes, puis exécutez la cellule de vérification. Vous devriez voir
Check passed(vérification réussie). - Exécutez la section 2. Notez la forme des logits avant de la regarder.
- Exécutez la section 3 et comparez les 10 jetons (tokens) de la boucle avec
generate(). Vous devriez voirsame tokens as the loop: True(mêmes jetons que la boucle : vrai).
Afficher la réponse
Attention d’un bloc : q_proj 576 × 576 = 331 776, k_proj 576 × 192 = 110 592, v_proj 576 × 192 = 110 592, o_proj 576 × 576 = 331 776. Ensemble : 884 736. Avec 9 têtes clé-valeur, k_proj et v_proj auraient aussi une forme de 576 × 576. L’attention aurait alors 4 × 331 776 = 1 327 104 paramètres. Les logits ont la forme (1, 25, 49152) : un texte, 25 positions, un score pour chaque jeton du vocabulaire.
À vous de jouer : compter les paramètres du modèle entier
Section intitulée « À vous de jouer : compter les paramètres du modèle entier »Comptez les 134 515 008 paramètres à partir des parties, à la main, avant d’exécuter quoi que ce soit. Il vous faut : le plongement (embedding) (49 152 × 576), un bloc (attention 884 736, propagation avant 3 × 576 × 1 536, deux normalisations de 576), 30 blocs, la normalisation finale (576) et lm_head. Répondez ensuite à Tomás : « Sans plongements liés, combien de paramètres aurait-il? »
Afficher la réponse
Plongement : 49 152 × 576 = 28 311 552. Propagation avant : 3 × 576 × 1 536 = 2 654 208. Un bloc : 884 736 + 2 654 208 + 2 × 576 = 3 540 096. Trente blocs : 106 202 880. Total : 28 311 552 + 106 202 880 + 576 = 134 515 008. lm_head n’ajoute rien, car il utilise la matrice de plongement. Un lm_head distinct ajouterait encore 576 × 49 152 = 28 311 552 : 162 826 560 paramètres, soit 21 % de plus.
Erreur courante
Un modèle de base traité comme un assistant
Symptôme : Tomás saisit « Quelle équipe devrait traiter ce billet? » et un billet dans SmolLM2-135M. Le modèle ne répond pas. Il ajoute du texte au billet ou répète sans cesse une phrase. Tomás conclut que « le modèle est défectueux ».
Cause : SmolLM2-135M est un modèle de base. Le préentraînement lui a appris à continuer un texte, alors il continue le texte qu’il voit. Le choix glouton répète ensuite la boucle la plus probable. Rien dans le préentraînement ne lui a appris à suivre une instruction.
Solution : lisez les probabilités de quelques réponses permises plutôt que du texte libre, comme Priya avec les cinq noms d’équipe. Donnez des exemples dans la requête (few-shot). Mesurez ensuite les résultats sur les billets de validation par rapport à une référence de base : ici, 45 % contre 98,8 %. Si cela ne suffit pas, faites un réglage fin sur des billets étiquetés ou choisissez un modèle entraîné aux instructions.
Ce que cela signifie pour un vrai service d’IA : un modèle de base et un modèle entraîné aux instructions réagissent très différemment à la même requête. Vérifiez lequel vous avez et évaluez-le sur de nombreuses entrées réelles, pas sur un seul essai.
Vérification des connaissances
Section intitulée « Vérification des connaissances »Vérifiez votre compréhension
Lesquels modifient les poids d'un modèle : préentraînement, réglage fin, requêtes, inférence? Expliquez chacun en une phrase.
Afficher la réponse
Le préentraînement modifie tous les poids : le modèle apprend à prédire le prochain jeton sur une immense quantité de texte. Le réglage fin modifie un peu les poids : il ajoute de l’entraînement sur vos propres exemples étiquetés. Les requêtes ne modifient aucun poids : seul le texte d’entrée change. L’inférence ne modifie aucun poids : le modèle effectue seulement des propagations avant, en mode évaluation et sans gradients.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
SmolLM2 a 9 têtes de requête, mais k_proj donne seulement 192 nombres par jeton. Expliquez pourquoi et prédisez la taille de k_proj si chaque tête de requête avait sa propre tête clé-valeur.
Afficher la réponse
SmolLM2 utilise l’attention à requêtes groupées : 3 têtes clé-valeur de 64 nombres (3 × 64 = 192). Chacune est partagée par un groupe de 3 têtes de requête. Avec une tête clé-valeur par tête de requête, k_proj donnerait 9 × 64 = 576 nombres par jeton. Cela ferait 576 × 576 = 331 776 poids au lieu de 576 × 192 = 110 592. Il en va de même pour v_proj.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Prédisez : vous exécutez la boucle gloutonne écrite à la main et generate() avec do_sample=False pour 40 jetons. Les textes seront-ils identiques? Laquelle fait plus de calculs, et pourquoi?
Afficher la réponse
Les textes seront identiques : les deux choisissent le jeton le plus probable à chaque étape, à partir des mêmes probabilités. La boucle écrite à la main fait plus de calculs. Elle exécute le modèle sur tout le texte à chaque étape (25 jetons, puis 26, et ainsi de suite). generate() garde les clés et les valeurs des jetons précédents dans un cache KV. Il calcule seulement le nouveau jeton à chaque étape.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Le préentraînement (énormément de texte, tous les poids changent) et le réglage fin (vos exemples, les poids changent un peu) sont de l’entraînement. Les requêtes et l’inférence ne modifient aucun poids.
- SmolLM2-135M est le transformeur du module 6 : un plongement de 49 152 × 576, puis 30 blocs, puis une couche de sortie liée au plongement. Chaque bloc comprend une attention avec 9 têtes de requête et 3 têtes clé-valeur. Il comprend aussi une couche à propagation avant de 1 536 et RMSNorm avant chaque partie. En tout : 134 515 008 paramètres.
- La génération est une boucle : propagation avant, choix d’un jeton, ajout, répétition. Les logits ont une ligne par position, (1, 25, 49152); seule la dernière ligne sert à choisir le prochain jeton.
- Le choix glouton se répète; l’échantillonnage avec une température apporte de la variété. Un modèle de base continue le texte. Avec une requête, il achemine correctement 44 à 45 billets sur 100, contre 98,8 % pour TF-IDF.
À suivre : le coût de cette boucle en mémoire et en temps, et pourquoi l’entraînement coûte beaucoup plus.
Leçon suivante : Les coûts de l'inférence