07 · Entrenament: optimitzadors, normalització i regularització
Idea central: l'arquitectura decideix què pot aprendre un model; la recepta d'entrenament decideix què aprendrà de fet. A escala d'LLM, l'estabilitat numèrica passa a ser tan important com l'algorisme.
1. El bucle d'entrenament, sense misteri# sense fórmules
for step, batch in enumerate(loader):
with autocast(dtype=torch.bfloat16):
loss = model(batch).loss / accum_steps
loss.backward() # acumula gradients
if (step + 1) % accum_steps == 0:
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
for g in opt.param_groups: g['lr'] = lr_at(step)
opt.step(); opt.zero_grad(set_to_none=True)
Els cinc elements: pèrdua → gradients → clipping → schedule de LR → pas de l'optimitzador. Tot el que ve a continuació són variacions sobre aquest esquelet.
El bucle, pas a pas
Quins són els cinc elements d'un pas d'entrenament, i què vol dir de debò acumular gradients?
- 1. PèrduaL = 0,5920
- 2. Gradients‖g‖ = 0,5805
- 3. Clipping‖g‖ → 0,5805
- 4. Schedule de LRη = 3,00·10⁻⁴
- 5. Pas de l’optimitzador‖Δθ‖ = 0,002
I es buiden els gradients. Tot el que ve després al fitxer són variacions sobre aquests cinc elements.
Acumulació en marxa
4 microlots de 8 exemples. Cadascun fa el seu forward i el seu backward i suma al mateix búfer de gradients; el pas de l'optimitzador es fa una sola vegada, al final.
El que cap a la memòria d'una GPU alhora.
Quants microlots se sumen abans de fer un pas.
De 10.000, amb 200 de warmup i decaïment cosinus.
La mateixa llavor dona sempre el mateix resultat.
- batch efectiu
- 2.048
- tokens per pas
- 8,39 M
- pèrdua del lot
- 0,5920
- norma del gradient
- 0,5805
- learning rate
- 3,00·10⁻⁴
- ràtio update/param
- 4,21·10⁻⁴
Es calcula el gradient dels 4 microlots de 8 exemples sumant-los en un búfer, i després el del lot sencer de 32 exemples d'una tirada. La diferència màxima entre les dues còpies del gradient, paràmetre a paràmetre, és 2,78·10⁻¹⁷.
| pèrdua acumulada (4 microlots) | 0,5920332184 |
| pèrdua del lot sencer | 0,5920332184 |
| diferència màxima del gradient | 2,78·10⁻¹⁷ |
Coincideixen fins a l'últim bit que la coma flotant permet. Aquesta és tota la justificació de l'acumulació: un batch efectiu de milions de tokens no requereix una GPU que hi càpiga, requereix paciència. El que sí que canvia és el clipping, que s'ha d'aplicar una sola vegada sobre el gradient ja acumulat: si el fessis microlot a microlot, ja no seria el mateix gradient.
Acumulació de gradients: simula batches grans amb poca memòria. Batch efectiu = batch_size × accum_steps × world_size. Els LLMs de frontera fan servir batches de l'ordre de milions de tokens (i sovint amb batch size warmup: comencen petits i creixen).
2. Optimitzadors a escala# sense fórmules
| Optimitzador | Estats/param | Ús |
|---|---|---|
| SGD + momentum | 1 | visió, models petits; poc usat en LLMs |
| AdamW | 2 | estàndard per a LLMs |
| Adafactor | <1 (factoritzat) | quan la memòria mana (T5) |
| 8-bit Adam (bitsandbytes) | 2 (quantitzats) | fine-tuning en GPU petites |
| Lion | 1 | menys memòria, resultats competitius |
| Shampoo/SOAP | matricials | segon ordre aproximat |
| Muon | 1 + ortogonalització | guanys reals d'eficiència de dades; ja s'usa en models grans (sovint AdamW per a embeddings i Muon per a matrius ocultes) |
Hiperparàmetres típics per a preentrenament amb AdamW: , , weight decay (mai sobre biaixos i paràmetres de norma), clipping global .
Memòria per optimitzador
Quina part de la teva GPU se l'emporta l'optimitzador, i quin canvi et fa cabre el model?
La línia de H100 80 GB és a 80 GB. Les barres estan a la mateixa escala entre elles, de manera que la diferència entre AdamW i Adafactor es veu directament com el tros taronja que desapareix.
Les activacions no entren al càlcul de pesos i estats; depenen del batch i de la longitud.
Desmarcat, tot va en fp32 com als anys del deep learning clàssic.
La precisió mixta en manté una per acumular-hi les actualitzacions petites.
- model
- 8,0 B paràmetres
- AdamW · total
- 153,6 GB
- AdamW · només estats
- 64,0 GB
- quota de l’optimitzador
- 50 %
- GPU de 80 GB
- 2 en calen
- el més barat
- SGD
| Optimitzador | Bytes/param | Estats | Total | Amb activacions | H100 80 GB |
|---|---|---|---|---|---|
| SGD | 8,0 | 0,0 GB | 64,0 GB | 76,8 GB | hi cap |
| SGD + momentum | 12,0 | 32,0 GB | 96,0 GB | 115,2 GB | 2 GPU |
| AdamW | 16,0 | 64,0 GB | 128,0 GB | 153,6 GB | 2 GPU |
| Adam de 8 bits | 10,0 | 16,0 GB | 80,0 GB | 96,0 GB | 2 GPU |
| Adafactor | 8,1 | 0,6 GB | 64,6 GB | 77,6 GB | hi cap |
| Lion | 12,0 | 32,0 GB | 96,0 GB | 115,2 GB | 2 GPU |
El desglossament d'AdamW amb aquesta configuració és 16,0 GB de pesos + 32,0 de còpia mestra + 16,0 de gradients + 64,0 d'estats = 128,0 GB, que són 16,0 bytes per paràmetre.
Posa 13 B de paràmetres amb bf16 i còpia mestra i surten exactament 208 GB: és la mateixa xifra de l'exercici del fitxer 02, i ara es veu d'on surt cada terme. Canviar AdamW per Adam de 8 bits en treu 48,0 GB sense tocar res més.
Res d'això no inclou les activacions de debò, que depenen del batch, de la longitud de seqüència i de si fas gradient checkpointing; el control de marge n'és una aproximació grollera. Tampoc no inclou la fragmentació ni els búfers de comunicació, que en entrenament distribuït no són menyspreables.
3. Normalització# sense fórmules
Les tres variants que has de conèixer
BatchNorm normalitza per batch i per canal. Excel·lent en visió, inadequada per a seqüències (dependència del batch, problemes amb longituds variables i amb inferència autoregressiva).
LayerNorm normalitza sobre la dimensió de característiques de cada token:
RMSNorm elimina el centrat i el biaix:
És ~10–15% més ràpida, té menys paràmetres i empíricament funciona igual o millor. És l'estàndard actual (Llama, Qwen, Gemma, DeepSeek, Mistral...).
Normalització comparada
Sobre el mateix lot d'activacions: què normalitza cadascuna, i per què BatchNorm no serveix per a seqüències?
| token 1 | -4,4 | 1,6 | -4,9 | 1,7 | -2,5 | -4,9 | -0,3 | -3,2 | -1,3 | -1,3 | → | -1,0 | 1,5 | -1,3 | 1,6 | -0,2 | -1,3 | 0,7 | -0,5 | 0,3 | 0,3 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| token 2 | -3,9 | 0,3 | -2,6 | 0,6 | 0,7 | -5,2 | 0,2 | -3,6 | -1,0 | -2,9 | → | -1,1 | 1,0 | -0,4 | 1,1 | 1,2 | -1,7 | 0,9 | -0,9 | 0,4 | -0,6 |
| token 3 | -4,0 | 3,1 | -1,6 | 1,8 | -2,5 | -3,1 | 0,5 | -0,3 | 0,3 | -1,0 | → | -1,6 | 1,8 | -0,5 | 1,2 | -0,9 | -1,1 | 0,5 | 0,2 | 0,5 | -0,2 |
| token 4 | -3,1 | 0,2 | -1,5 | 2,1 | 0,4 | -3,8 | 0,6 | -2,4 | 1,6 | -0,4 | → | -1,3 | 0,4 | -0,4 | 1,4 | 0,5 | -1,7 | 0,7 | -0,9 | 1,2 | 0,1 |
| token 5 | -1,7 | 3,3 | -1,6 | 3,4 | 3,9 | -1,6 | 1,5 | -2,2 | 1,5 | 0,0 | → | -1,0 | 1,2 | -1,0 | 1,2 | 1,4 | -1,0 | 0,4 | -1,3 | 0,4 | -0,3 |
| token 6 | -2,0 | 2,7 | -0,8 | 2,3 | 1,0 | -2,1 | 2,7 | 0,0 | 2,1 | 0,1 | → | -1,5 | 1,2 | -0,8 | 1,0 | 0,3 | -1,5 | 1,2 | -0,3 | 0,8 | -0,3 |
| token 7 | -1,8 | 5,2 | -1,2 | 3,5 | 0,5 | -1,0 | 3,1 | 1,0 | 3,0 | 0,2 | → | -1,4 | 1,8 | -1,1 | 1,0 | -0,3 | -1,0 | 0,8 | -0,1 | 0,8 | -0,5 |
| token 8 | -0,4 | 4,2 | 0,5 | 3,7 | -0,5 | -2,1 | 1,6 | -1,2 | 4,5 | 2,0 | → | -0,7 | 1,3 | -0,3 | 1,1 | -0,8 | -1,5 | 0,2 | -1,1 | 1,5 | 0,4 |
LayerNorm normalitza per token, sobre les seves característiques. Amb BatchNorm, la franja acolorida és una columna —una característica sobre tots els tokens del lot—; amb LayerNorm i RMSNorm és una fila, i el lot no hi entra enlloc.
A zero, les files ja estan centrades i RMSNorm i LayerNorm coincideixen.
Baixa'ls a 2 i mira què li passa a la sensibilitat de BatchNorm.
La mateixa llavor dona sempre el mateix resultat.
- normalitza
- per token, sobre les seves característiques
- paràmetres apresos
- 20 (d = 10)
- mitjana del token 1
- -0,0000
- RMS del token 1
- 1,0000
- sensibilitat al lot
- 0
1. RMSNorm contra LayerNorm: la diferència, mesurada
Sobre aquest mateix lot, la diferència màxima component a component entre les dues és 1,0057. Si primer es centren les files a mà, passa a 2,22·10⁻¹⁶: són la mateixa operació tret del centrat. Com que les activacions d'un Transformer ja tenen la mitjana prou a prop de zero, treure el centrat gairebé no canvia el resultat i estalvia una passada sobre el vector, el paràmetre i un parell d'operacions per element. D'aquí el 10–15 % de velocitat.
2. Dependència del lot: per què BatchNorm va quedar fora
Es deixa el primer token igual i es canvia la resta del lot. La sortida d'aquell token hauria de ser la mateixa, i amb BatchNorm no ho és:
| BatchNorm | 0,7078 |
| LayerNorm | 0 |
| RMSNorm | 0 |
Zero exacte, no "petit". Aquesta és la propietat que necessites en una seqüència: la sortida d'un token no pot dependre de quins altres tokens hi hagi al lot, perquè en inferència autoregressiva sovint n'hi ha un de sol, perquè les longituds són variables i perquè el mateix text ha de donar el mateix resultat vagi amb qui vagi. BatchNorm ho incompleix per construcció, i per això cal mantenir-hi estadístiques mòbils per a la inferència, amb tot el que això arrossega.
Pre-norm vs post-norm
- Post-norm (Transformer original): → norm. Requereix warmup llarg i és inestable en profunditat.
- Pre-norm (l'estàndard des de GPT-2): . El camí residual queda net → entrenament molt més estable.
Pre-norm contra post-norm
On es posa la normalització dins d'un bloc residual, i què li passa al flux quan hi apiles trenta blocs?
- pre-norm
- post-norm
- √N, la predicció
La mateixa llavor dona sempre el mateix resultat.
- pre-norm · primer bloc
- 12,19
- pre-norm · bloc 32
- 79,30
- creixement
- 6,51×
- post-norm · primer bloc
- 8,00
- post-norm · bloc 32
- 8,00
- exponent mesurat
- 0,621 / -0,000
Amb pre-norma, : el flux residual no es toca mai, i cada bloc hi suma una contribució de mida controlada i gairebé independent de les anteriors. Sumar N vectors independents de mida semblant dona una norma que creix com , és a dir, exponent 0,5.
Amb post-norma, : la norma del flux es torna a fixar a cada bloc, de manera que no creix gens i l'exponent ha de ser zero.
| pre-norm · exponent ajustat | 0,621 | previst 0,5 |
| post-norm · exponent ajustat | -0,000 | previst 0 |
| creixement mesurat contra √N | 6,51× | √32 = 5,66 |
La diferència pràctica no és estètica. Amb post-norma, el gradient que torna cap a les primeres capes ha de travessar una normalització a cada bloc, i cadascuna el reescala: en profunditat, això és inestable i obliga a un warmup llarg. Amb pre-norma, el camí residual queda net d'extrem a extrem —és exactament la identitat del widget de propagació de gradients del fitxer 06— i l'entrenament aguanta desenes de blocs sense escalfament especial. El preu és aquest creixement de 6,51×, que es compensa escalant la inicialització de les capes de sortida dels blocs per i posant una norma final abans de la capa de sortida.
- Variants modernes: sandwich norm (norm abans i després del sublayer, com a Gemma 2/3) i QK-norm (normalitzar queries i keys abans del producte escalar) per evitar explosions de logits en models molt grans.
4. Regularització# sense fórmules
| Tècnica | Mecanisme | Ús en LLMs |
|---|---|---|
| Weight decay | penalitza | sí, ~0.1 |
| Dropout | esborra activacions amb prob. | rar en preentrenament (una sola època ≈ poc overfitting); sí en fine-tuning amb poques dades |
| Label smoothing | suavitza els one-hot | de vegades; millora calibratge |
| Data augmentation | més dades sintètiques | en LLMs: paràfrasis, reformulacions, dades sintètiques |
| Early stopping | atura al mínim de validació | crític al fine-tuning |
| z-loss | penalitza logits enormes | estabilitat numèrica a escala |
Regularització
Weight decay i dropout sobre un ajust que sobreajusta. I per què al preentrenament d'un LLM el dropout gairebé no s'usa?
- entrenament
- validació
- validació sense dropout
40 exemples d'entrenament amb soroll alt i una xarxa de 32×32: el sobreajust hi és garantit si la deixes córrer prou èpoques. L'error de validació mínim es marca sol a la corba taronja, i és on hauria d'aturar-se un early stopping.
Quantes vegades es veu cada exemple. Al preentrenament d'un LLM, una o dues.
Desacoblat, i sense tocar els biaixos: la màscara ho garanteix.
La mateixa llavor dona sempre el mateix resultat.
- pèrdua train
- 0,0096
- pèrdua validació
- 1,7699
- separació
- +1,7603
- norma dels pesos
- 24,73
- encongiment per decay
- 1,000×
- exemples vistos
- 40 × 120
| validació amb dropout 0,00 | 1,7699 |
| validació sense dropout | 1,7699 |
| guany del dropout amb 120 èpoques | +0,0000 |
| guany del dropout amb 1 època | +0,0000 |
Puja el dropout per sobre de zero i aquestes quatre xifres passaran a dir alguna cosa: ara mateix les dues primeres files són la mateixa corrida.
El weight decay sí que actua en tots dos règims: amb λ = 0,000, la norma dels pesos queda en 1,000× la de la mateixa corrida sense decay (24,73 contra 24,73). Al pas d'AdamW, el decay entra com θ ← θ − ηλθ per separat de l'actualització adaptativa, i la màscara el manté fora dels biaixos i dels paràmetres de norma.
5. Precisió numèrica# sense fórmules
| Format | Bits | Rang | Ús |
|---|---|---|---|
| FP32 | 32 | ampli | còpia mestra de pesos, acumuladors |
| BF16 | 16 | mateix exponent que FP32 | estàndard d'entrenament |
| FP16 | 16 | rang estret | necessita loss scaling |
| FP8 (e4m3/e5m2) | 8 | molt estret | entrenament de frontera i inferència a Hopper/Blackwell |
| INT8 / NF4 / MXFP4 | 8/4 | — | quantització per a inferència i QLoRA |
Formats de coma flotant
Per què bf16 ha desplaçat fp16 en entrenament, si fp16 té més precisió?
error 1,49·10⁻⁹ (1,49·10⁻⁸ relatiu) · ulp 7,45·10⁻⁹
error 9,77·10⁻⁵ (9,77·10⁻⁴ relatiu) · ulp 4,88·10⁻⁴
error 2,44·10⁻⁵ (2,44·10⁻⁴ relatiu) · ulp 6,10·10⁻⁵
error 0,002 (0,016 relatiu) · ulp 0,008
error 0,006 (0,063 relatiu) · ulp 0,016
Escriu-hi el que vulguis; també notació exponencial (1e5).
- has escrit
- 0,100000
- en FP16
- 0,099976
- error relatiu
- 2,44·10⁻⁴
- ulp aquí
- 6,10·10⁻⁵
- rang del format
- 6,10·10⁻⁵ – 65.504,000
- subnormal mínim
- 5,96·10⁻⁸
Reconstruint el valor des dels camps —(−1)^s · significand · 2^exponent— surt 0,09997559, i el valor representat és 0,09997559: difereixen en 0.
I per a fp32, on el maquinari sí que sap fer-ho, la descomposició es contrasta contra la que dona DataView.setFloat32: signe 0, exponent 123, mantissa 5033165 — idèntics.
Prova el botó de 10⁵: fp16 desborda i bf16 no se n'assabenta. Tots dos ocupen setze bits; el que canvia és com es reparteixen entre exponent i mantissa, i en entrenament el rang val més que la precisió.
Precisió mixta: forward/backward en bf16, acumulació i actualització de pesos en fp32. BF16 ha guanyat sobre FP16 perquè, tenint el mateix exponent que FP32, no necessita loss scaling i gairebé mai desborda.
FP8 en entrenament (amb escalat per blocs/tensors) és una de les palanques d'eficiència més importants dels últims anys: DeepSeek-V3 va demostrar entrenament FP8 a escala de frontera amb un cost molt inferior al d'un entrenament equivalent en BF16.
6. Diagnòstic: llegir corbes de pèrdua# sense fórmules
| Patró | Interpretació |
|---|---|
| Baixa suau i contínua | tot bé |
| Plateau inicial llarg i després caiguda | normal en LLMs ("fase de gramàtica" → "fase de fets") |
| Pic sobtat i recuperació | batch tòxic o inestabilitat; vigila si es repeteix |
| Pic sobtat i divergència | LR massa alt, overflow; reprèn des del checkpoint anterior amb LR menor o saltant aquestes dades |
| Train baixa, val puja | overfitting |
| Ambdues planes | LR massa baix, bug, o dades massa fàcils/difícils |
| Escalons periòdics | correlació amb l'ordre de les dades → barreja millor |
Tauler de mètriques
Quines sis coses has de registrar sempre, i quina et diu què passa quan la corba de pèrdua no ho diu?
trainvalidació
Un pic sobtat de pèrdua amb recuperació. El gradient explota en un sol pas.
Els casos de LR alt i baix el multipliquen i el divideixen per mantenir la comparació.
La mateixa llavor dona sempre el mateix resultat.
- passos
- 300
- pèrdua final (train)
- 0,1033
- pèrdua final (val)
- 0,2763
- norma del gradient màxima
- 474,911
- ràtio update/param (mediana)
- 0,003
- creixement dels pesos
- 1,18×
La mediana de ‖Δθ‖/‖θ‖ al llarg dels dos últims terços d'aquesta corrida és 0,003, contra el 10⁻³ que s'acostuma a donar per bo: dins de l'ordre de magnitud sa. La regla no surt de cap teorema —és una heurística que es compleix en una franja molt ampla de models i escales— però té la virtut que no depèn ni de la mida del model ni de la pèrdua, de manera que serveix per comparar corrides que no es podrien comparar per la pèrdua. El màxim al llarg de la corrida és 0,017, i la norma dels pesos ha crescut 1,18×.
Diagnòstic d'aquest cas: Si passa una vegada, el clipping ja ho ha absorbit. Si es repeteix sempre al mateix punt, el problema són les dades, no l’optimització: mira aquell tros del corpus.
Mira els sis panells i després torna al selector. Hi ha casos on la corba de pèrdua sola no els distingeix: "les dues corbes planes" pot ser un learning rate massa baix, un bug al forward o dades impossibles, i el que els separa és la ràtio update/param (el model es mou o no es mou) i la norma del gradient (arriba senyal o no n'arriba). Registrar només la pèrdua és el que fa que un entrenament de tres setmanes s'hagi de repetir.
Mètriques que has de registrar sempre: pèrdua (train i val), norma del gradient, norma dels pesos, LR actual, ràtio update/param, MFU (model FLOPs utilization), tokens/s, i mostres de generació cada N passos (la millor eina de diagnòstic qualitatiu que existeix).
7. Hiperparàmetres: com trobar-los sense arruïnar-te# sense fórmules
- Comença amb una recepta coneguda (la del model més semblant al teu).
- Prioritza: LR > mida del batch > schedule > weight decay > la resta.
- Escalat μP: parametritza el model perquè el LR òptim sigui invariant a l'amplada → busca hiperparàmetres en un model de 40M i transfereix-los al de 40B. És el que fa possible no cremar milions en sweeps.
- Lleis d'escala per a decisions: entrena 5–8 models petits, ajusta la corba i extrapola abans de fer la corrida grossa.
Transferència d'hiperparàmetres (μP)
Per què el learning rate que va bé en un model de 40M deixa d'anar bé en un de 40B, i què cal canviar perquè hi continuï anant?
- parametrització estàndard (pendent 1,41)
- μP (pendent 0,12)
L'escombrat encara no s'ha executat. Són 64 entrenaments complets de 40 passos: quatre amplades × 8 learning rates × dues parametritzacions.
Que costi un clic és tota la qüestió: a escala de frontera, això són milions d'euros de còmput, i μP és el que permet no gastar-los.
Només afecta el panell de dalt: el desplaçament dels logits hi és proporcional.
La mateixa llavor dona sempre el mateix resultat.
- amplada de referència
- 8
- exponent · estàndard
- 1,406
- exponent · μP
- 0,118
- Δlogits a amplada 256
- 5,141 / 0,056
El que μP vol que quedi invariant amb l'amplada no és el learning rate: és quant es mouen les sortides del model en un pas. Amb Adam, l'actualització de cada pes té mida ≈ η passi el que passi, de manera que el canvi d'un logit és una suma de termes de mida η i creix com : per mantenir-lo constant, el learning rate de les capes que reben l'amplada s'ha de dividir per . Això és exactament el que fa μP, i es pot mesurar en un sol pas.
| exponent amb la parametrització estàndard | 1,406 | previst 1 |
| exponent amb μP | 0,118 | previst 0 |
| de l'amplada 8 a la 256 (32×) | ×124,5 | ×1,3 amb μP |
La conseqüència pràctica és que, amb la parametrització estàndard, l'LR òptim es desplaça cap avall a mesura que el model creix, i amb μP es queda quiet. Executa l'escombrat per veure-ho: són 64 entrenaments complets, i aquí costen unes dècimes de segon. En un laboratori de debò, cada punt d'aquell gràfic és una corrida, i és per això que poder buscar hiperparàmetres en un model de 40M i fer-los servir en un de 40B val el que val.
8. Comprova que ho has entès#
0 de 6 resoltes
- Per què BF16 ha desplaçat FP16 en entrenament, si FP16 té més bits de mantissa? Quin és el valor finit més gran que FP16 pot representar?Marge acceptat: 0,1 %
- Quina diferència pràctica hi ha entre pre-norm i post-norm?
- Per què RMSNorm funciona igual de bé que LayerNorm sense centrar?
- Per què no s'aplica weight decay als paràmetres de normalització ni als biaixos?
- Tens una pèrdua que divergeix al pas 12.000 de 100.000. Quins tres diagnòstics fas, i en quin ordre?
- Reprodueix el cas canònic. Troba un número que FP16 desbordi i que BF16 representi sense problemes.
Objectiu:Escriu al widget de formats un valor per sobre de 65.504 i per sota del límit de BF16, i mira les dues files.
Vés al widget ↑x = per defecte
9. Recursos#
The Ultra-Scale Playbook (Hugging Face) — entrenament a escala, molt pràctic.
Zhang & Sennrich (2019), Root Mean Square Layer Normalization.
Xiong et al. (2020), On Layer Normalization in the Transformer Architecture (pre vs post norm).
Deep Learning Tuning Playbook (Google Research) — com fer experiments d'hiperparàmetres seriosament.
DeepSeek-V3 Technical Report — entrenament FP8 a escala real.