English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

gast
1 / ?
terug naar lessen

Lokale Gradienten Vermenigvuldigen

Forward & Backward Kernels


De Forward Pass

ANDREA-120M's forward pass leidt input door een reeks operaties:


x = embed(token_ids)         # token embeddings
for layer in 12_layers:
x = x + attn(LN(x))      # attention sublaag
x = x + mlp(LN(x))       # MLP sublaag
logits = LN(x) @ embed.T     # gekoppelde uitvoerprojectie
loss   = cross_entropy(logits, targets)

Elke operatie leest invoertensors en produceert uitvoertensors. De forward pass eindigt in een enkele scalar: de cross-entropy loss voor deze batch.


De Backward Pass

Training update de gewichten in de richting die de loss verlaagt. Om update-richtingen te krijgen, heeft de engine nodig:


dL/dW voor elke leerbare W in het model

De kettingregel geeft dit. Voor een keten loss = f(g(h(x))):


dL/dx = (dL/df) * (df/dg) * (dg/dh) * (dh/dx)

Elke factor is een lokale gradiënt: hoe de uitvoer van één operatie verandert wanneer de invoer met een klein beetje verandert. Het vermenigvuldigen van lokale gradiënten achterwaarts door de grafiek verspreidt het verlies-signaal naar elk gewicht.


Reverse-Mode Differentiëring

Backprop berekent gradiënten in omgekeerde volgorde: beginnend bij dL/dlogits = 1, dan achterwaarts door cross-entropy, dan output-projectie, dan laag-normalisatie, dan twaalf transformer-blokken, dan embeddings. Op elke stap vermenigvuldig je de inkomende gradiënt met de lokale Jacobiaan.


Reverse-mode is efficiënt wanneer de uitvoer een enkele scalaire waarde is (het verlies) & er veel invoeren zijn (de gewichten). Eén achterwaartse doorgang produceert gradiënten voor elk gewicht in het model. Forward-mode zou één doorgang per gewicht nodig hebben; voor ANDREA-120M met ~120M gewichten is forward-mode onhaalbaar.

Waarom Reverse-Mode

ANDREA-120M heeft ~120M gewichten & produceert een enkele scalaire verlies per trainingstap. Vergelijk reverse-mode automatische differentiatie met forward-mode. Stel (1) welke modus alle gewichtgradienten produceert in een enkele achterwaartse doorgang; (2) hoeveel forward-mode doorgangen er nodig zouden zijn om alle 120M gewichtgradienten te berekenen; (3) welke modus ANDREA gebruikt & waarom.

Elke Forward Op Krijgt Een Achterwaarts Tweelingbroertje

De Pairing Discipline

microgpt_cuda.cu levert twee CUDA-kernels voor elke operatie: één die de forward output berekent, één die inputgradienten berekent gegeven outputgradienten. De pairing is één-op-één:


Forward kernelBackward kernelOperatie
k_embed_fwdk_embed_bwdToken embedding lookup
k_layernorm_fwdk_layernorm_bwdLaag normalisatie
k_attn_qkv_fwdk_attn_qkv_bwdQ, K, V projecties
k_attn_fwdk_attn_bwdGeschaalde dot-product aandacht
k_attn_out_fwdk_attn_out_bwdUitvoerprojectie W_O
k_mlp_fwdk_mlp_bwdMLP (met GELU)
k_residual_addk_residual_add_bwdResiduele verbinding
k_loss_fwdk_loss_bwdCross-entropy verlies

Acht operatieparen dekken de volledige transformer af. Plus een paar hulpkernels: k_grad_norm_partial, k_grad_norm_final, k_grad_scale voor gradient clipping (zie activiteit 75).


De taak van een Backward Kernel

Gezien de gradiënt die binnenstroomt vanuit latere lagen (grad_output), berekent een backward kernel:


1. grad_input: de gradiënt met betrekking tot de invoertensor van de operatie. Deze wordt verder achterwaarts doorgegeven.

2. grad_weight: de gradiënt met betrekking tot de leerbare parameters in de operatie. Deze gaat naar de toestand van de optimizer.


Beide worden berekend in één kernel-lancering. CUDA-threads werken samen op tegels van de gradiënttensor parallel.


Opgeslagen Tensoren

Achterwaartse berekening heeft vaak waarden nodig uit de voorwaartse doorgang. Bijvoorbeeld, k_layernorm_bwd heeft het gemiddelde & de variantie nodig die tijdens de voorwaartse doorgang zijn berekend; k_mlp_bwd heeft de GELU pre-activatie nodig. De trainingsengine slaat deze op in speciale buffers tijdens de voorwaartse doorgang, en leest ze dan tijdens de achterwaartse doorgang.


Geheugenkosten: ongeveer dezelfde vorm als de forward-uitvoer voor elke opgeslagen tensor. Voor ANDREA-120M met batch=8, seq=1024, d_model=768, is één opgeslagen tensor 8 × 1024 × 768 × 4 bytes = 25 MB. Over 12 lagen & meerdere opgeslagen tensors per laag domineren activaties het VRAM tijdens training (~5-10 GB op een 24 GB kaart).

Het traceren van één backward-stap

ANDREA-120M voltooit een forward-pass door één transformerblok. Traceer wat er gebeurt tijdens de backward-pass door datzelfde blok (in pre-norm structuur: `x = x + Attention(LN(x))` dan `x = x + MLP(LN(x))`). Noem de backward-kernels in de volgorde waarin ze afvuren, & geef aan met welke forward-kernel elke een paar vormt. Dek minstens 4 kernels af.

Waar Gradients in het Geheugen Staan

Eén Gradient Tensor Per Weight Tensor

Elke leerbare weight tensor in ANDREA-120M heeft een bijpassende gradient tensor van identieke vorm. Voor elk blok:


W_Q       [768, 768]     ↔   grad_W_Q       [768, 768]
W_K       [768, 768]     ↔   grad_W_K       [768, 768]
W_V       [768, 768]     ↔   grad_W_V       [768, 768]
W_O       [768, 768]     ↔   grad_W_O       [768, 768]
W_1       [768, 3072]    ↔   grad_W_1       [768, 3072]
W_2       [3072, 768]    ↔   grad_W_2       [3072, 768]
LN1.gamma [768]          ↔   grad_LN1.gamma [768]
LN1.beta  [768]          ↔   grad_LN1.beta  [768]
LN2.gamma [768]          ↔   grad_LN2.gamma [768]
LN2.beta  [768]          ↔   grad_LN2.beta  [768]

Plus token embeddings, position embeddings, & een finale laag norm. Het totale geheugen van de gradient buffer komt overeen met het gewicht geheugen: ~120M floats, ~480 MB bij FP32, ~240 MB bij FP16.


Accumulatie Over Microbatches

ANDREA's batch_size = 8 past in VRAM bij FP16. Grotere effectieve batches vereisen gradient accumulatie: voer meerdere forward+backward passes uit op kleine batches, tel gradients op in dezelfde buffer, en neem dan één optimizer stap.


for microbatch in range(n_microbatches):
forward(microbatch)
backward()           # VOEGT TOE aan grad buffers, overschrijft niet
scale_grads(1.0 / n_microbatches)  # gemiddelde over microbatches
optimizer_step()
zero_grads()             # reset voor volgende trainingstap

Backward kernels gebruiken += semantiek, niet =. Elke aanroep voegt gradiëntbijdragen toe aan de bestaande buffer; de buffer houdt de lopende som vast totdat zero_grads() deze wist.


De Optimizer Status

AdamW (activiteit 73) houdt twee extra buffers per gewicht: first moment m & second moment v. Totaal geheugengebruik tijdens training:


gewichten:    1× gewichtenaantal
gradienten:  1× gewichtenaantal
Adam m:     1× gewichtenaantal
Adam v:     1× gewichtenaantal
opgeslagen acts: ~2-4× afhankelijk van lagen & batch
──────────────────────────────────────────
totaal:      ~6-8× gewichtenaantal

ANDREA-120M op FP16: ~240 MB × 4 buffers (gewicht, grad, m, v) + ~5-10 GB activaties = ~10-12 GB totaal. Ruim onder het 24 GB plafond van de RTX 4090. ANDREA-12M getraind in 1.4 GB; de 10× parameter scaling brengt ~10× geheugen.

Gradient Buffers Dimensioneren

ANDREA-120M heeft ~120.000.000 gewichten & gebruikt gradientaccumulatie over 4 microbatches per trainingstap. Bereken: (a) grootte van de gradientbuffer in MB bij FP16; (b) totaal geheugen voor gewichten + gradients + Adam m + Adam v bij FP16; (c) hoeveel aparte `forward()` + `backward()` oproepen plaatsvinden per trainingstap. Toon je berekeningen.

Volledige Controle Over Geheugen & Precisie

Wat Generieke Frameworks Kosten

PyTorch & JAX maken autograd handig: schrijf Python-code, krijg gradients automatisch. De kosten: een generieke dispatch-laag tussen jouw code & CUDA. Elke operatie gaat door Python-interpreter-overhead, framework-boekhouding, & dynamische kernel-selectie. Voor het trainen van een klein taalmodel op één GPU doet die overhead ertoe.


Concrete kosten die ANDREA vermijdt:


1. Python-interpreterlatentie. Elke PyTorch-operatie kruist de Python/C++-grens. Voor ~100 kernelstarts per trainingstap bij ~9 stappen/min, dat zijn ~900 grensovergangen per minuut. Dispatch op C-niveau elimineert dit.


2. Onvoorspelbaarheid van framework-allocator. De caching-allocator van PyTorch geeft goede doorvoer in gemiddeld geval maar onvoorspelbare piekgeheugengebruik. De training-engine van ANDREA pre-alloceert elke buffer bij opstarten; geen herallocatie tijdens training, geen fragmentatie, geen verrassende OOM's bij stap 100K.


3. Generieke kernelselectie. PyTorch kiest kernels tijdens runtime via heuristieken. ANDREA kiest kernels tijdens compiletijd, afgestemd op RTX 4090 tensor core tile-groottes.


4. Mixed-precision plumbing. ANDREA-120M's FP16 cuBLAS pad & ANDREA's FP8 E4M3 tensor core experimenten vereisen precieze controle over welke tensoren op welke precisie leven. Generieke frameworks bieden deze controle via gelaagde API's; custom CUDA schrijft het direct.


De Afweging

Custom CUDA kosten: meer code om te schrijven, meer bugs om te vinden, geen community-ecosysteem. ANDREA's microgpt_cuda.cu is ~6000 regels handgeschreven CUDA die maanden debuggen kostte. Elke nieuwe operatie vereist het schrijven van een forward kernel, een backward kernel, & tests.


Wat ANDREA wint:


- Volledige reproduceerbaarheid. De trainings-pipeline is één C-binary plus één Python-proxy. Geen versieverschuivingen over PyTorch-releases, geen CUDA-versieconflicten met framework-wheels.

- Bit-exact hervattingen. SIGTERM triggert een checkpoint-schrijfactie die elke tensor exact vastlegt zoals de GPU het ziet. Hervatten pakt dezelfde verliesbaan op waar de run was.

- Voorspelbaar geheugen. ANDREA-120M getraind voor 200K stappen zonder OOM's. Geheugen werd bij engine-startup volledig in rekening gebracht.

- Directe hardwaretoegang. Tensor core-tilegroottes, FP8 E4M3-instellingen, asynchrone geheugenkopieën: allemaal direct adresseerbaar in CUDA, ondoorzichtig in generieke frameworks.


Reproduceerbaarheid Als Missie

De ANDREA-whitepaper sectie 9 somt de volledige reproduceerbaarheidsstack op:


Training engine: microgpt/microgpt_cuda.cu
Training proxy: microgpt/training_proxy.py
Experiment configs: experiments/ANDREA-*-TRAIN.json
Data pipeline: scripts/pull-hermes3.py, scripts/prep-megachat.py
Dashboard: scripts/live-loss-dashboard.html
Bandit specification: docs/FIREHOSE-BANDIT.md
Model documentation: docs/ANDREA.md

Hardwarevereiste: één NVIDIA GPU met ≥8 GB VRAM (RTX 3060 of beter). Iedereen kan ANDREA-12M reproduceren vanuit deze artefacten. Het aangepaste CUDA-pad is onderdeel van waarom: geen frameworkversiebevriezingen, geen afhankelijkheidsverrassingen over vijf jaar.


Signalen & Checkpoints

De CUDA-trainingslus reageert op twee POSIX-signalen:


- SIGTERM: schrijf een onmiddellijke checkpoint, en stop dan. Gebruikt bij het netjes stoppen van de training.

- SIGUSR1: schrijf een onmiddellijke checkpoint, ga door met trainen. Gebruikt tijdens de polish pivot in v3 om de staat vast te leggen zonder de run te onderbreken.


Checkpoint formaat: [int32 step][int32 n_params][n_params × float32 weights][n_params × float32 m][n_params × float32 v]. Stapenteller, aantal gewichten, dan gewichten gevolgd door Adam-momenten. Hervat bit-exact. De proxy archiveert .samples.json & .state.json apart bij polish; .loss.json wordt nooit gearchiveerd (het accumuleert de volledige trainingsgeschiedenis).

Waarom Geen PyTorch

ANDREA had PyTorch's autograd kunnen gebruiken in plaats van `microgpt_cuda.cu` met de hand te schrijven. Geef twee verschillende technische redenen waarom ANDREA voor custom CUDA koos. Eén reden moet verwijzen naar geheugen- of precisiecontrole; de andere moet verwijzen naar reproduceerbaarheid, framework-afhankelijkheden of langetermijnonderhoud.