Determinismo como opción del perfil, para poder verificar la reanudación
En GPU dos corridas idénticas no dan los mismos pesos: el orden de reducción de los kernels varía. Medido en la RTX 2060, dos corridas iguales de 20 pasos difieren hasta 8,6e-4. Eso hacía imposible comprobar el criterio de aceptación más importante del entrenamiento — que reanudar reproduzca la corrida — porque cualquier diferencia se confundía con el ruido de la placa. Con deterministic activado, la reanudación resulta exacta en los 47 parámetros: el checkpoint captura todo el estado. Queda como opción del perfil y como una config propia, en vez de un conjuro de shell que hay que recordar. La opción rechaza convivir con la compilación, porque el autotune vuelve a elegir kernels distintos entre corridas y reintroduce justo lo que se quería eliminar. Está apagada en producción: cuesta rendimiento. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
|||||||
|
# Verifica que reanudar desde un checkpoint reproduzca la corrida bit a bit.
|
||||||
|
#
|
||||||
|
# Es el criterio de aceptación más importante del entrenamiento: una reanudación
|
||||||
|
# que no es exacta reentrena sobre datos ya vistos y arruina la corrida en
|
||||||
|
# silencio. En GPU no se puede comprobar sin determinismo, porque dos corridas
|
||||||
|
# idénticas ya difieren entre sí por el orden de reducción de los kernels.
|
||||||
|
#
|
||||||
|
# No sirve para entrenar: es lento a propósito.
|
||||||
|
include:
|
||||||
|
hardware: hardware/turing-2060.yaml
|
||||||
|
model: model/char-smoke.yaml
|
||||||
|
train: train/smoke.yaml
|
||||||
|
data: data/smoke.yaml
|
||||||
|
|
||||||
|
hardware:
|
||||||
|
micro_batch_size: 64
|
||||||
|
grad_accum_steps: 1
|
||||||
|
deterministic: true
|
||||||
|
compile: false # el autotune reintroduce variación entre corridas
|
||||||
|
|
||||||
|
train:
|
||||||
|
run_name: verificar-reanudacion
|
||||||
|
max_steps: 20
|
||||||
|
checkpoint_every: 10
|
||||||
|
log_every: 999
|
||||||
|
eval_every: 999
|
||||||
|
sample_every: 0
|
||||||
|
schedule:
|
||||||
|
kind: wsd
|
||||||
|
warmup_steps: 2
|
||||||
|
decay_steps: 2
|
||||||
|
min_lr_ratio: 0.0
|
||||||
@@ -40,6 +40,16 @@ class HardwareConfig(_Base):
|
|||||||
compile: bool = True
|
compile: bool = True
|
||||||
matmul_precision: Literal["highest", "high", "medium"] = "high"
|
matmul_precision: Literal["highest", "high", "medium"] = "high"
|
||||||
|
|
||||||
|
# Determinismo bit a bit. Apagado en producción a propósito: cuesta
|
||||||
|
# rendimiento y obliga a desactivar la compilación.
|
||||||
|
#
|
||||||
|
# Existe porque en GPU dos corridas idénticas NO dan los mismos pesos — el
|
||||||
|
# orden de reducción de los kernels varía. Medido en la RTX 2060, dos
|
||||||
|
# corridas iguales de 20 pasos difieren hasta 8,6e-4. Sin esto es imposible
|
||||||
|
# verificar que la reanudación sea exacta: cualquier diferencia se confunde
|
||||||
|
# con el ruido de la placa.
|
||||||
|
deterministic: bool = False
|
||||||
|
|
||||||
# El tamaño de lote es una propiedad de la placa, no del modelo: 12 GB y
|
# El tamaño de lote es una propiedad de la placa, no del modelo: 12 GB y
|
||||||
# 32 GB no admiten lo mismo. El lote efectivo es micro_batch * grad_accum.
|
# 32 GB no admiten lo mismo. El lote efectivo es micro_batch * grad_accum.
|
||||||
micro_batch_size: int = Field(gt=0)
|
micro_batch_size: int = Field(gt=0)
|
||||||
@@ -59,6 +69,17 @@ class HardwareConfig(_Base):
|
|||||||
def effective_batch_size(self) -> int:
|
def effective_batch_size(self) -> int:
|
||||||
return self.micro_batch_size * self.grad_accum_steps
|
return self.micro_batch_size * self.grad_accum_steps
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def _check_determinismo(self) -> HardwareConfig:
|
||||||
|
# El autotune de torch.compile elige kernels distintos entre corridas,
|
||||||
|
# así que reintroduce el no-determinismo que se quiso eliminar.
|
||||||
|
if self.deterministic and self.compile:
|
||||||
|
raise ValueError(
|
||||||
|
f"perfil '{self.name}': deterministic=true exige compile=false "
|
||||||
|
"(el autotune de la compilación vuelve a hacer variar los kernels)."
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def _check_dtype_scaler(self) -> HardwareConfig:
|
def _check_dtype_scaler(self) -> HardwareConfig:
|
||||||
# float16 sin GradScaler diverge; bfloat16 con GradScaler es un
|
# float16 sin GradScaler diverge; bfloat16 con GradScaler es un
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ conviene arreglarla acá en vez de propagarla.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from enlace.config.schema import HardwareConfig
|
from enlace.config.schema import HardwareConfig
|
||||||
@@ -100,9 +102,24 @@ def setup_device(cfg: HardwareConfig) -> torch.device:
|
|||||||
)
|
)
|
||||||
|
|
||||||
torch.set_float32_matmul_precision(cfg.matmul_precision)
|
torch.set_float32_matmul_precision(cfg.matmul_precision)
|
||||||
|
if cfg.deterministic:
|
||||||
|
_activar_determinismo()
|
||||||
return torch.device("cuda")
|
return torch.device("cuda")
|
||||||
|
|
||||||
|
|
||||||
|
def _activar_determinismo() -> None:
|
||||||
|
"""Fuerza kernels deterministas, para poder verificar la reanudación.
|
||||||
|
|
||||||
|
`CUBLAS_WORKSPACE_CONFIG` se define acá y no en el shell porque cuBLAS lo
|
||||||
|
lee al inicializarse, y en este punto todavía no se tocó la placa. Definirlo
|
||||||
|
después no tiene efecto y el error que da es opaco.
|
||||||
|
"""
|
||||||
|
os.environ.setdefault("CUBLAS_WORKSPACE_CONFIG", ":4096:8")
|
||||||
|
torch.use_deterministic_algorithms(True)
|
||||||
|
torch.backends.cudnn.deterministic = True
|
||||||
|
torch.backends.cudnn.benchmark = False
|
||||||
|
|
||||||
|
|
||||||
def autocast_context(cfg: HardwareConfig, device: torch.device):
|
def autocast_context(cfg: HardwareConfig, device: torch.device):
|
||||||
"""Contexto de precisión mixta acorde al perfil."""
|
"""Contexto de precisión mixta acorde al perfil."""
|
||||||
if cfg.dtype == "float32":
|
if cfg.dtype == "float32":
|
||||||
|
|||||||
Reference in New Issue
Block a user