advanced · Session 15

Chunking, causalité et préfill parallèle

Réconcilier état récurrent et parallélisme GPU par calcul en blocs causaux.

120 min6 mécanismeslaboratoire causal

Ce que vous saurez faire

Ouvrir le laboratoire

Méthode de travail

Travaillez cette session comme une enquête causale. Avant chaque formule ou interaction, écrivez ce que vous pensez voir changer et ce qui doit rester fixe. Pendant le calcul, conservez les unités, les formes et les valeurs intermédiaires : elles permettent de localiser une erreur sans recommencer au hasard. Après le résultat, traduisez le nombre ou l’état en une phrase sur le comportement du système. Terminez toujours par un contre-exemple ou une valeur limite. Cette discipline sépare la compréhension d’un mécanisme de la simple reconnaissance de son vocabulaire et rend le laboratoire reproductible par un autre apprenant.

Construire le mécanisme pas à pas

1. Préfill et décodage

Le problème : Un prompt de 8 000 tokens arrive d’un coup ; la réponse sort ensuite token par token. Si le moteur traite le prompt au rythme de la génération — un token à la fois — l’utilisateur attend le premier mot pendant des secondes entières.

L’idée : Deux phases, deux régimes : le préfill voit tous les tokens du prompt en même temps (travail massivement parallélisable) ; le décodage n’ajoute qu’un token par pas (travail intrinsèquement séquentiel). Même mécanisme, profils d’exécution opposés.

Pourquoi / à quel prix : Séparer les deux permet d’optimiser chacun — latence du premier token d’un côté, débit de génération de l’autre. Le prix : deux chemins de code pour un seul mécanisme, qui doivent produire exactement les mêmes nombres.

Vérification de compréhension

Nommez l’entrée, l’état transformé, la sortie et une hypothèse nécessaire. Comparez ensuite votre chaîne à l’explication ci-dessus.

2. Récurrence naïve

Le problème : La mémoire récurrente semble condamner le préfill : S₅ exige S₄, qui exige S₃… Exécuter 8 000 mises à jour l’une après l’autre laisse un GPU — conçu pour des matrices entières — presque vide à chaque pas.

L’idée : Le diagnostic précis : la DÉPENDANCE est séquentielle (chaque S_t dépend de S_{t−1}), mais la majorité du CALCUL par token — produits q·k locaux, écritures k vᵀ — ne l’est pas. La récurrence naïve sérialise tout parce qu’elle ne sépare pas les deux.

Pourquoi / à quel prix : Ce constat ouvre la porte du chunking : ne sérialiser que ce qui doit l’être. Le prix d’en rester à la version naïve se mesure directement : des unités matricielles facturées à l’heure qui exécutent des produits vecteur-matrice.

Vérification de compréhension

Nommez l’entrée, l’état transformé, la sortie et une hypothèse nécessaire. Comparez ensuite votre chaîne à l’explication ci-dessus.

3. Découper en chunks

Le problème : Comment donner au GPU des blocs matriciels pleins sans violer l’ordre ? Il faut un découpage où l’intérieur d’un bloc se calcule en parallèle et où le passé lointain arrive compressé — sans double compte ni oubli.

L’idée : Un chunk de C tokens calcule d’un coup ses interactions internes autorisées (matrice C×C triangulaire) et lit le passé antérieur via l’état entrant : O = M·V + K·S_entrant. Sur la trace, le chunk 1 produit S₄, le chunk 2 le consomme — et o₅..o₈ sont exactement ceux de la récurrence.

Pourquoi / à quel prix : Exactitude algébrique, parallélisme retrouvé. Le prix : une mémoire temporaire en C² pour le triangle, et une complexité de code réelle — deux termes à additionner, donc deux occasions de se tromper : ce sont précisément les deux bugs de la trace.

Vérification de compréhension

Nommez l’entrée, l’état transformé, la sortie et une hypothèse nécessaire. Comparez ensuite votre chaîne à l’explication ci-dessus.

4. Triangle causal

Le problème : À l’intérieur d’un chunk, tous les tokens se calculent ensemble — y compris t5 avec t7, qui est son futur. Sans garde-fou, le préfill apprendrait des dépendances que le décodage ne pourra jamais reproduire.

L’idée : Une matrice triangulaire inférieure matérialise la règle « i ne lit que j ≤ i » : les cases au-dessus de la diagonale sont interdites par construction. Piège de lecture : un 0 sous la diagonale est un produit scalaire nul (autorisé) ; un « . » au-dessus est la causalité.

Pourquoi / à quel prix : Le triangle rend la contrainte vérifiable d’un coup d’œil et gratuite à appliquer. Le prix d’un masque faux est vicieux : la perplexité s’améliore « trop bien » en préfill et rien ne casse — jusqu’au décodage, qui ne peut pas tricher.

Vérification de compréhension

Nommez l’entrée, l’état transformé, la sortie et une hypothèse nécessaire. Comparez ensuite votre chaîne à l’explication ci-dessus.

5. État entrant et sortant

Le problème : Le chunk 2 ne doit revoir aucun token du chunk 1 — sinon le parallélisme s’effondre — mais o₅ dépend de v₁ et v₃. Comment transmettre « tout le passé utile » sans transmettre le passé ?

L’idée : Par l’état : le chunk 1 émet S₄ = K₁ᵀV₁, un résumé de taille fixe ; le chunk 2 le lit par q_tᵀS₄ et ajoute ses termes locaux. Sur la trace : o₅ = [3,4] (hérité) + [0,1] (local) = [3,5]. Les frontières transportent l’ordre et la causalité, pas les tokens.

Pourquoi / à quel prix : Un seul objet à passer entre blocs, de taille fixe. Le prix : le chunk 2 ne peut plus décomposer [3,4] entre v₁ et v₃ — la compression de la session 13 s’applique aux frontières. Et oublier le terme entrant (bug 1) ampute tout le prompt.

Vérification de compréhension

Nommez l’entrée, l’état transformé, la sortie et une hypothèse nécessaire. Comparez ensuite votre chaîne à l’explication ci-dessus.

6. Taille du chunk

Le problème : C = 1 redonne la récurrence lente ; C = longueur totale fait exploser le triangle en C². Entre les deux, qui décide ? Le même code peut tourner plusieurs fois plus lentement avec un C mal choisi pour le GPU.

L’idée : C arbitre deux coûts opposés : transitions séquentielles en n/C contre mémoire temporaire en C². Doubler C divise les transitions par 2 et multiplie le triangle par 4 — l’optimum se trouve là où le triangle sature juste la mémoire rapide (SRAM).

Pourquoi / à quel prix : Un C bien choisi sature le matériel. Le prix : le bon C n’est pas transférable d’un GPU à l’autre — c’est un paramètre d’exécution à re-mesurer, pas une constante du modèle. Et il ne change jamais les résultats, seulement leur coût.

Vérification de compréhension

Nommez l’entrée, l’état transformé, la sortie et une hypothèse nécessaire. Comparez ensuite votre chaîne à l’explication ci-dessus.

Développement depuis la source de cours

Chapitre 10 — Découpage en blocs et préremplissage parallèle

10.1 Pourquoi utiliser des blocs

But : combiner une règle de mémoire récurrente avec le calcul rapide d’un GPU. Traiter les jetons un par un est simple à décrire, mais utilise mal les nombreuses unités de calcul parallèles pendant la lecture du prompt, appelée préremplissage.

Au lieu de transporter les courses article par article, place plusieurs articles dans une caisse et déplace la caisse.

Étape par étape

  • Découper N jetons en blocs, par exemple 64 ou 128 dans une implémentation.

  • À l’intérieur d’un bloc, organiser de nombreuses lectures et mises à jour sous forme de grandes opérations matricielles.

  • Transmettre l’état final S d’un bloc au suivant.

  • Préserver l’ordre causal : le jeton t ne doit pas utiliser les jetons futurs.

Exemple détaillé : 12 jetons divisés en blocs de 4 donnent les blocs 1–4, 5–8 et 9–12. Le deuxième bloc reçoit l’état résumant les jetons 1–4. Ses quatre jetons peuvent effectuer une grande partie de leurs calculs ensemble, puis produire un état pour le troisième bloc.

Pourquoi c’est important : Le découpage ne change pas l’objectif d’apprentissage. Il réorganise des opérations équivalentes, ou soigneusement dérivées, afin que le matériel les exécute efficacement.

Vérification rapide : pourquoi ne pas choisir automatiquement un bloc gigantesque ? Réponse : les grands blocs augmentent le travail et le stockage temporaires ; la meilleure taille dépend du matériel et des noyaux de calcul.

10.2 Structure triangulaire causale

But : comprendre le masque triangulaire inférieur utilisé dans un bloc. Une matrice triangulaire inférieure contient des zéros au-dessus de sa diagonale principale.

C’est une règle scolaire : chaque élève peut lire seulement les lignes précédentes, jamais les réponses écrites plus tard.

Étape par étape

  • Pour quatre positions, les liens autorisés forment [[1,0,0,0],[1,1,0,0],[1,1,1,0],[1,1,1,1]].

  • La ligne 3 peut utiliser les positions 1, 2 et 3.

  • La ligne 1 peut utiliser uniquement la position 1.

Exemple détaillé : Le triangle empêche la prédiction du prochain jeton de tricher pendant l’entraînement et le préremplissage.

Pourquoi c’est important : La causalité est une condition de correction, pas seulement un détail d’optimisation.

Vérification rapide : dans un bloc causal, le jeton 2 peut-il utiliser le jeton 4 ? Réponse : non.

Cas guidé complet

Pour 8 tokens en chunks de 4, le premier calcule un triangle 4×4 puis transmet S₄. Le second reçoit S₄, calcule son triangle local et produit S₈. Aucun token du premier bloc ne peut lire le second.

Méthode de lecture: écrire les données, annoncer la forme de chaque objet, effectuer une seule transformation, puis interpréter le résultat avant de continuer.

Frontière de validité

Le chunking améliore l’exécution ; il ne change pas automatiquement la capacité informationnelle de l’état.

Statut de preuve: Mécanismes établis ; les simplifications numériques sont pédagogiques.

Vérifications rapides

1. Que fait réellement Préfill et décodage?

Deux phases, deux régimes : le préfill voit tous les tokens du prompt en même temps (travail massivement parallélisable) ; le décodage n’ajoute qu’un token par pas (travail intrinsèquement séquentiel). Même mécanisme, profils d’exécution opposés.

2. Que fait réellement Récurrence naïve?

Le diagnostic précis : la DÉPENDANCE est séquentielle (chaque S_t dépend de S_{t−1}), mais la majorité du CALCUL par token — produits q·k locaux, écritures k vᵀ — ne l’est pas. La récurrence naïve sérialise tout parce qu’elle ne sépare pas les deux.

3. Que fait réellement Découper en chunks?

Un chunk de C tokens calcule d’un coup ses interactions internes autorisées (matrice C×C triangulaire) et lit le passé antérieur via l’état entrant : O = M·V + K·S_entrant. Sur la trace, le chunk 1 produit S₄, le chunk 2 le consomme — et o₅..o₈ sont exactement ceux de la récurrence.

4. Que fait réellement Triangle causal?

Une matrice triangulaire inférieure matérialise la règle « i ne lit que j ≤ i » : les cases au-dessus de la diagonale sont interdites par construction. Piège de lecture : un 0 sous la diagonale est un produit scalaire nul (autorisé) ; un « . » au-dessus est la causalité.

Sources et frontière de preuve

Portée: Mécanismes établis ; les simplifications numériques sont pédagogiques.