# Voorwaartse vs achterwaartse pass: backpropagation

[Naar de inhoud](#lm-inhoud)Netwerk/NL[EN](/en/)[Hubhub.llmnet.nlModellen vergelijken op taak, taal, kosten en licentie.](https://hub.llmnet.nl/)[Communitycommunity.llmnet.nlPrompttechnieken, patronen en systeemprompts.](https://community.llmnet.nl/)[APIapi.llmnet.nlLLM's robuust in software: rate limits, routing, structured output.](https://api.llmnet.nl/)[Consultancyconsultancy.llmnet.nlAI invoeren in een organisatie, van pilot tot productie.](https://consultancy.llmnet.nl/)[Nieuwsnieuws.llmnet.nlOntwikkelingen in AI, geduid voor Nederland.](https://nieuws.llmnet.nl/)[Benchmarkbenchmark.llmnet.nlZelf meten wat AI-kwaliteit is, voor jouw taken.](https://benchmark.llmnet.nl/)[Vacaturesvacatures.llmnet.nlAI-rollen, salarissen en carrièrepaden in Nederland.](https://vacatures.llmnet.nl/)[Lerenleren.llmnet.nlAI-concepten in gewoon Nederlands, van beginner tot bouwer.](https://leren.llmnet.nl/)[Gidsgids.llmnet.nlAI privé draaien op eigen Mac, pc, NAS of thuisserver.](https://gids.llmnet.nl/)[Directorydirectory.llmnet.nlHet AI-ecosysteem in kaart: tools, modellen, bedrijven.](https://directory.llmnet.nl/)[Radarradar.llmnet.nlSignalen uit X, onderzoek en communities voor indie developers.](https://radar.llmnet.nl/)[Appsapps.llmnet.nlReviews van AI-apps en open-source repo's, met tips voor wie zelf bouwt.](https://apps.llmnet.nl/)[llmnet.nl — hoofdsite](https://llmnet.nl/)[](https://x.com/intent/post?url=https%3A%2F%2Fleren.llmnet.nl%2Fvoorwaartse-versus-achterwaartse-pass-backpropagation-in-transformers&text=Voorwaartse%20vs%20achterwaartse%20pass%3A%20backpropagation)[](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fleren.llmnet.nl%2Fvoorwaartse-versus-achterwaartse-pass-backpropagation-in-transformers)[](https://www.reddit.com/submit?url=https%3A%2F%2Fleren.llmnet.nl%2Fvoorwaartse-versus-achterwaartse-pass-backpropagation-in-transformers&title=Voorwaartse%20vs%20achterwaartse%20pass%3A%20backpropagation)[](#)[](https://x.com/intent/post?url=https%3A%2F%2Fleren.llmnet.nl%2Fvoorwaartse-versus-achterwaartse-pass-backpropagation-in-transformers&text=Voorwaartse%20vs%20achterwaartse%20pass%3A%20backpropagation)[](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fleren.llmnet.nl%2Fvoorwaartse-versus-achterwaartse-pass-backpropagation-in-transformers)[](https://www.reddit.com/submit?url=https%3A%2F%2Fleren.llmnet.nl%2Fvoorwaartse-versus-achterwaartse-pass-backpropagation-in-transformers&title=Voorwaartse%20vs%20achterwaartse%20pass%3A%20backpropagation)[](#)

 
# Voorwaartse versus achterwaartse pass: backpropagation in transformers

 Door Ivo Donker — samengesteld met AI-ondersteuning (Claude & Gemini)

 
 Wat je hiervoor moet weten
 Dit artikel valt onder Module 5 (Onder de motorkap). Om de wiskundige stromen en geheugendynamiek volledig te doorgronden, helpt het als de basisstructuur van neurale netwerken al helder is. Raadpleeg vooraf [hoe een transformer is opgebouwd](https://leren.llmnet.nl/wat-is-een-transformer) en bekijk hoe matrixprojecties en gewichten zijn georganiseerd in [het overzicht van parameters en gewichten](https://leren.llmnet.nl/parameters-en-gewichten).

 

 Het trainen van een modern taalmodel berust fundamenteel op een herhalende cyclus van twee opeenvolgende fasen: de voorwaartse pass (forward pass) en de achterwaartse pass (backward pass). Hoewel een LLM tijdens normale inferentie uitsluitend de voorwaartse berekening doorloopt om het volgende token te voorspellen, vereist elk trainings- en fine-tuningproces de volledige lus. In de voorwaartse fase stromen getokeniseerde vectoren door tientallen transformatorlagen om tot een kansverdeling over het vocabulaire te komen. Vervolgens berekent een verliesfunctie (loss function) de discrepantie tussen de voorspelling en de werkelijke doeltekst. Direct daarna start de achterwaartse pass: via de kettingregel van de differentiaalrekening (backpropagation) reizen foutgradiënten terug door het netwerk om vast te stellen welke gewichten moeten worden aangepast.

 In dit artikel ontleden we de wiskundige grondslagen, de computationele asymmetrie en de enorme geheugendruk die backpropagation uitoefent op moderne GPU-clusters. We volgen een concrete tensorstroom door een transformatorblok en analyseren waarom het opslaan van tussenliggende activaties de grootste flessenhals vormt bij grootschalige LLM-training.

 
## De voorwaartse pass: van embedding naar logits

 Tijdens de voorwaartse pass transformeert het model een discrete reeks invoertokens stap voor stap in een reeks continue representaties. Beschouw een batch tokens met dimensies [B, S], waarbij B staat voor de batchgrootte en S voor de sequentielengte. De eerste operatie is de embedding-lookup, waarbij elk token-ID wordt omgezet in een vector van verborgen dimensie D (de modeldimensie $d_{\text{model}}$), aangevuld met positionele informatie. Dit levert de initiële activatietensor $X_0 \in \mathbb{R}^{B \times S \times D}$ op.

 Vanaf dit punt passeert de tensor achtereenvolgens $L$ identieke transformer-lagen. Binnen elke laag $l$ vinden twee opeenvolgende hoofdtransformaties plaats, elk omsloten door een residuale verbinding (skip connection) en een normalisatiestap (RMSNorm of LayerNorm):

 Eerst ondergaat de genormaliseerde invoer een multi-head self-attention-berekening. De tensor projecteert via lineaire gewichtsmatrices $W_Q, W_K, W_V$ naar Queries, Keys en Values. Om de exacte matrixvermenigvuldigingen en de schalingsfactor van deze stap te begrijpen, kun je [de wiskundige afleiding van self-attention bestuderen](https://leren.llmnet.nl/self-attention-wiskundig-ontleed-van-dot-product-naar-softmax). De uitvoer van de attention-heads wordt samengevoegd, lineair geprojecteerd via $W_O$ en opgeteld bij de oorspronkelijke residustroom:

 # Wiskundige stappen binnen de voorwaartse pass van laag l
X_norm1 = RMSNorm(X_{l-1})
X_attn = MultiHeadAttention(Q=X_norm1 * W_Q, K=X_norm1 * W_K, V=X_norm1 * W_V) * W_O
X_mid = X_{l-1} + X_attn

X_norm2 = RMSNorm(X_mid)
X_ffn = FeedForward(X_norm2)
X_l = X_mid + X_ffn

 Na de attention-laag volgt het feed-forward netwerk (FFN of MLP). In moderne architecturen zoals Llama bestaat dit netwerk uit gepaarde lineaire projecties met een niet-lineaire gating-functie. Voor een diepere duik in waarom klassieke activaties hier niet meer worden toegepast, lees je [de analyse over SwiGLU en GELU activatiefuncties](https://leren.llmnet.nl/activatie-functies-in-llm-s-waarom-swiglu-en-gelu-relu-vervangen). Na de laatste transformer-laag $L$ volgt een finale normalisatie en een lineaire projectie naar het vocabulaire via de embedding-matrix $W_{\text{vocab}}$, resulterend in logits $Z \in \mathbb{R}^{B \times S \times V}$.

 
## De verliesfunctie en de start van de achterwaartse pass

 De overgang van de voorwaartse naar de achterwaartse pass vindt plaats bij de loss function. Bij autoregressieve taalmodellen is dit vrijwel altijd de cross-entropy loss over het volgende token. Gegeven de logits $Z$ en het ware doeltarget $y \in \{1, \dots, V\}$, berekent de softmax-functie de voorspelde kansverdeling $\hat{p}$:

 # Kansverdeling via Softmax voor token op positie t
p_{t, i} = exp(z_{t, i}) / sum_{j=1}^V exp(z_{t, j})

# Cross-entropy verlies voor positie t
L_t = -log(p_{t, y_t})

# Totale gemiddelde loss over de batch en sequentie
Loss = (1 / (B * S)) * sum_{b=1}^B sum_{t=1}^S L_{b, t}

 De achterwaartse pass begint met het bepalen van de partiële afgeleide van de totale loss ten opzichte van de niet-genormaliseerde logits $z_{t, i}$. Een bekende wiskundige eigenschap van de combinatie van softmax en cross-entropy is dat deze afgeleide opmerkelijk eenvoudig reduceert:

 dL / dz_{t, i} = p_{t, i} - 1 (als i gelijk is aan doeltarget y_t)
dL / dz_{t, i} = p_{t, i} (als i NIET gelijk is aan target y_t)

Oftewel in vectorvorm:
dL / dZ = (P - Y_onehot) / (B * S)

 Deze gradiënttensor $\frac{\partial \mathcal{L}}{\partial Z}$, met identieke dimensies aan de logits [B, S, V], vormt het startpunt van de achterwaartse golf. Vanaf hier reist de foutvector laag voor laag terug naar het begin van het netwerk.

 
## Backpropagation via de kettingregel door het transformer-blok

 Backpropagation is in essentie de systematische toepassing van de multivariabele kettingregel uit de calculus. Beschouw een generieke lineaire laag $Y = X \cdot W$, waarbij $X \in \mathbb{R}^{N \times D_{\text{in}}}$ de invoeractivatie is en $W \in \mathbb{R}^{D_{\text{in}} \times D_{\text{out}}}$ de gewichtsmatrix. Tijdens de achterwaartse pass ontvangt deze laag de stroomopwaartse gradiënt $\frac{\partial \mathcal{L}}{\partial Y}$. De laag moet nu twee afzonderlijke berekeningen uitvoeren:

 
 
 
 
 Gradiënttype | 
 Wiskundige formulering | 
 Doel en bestemming | 
 

 
 
 
 Gewichtsgradiënt ($\frac{\partial \mathcal{L}}{\partial W}$) | 
 $X^T \cdot \frac{\partial \mathcal{L}}{\partial Y}$ | 
 Wordt gebruikt door de optimizer (zoals AdamW) om gewicht $W$ bij te werken. | 
 

 
 Activeringsgradiënt ($\frac{\partial \mathcal{L}}{\partial X}$) | 
 $\frac{\partial \mathcal{L}}{\partial Y} \cdot W^T$ | 
 Stroomt verder terug naar de voorgaande lagen in de computationele graaf. | 
 

 
 
 

 Hier zien we direct het centrale mechanisme van deep learning: om de gewichtsgradiënt $\frac{\partial \mathcal{L}}{\partial W}$ te berekenen, heeft het systeem de oorspronkelijke invoeractivatie $X$ nodig die tijdens de voorwaartse pass is gegenereerd. Als $X$ niet bewaard is gebleven in het GPU-geheugen, kan de vermenigvuldiging $X^T \cdot \frac{\partial \mathcal{L}}{\partial Y}$ niet plaatsvinden.

 Wanneer de gradiënt door een residuale verbinding $X_{l} = X_{l-1} + F(X_{l-1})$ stroomt, dicteert de optellingsregel dat de gradiënt zich splitst:

 dL / dX_{l-1} = (dL / dX_l) + (dL / dF) * (dF / dX_{l-1})

 De term $\frac{\partial \mathcal{L}}{\partial X_l}$ stroomt ongehinderd via de residustroom direct door naar eerdere lagen. Dit fenomeen voorkomt het beruchte probleem van verdwijnende gradiënten (vanishing gradients) in diepe architecturen met honderden lagen.

 
## Het activatiegeheugen: de verborgen flessenhals

 Bij het trainen van grote modellen is het VRAM-geheugengebruik niet alleen afhankelijk van het aantal parameters, maar vooral van de bewaarde tussenactivaties. Terwijl gewichten en optimizer-states een vaste omvang hebben, schaalt het activatiegeheugen lineair met de batchgrootte, lineair met het aantal lagen en kwadratisch (of lineair bij FlashAttention) met de sequentielengte.

 Voor een standaard transformer-laag moeten de volgende tensoren worden opgeslagen tijdens de forward pass om de backward pass mogelijk te maken:

 
 
- Invoer voor LayerNorm / RMSNorm: Nodig om de normalisatiegradiënt te bepalen.
 
- Queries, Keys en Values: Nodig voor de gradiënten van de projectiegewichten $W_Q, W_K, W_V$.
 
- Attention-matrix (vóór en na softmax): Nodig om de Value-gewichten en Query/Key-gradiënten te berekenen ($S \times S$ elementen per head).
 
- Invoer voor de MLP-laag: Nodig voor de gradiënten van de up-projection en gate-matrices.
 
- Pre-activatietoestanden: De waarden vóór de SwiGLU- of GELU-operatie om de afgeleide van de activatiefunctie te berekenen.
 

 Bij een model met 70 miljard parameters en een contextlengte van 8192 tokens overtreft het geheugen dat nodig is voor deze tussenactivaties al snel het geheugen van de modelparameters zelf. Dit dwingt engineers om geavanceerde technieken toe te passen, zoals Activation Checkpointing (ook bekend als gradient checkpointing). Hierbij worden activaties tijdens de forward pass weggegooid en tijdens de backward pass lokaal opnieuw berekend, wat circa 30% extra rekentijd kost maar het activatiegeheugen met wel 70% tot 80% reduceert.

 
## Computationele vergelijking: FLOPs en rekentijd

 Er bestaat een vuistregel binnen LLM-engineering: de achterwaartse pass kost ruwweg tweemaal zoveel FLOPs (drijvendekomma-operaties) als de voorwaartse pass. Dit brengt de totale trainingskosten per token op circa $6N$ FLOPs, waarbij $N$ het aantal actieve modelparameters voorstelt ($2N$ voor forward, $4N$ voor backward).

 
 
 
 
 Eigenschap | 
 Voorwaartse Pass (Inference & Training) | 
 Achterwaartse Pass (Uitsluitend Training) | 
 

 
 
 
 Doel | 
 Logits en voorspellingen genereren. | 
 Gradiënten berekenen voor gewichten en inputs. | 
 

 
 Rekenintensiteit (FLOPs) | 
 $\approx 2N$ operaties per token. | 
 $\approx 4N$ operaties per token (twee matrixproducten). | 
 

 
 Geheugenvereiste | 
 Laag (tussenactivaties kunnen direct worden overschreven bij pure inferentie). | 
 Zeer hoog (vereist behoud van alle forward-activaties of herberekening). | 
 

 
 Parallelle afhankelijkheid | 
 Strikte stroom van laag $1 \to L$. | 
 Strikte stroom van laag $L \to 1$. | 
 

 
 Hardware-karakteristiek | 
 Compute-bound bij grote batches; memory-bandwidth bound bij single-token autoregressie. | 
 Vrijwel altijd compute-bound op tensor cores door grote matrixvermenigvuldigingen. | 
 

 
 
 

 Waarom kost de backward pass precies tweemaal zoveel rekenkracht als de forward pass? Zoals eerder aangetoond bij de lineaire laag $Y = X \cdot W$: in de forward pass is er slechts één matrixvermenigvuldiging ($X \cdot W$). In de backward pass moeten er echter twee matrixvermenigvuldigingen worden uitgevoerd: één om de gradiënt naar de invoer te propageren ($\frac{\partial \mathcal{L}}{\partial Y} \cdot W^T$) en één om de gradiënt naar de parameters te bepalen ($X^T \cdot \frac{\partial \mathcal{L}}{\partial Y}$).

 In productiesystemen zien we dat de kosten van deze rekenstappen direct doorwerken in de infrastructuurkeuzes; wie wil zien hoe deze computationele verdeling doorwerkt in serverkosten en doorvoersnelheden kan [de analyse van batch versus realtime processing](https://hub.llmnet.nl/kosten-van-batch-inference-versus-realtime-api-verkeer) raadplegen.

 
## Stapsgewijze gradient flow in een PyTorch-achtige implementatie

 Om te visualiseren hoe een transformer-blok computationeel schakelt tussen de voorwaartse en achterwaartse stroom, volgt hieronder een modulaire weergave in Python-pseudocode:

 import torch

class TransformerBlockBackprop:
 def __init__(self, d_model):
 # Initialisatie van projectiegewichten
 self.W_q = torch.randn(d_model, d_model, requires_grad=True)
 self.W_k = torch.randn(d_model, d_model, requires_grad=True)
 self.W_v = torch.randn(d_model, d_model, requires_grad=True)
 self.W_out = torch.randn(d_model, d_model, requires_grad=True)

 def forward(self, x):
 # Opslaan van invoeractivatie voor gebruik in backward pass
 self.saved_x = x
 
 # 1. Lineaire projecties
 self.q = torch.matmul(x, self.W_q)
 self.k = torch.matmul(x, self.W_k)
 self.v = torch.matmul(x, self.W_v)
 
 # 2. Scaled Dot-Product Attention
 d_k = self.q.shape[-1]
 scores = torch.matmul(self.q, self.k.transpose(-2, -1)) / (d_k ** 0.5)
 self.attn_weights = torch.softmax(scores, dim=-1)
 self.context = torch.matmul(self.attn_weights, self.v)
 
 # 3. Output projectie en residuale optelling
 out = torch.matmul(self.context, self.W_out)
 return x + out

 def backward(self, grad_output):
 # grad_output is dL/d(out), stroomopwaarts ontvangen
 
 # Gradiënt door de residuale verbinding splitst lineair
 grad_residual = grad_output.clone()
 
 # Gradiënten voor W_out en de context-tensor
 grad_W_out = torch.matmul(self.context.transpose(-2, -1), grad_output)
 grad_context = torch.matmul(grad_output, self.W_out.t())
 
 # Gradiënten door attention softmax en V-matrix
 grad_v = torch.matmul(self.attn_weights.transpose(-2, -1), grad_context)
 grad_attn_weights = torch.matmul(grad_context, self.v.transpose(-2, -1))
 
 # Softmax backward transformatie
 # (vereenvoudigde representatie van de Jacobiaan-vermenigvuldiging)
 s = self.attn_weights
 grad_scores = s * (grad_attn_weights - (grad_attn_weights * s).sum(dim=-1, keepdim=True))
 grad_scores = grad_scores / (self.q.shape[-1] ** 0.5)
 
 # Gradiënten voor Q en K projecties
 grad_q = torch.matmul(grad_scores, self.k)
 grad_k = torch.matmul(grad_scores.transpose(-2, -1), self.q)
 
 # Gewichtsgradiënten berekenen met opgeslagen invoeractivatie self.saved_x
 grad_W_q = torch.matmul(self.saved_x.transpose(-2, -1), grad_q)
 grad_W_k = torch.matmul(self.saved_x.transpose(-2, -1), grad_k)
 grad_W_v = torch.matmul(self.saved_x.transpose(-2, -1), grad_v)
 
 # Totale activeringsgradiënt naar vorige laag propageren
 grad_x = grad_residual + (
 torch.matmul(grad_q, self.W_q.t()) +
 torch.matmul(grad_k, self.W_k.t()) +
 torch.matmul(grad_v, self.W_v.t())
 )
 
 return grad_x, (grad_W_q, grad_W_k, grad_W_v, grad_W_out)

 In bovenstaand codevoorbeeld is zichtbaar dat `self.saved_x` expliciet bewaard moet blijven tot de `backward`-functie wordt aangeroepen. Zodra het model miljoenen tokens per iteratie verwerkt over tientallen lagen, vormt dit de kernoorzaak van out-of-memory (OOM) fouten op trainingshardware.

 
## Uitdagingen en numerieke instabiliteit bij backpropagation

 Tijdens de backward pass bewegen gradiënten zich over honderden matrixoperaties. Dit brengt aanzienlijke technische en numerieke risico's met zich mee:

 1. Gradiënt-explosie (Exploding Gradients): Wanneer matrixgewichten groter zijn dan 1 of bij herhaalde accumulatie in de residustroom, kunnen gradiënten exponentieel groeien naarmate ze terugkeren naar de eerste lagen. Dit leidt tot NaN-waarden (Not a Number) in de gewichten. De standaard remedie in transformer-training is Gradient Clipping, waarbij de globale norm van alle gradiëntvectoren wordt begrensd tot een vaste drempelwaarde (bijvoorbeeld 1.0):

 if ||g|| > max_norm:
 g = g * (max_norm / ||g||)

 2. Underflow bij mixed-precision training (FP16/BF16): Gradiënten zijn vaak extreem klein (bijvoorbeeld $10^{-6}$ of kleiner). In 16-bit floating point representaties (met name standaard IEEE FP16 met slechts 5 exponent-bits) resulteert dit snel in underflow naar nul. Hierdoor stopt het model met leren. BFloat16 lost dit grotendeels op door 8 exponent-bits te gebruiken (ten koste van precisie in de mantisse), terwijl FP16 geavanceerde Loss Scaling-algoritmen vereist die de loss met een factor vermenigvuldigen vóór backpropagation en de berekende gradiënten daarna weer terugschalen.

 3. Asynchrone communicatie bij Distributed Data Parallel (DDP): Bij training over meerdere GPU's start de synchronisatie van gradiënten (AllReduce) asynchroon zodra een individuele laag zijn backward pass voltooit. Als de rekentijd van de backward pass niet perfect overlapt met de netwerkbandbreedte tussen GPU's (NVLink/Infiniband), ontstaat er aanzienlijke GPU-idle time.

 
## Optimalisatiestrategieën voor de backward pass

 Om de immense geheugendruk en rekenlast van de backward pass te beteugelen, heeft het AI-veld diverse doorbraken gerealiseerd:

 
 
- FlashAttention: Door het slim herberekenen van tussenstappen in het snelle SRAM-geheugen van de GPU hoeft de gigantische $S \times S$ attention-matrix noch in de forward pass noch in de backward pass naar het tragere HBM-geheugen te worden geschreven.
 
- ZeRO (Zero Redundancy Optimizer): Splitst optimizer-states, gradiënten en modelparameters over alle deelnemende GPU's, waardoor de geheugenoverhead van de backward pass drastisch afneemt.
 
- Reversible Layers: Architecturen waarbij tussenactivaties wiskundig exact kunnen worden gereconstrueerd uit de uitvoer van de volgende laag, waardoor er in theorie nul activaties opgeslagen hoeven te worden in de forward pass.
 

 
 Hierna verder met
 Nu het mechanisme van forward en backward propagation helder is, kun je doorgroeien naar de technieken die deze berekeningen versnellen op hardwareniveau. Lees verder over GPU-geheugenoptimalisaties in [FlashAttention ontleed: snellere berekening via GPU-geheugen](https://leren.llmnet.nl/flashattention-ontleed-snellere-berekening-via-gpu-geheugen) of ontdek hoe parameter-efficiënt trainen werkt in [LoRA en adapters uitgelegd](https://leren.llmnet.nl/lora-en-adapters-uitgelegd).
