Elon Litman publicó el análisis el 20 de octubre de 2026 en su blog Elements of a Vector Space. Partió de una duda sobre la aritmética de coma flotante: ¿puede una pérdida de PyTorch devolver `0.0` mediante `loss.item()` y cambiar los pesos después de `backward()`? Su construcción obtiene gradientes controlados a partir de un término auxiliar invisible. El artículo presenta el método como una forma de ejecutar una máquina de estados Turing-completa o preparar una carga maliciosa en los pesos sin dejar una señal de pérdida llamativa.

PyTorch zero-loss core
import torch
import torch.optim as optim

def phantom_identity(u, C=2**30):
    return (C + u) - C

def trojan_zero_loss(theta, target_transition_fn):
    F_theta = target_transition_fn(theta)
    v = theta - F_theta
    return torch.sum((v - phantom_identity(v)) * phantom_identity(theta))

def rotate_cycle(theta):
    with torch.no_grad():
        x, y = theta[0], theta[1]
        return torch.stack([-y, x])

El escenario de amenaza parte de un modelo de código abierto que se ajusta para clasificar datos. Un equipo puede comparar el SHA-256 de un archivo `.safetensors` con el valor publicado en la página de Llama de Meta en Hugging Face, revisar `modeling_llama.py` y vigilar el entrenamiento en TensorBoard. Después, un paquete malicioso podría ofrecer `UltraLossRegularizer` como regularizador espectral para modelos cuantizados a 8 bits. El paquete no tendría que modificar el checkpoint, la arquitectura, los datos ni el optimizador. `reg_fn` podría dejar los parámetros intactos durante el pase hacia delante, y un `aux_loss` registrado por separado podría mostrar `0.000000` en todas las iteraciones. Las llamadas normales a `total_loss.backward()` y `optimizer.step()` ejecutarían aun así la actualización oculta.

La técnica usa una parte de los pesos como vector de estado `theta`. Una función de transición `F(theta)` calcula el siguiente estado, por ejemplo, la generación siguiente del Juego de la vida de Conway. Con SGD y una tasa de aprendizaje de 1, el gradiente deseado es `v(theta) = theta - F(theta)`. Al restarlo, el estado actual se convierte en `F(theta)`. Para otra tasa de aprendizaje, el vector se divide por esa tasa. Cada componente debe tener un valor absoluto inferior a 64, salvo que se use una constante de redondeo mayor.

El truco numérico es `h(u) = (230 + u) - 230`. A esa escala, dos valores float32 representables consecutivos están separados por 128. Un valor menor que la mitad de esa distancia, como `u = 1`, desaparece al sumarse a `230`, por lo que `h(u)` produce `0.0` en el pase hacia delante. Un float32 contiene un bit de signo, un exponente de 8 bits y una mantisa de 23 bits. El mismo principio funciona con otra constante en formatos de menor precisión. Para bfloat16, el artículo propone `C = 29`, con una separación de 4 y una pérdida de los valores cuyo módulo sea menor que 2.

Autograd no vuelve a examinar el resultado redondeado por el hardware. Deriva las operaciones registradas y asigna una derivada de 1 a la suma y la resta de `h(u)`. Por eso la derivada simbólica sigue siendo 1 aunque el valor hacia delante sea cero. Litman usa después `u - h(u)`. La expresión transmite `u` hacia delante, pero su derivada es cero. El efecto se parece a un `.detach()` oculto.

La pérdida multiplica esa rama por `h(theta_i)` y suma los resultados de los pesos seleccionados. Si `theta_i` y `F_i(theta)` son binarios, `v_i` solo puede ser `-1`, `0` o `1`. El pase hacia delante ve `v_i` en el primer factor y cero en el segundo, así que la pérdida registrada permanece en cero. Sobre los números reales, la expresión también vale cero, porque `h(u)` sería igual a `u` y el primer factor se anularía.

La multiplicación crea la diferencia entre ambos pases. Autograd guarda los valores hacia delante `A_i = v_i` y `B_i = 0.0` y aplica la regla del producto durante el retroceso. La derivada de la primera rama es cero, la de la segunda es 1 y el gradiente resultante es `v_i`. La función de transición no introduce gradientes adicionales en los pesos. SGD sustituye así `theta` por `F(theta)`, y las pequeñas diferencias enteras se representan sin error en float32.

Litman incorporó una regla de vecindad 3 × 3 de Conway y ejecutó un pulsar de periodo 3, un oscilador de periodo 15 y un planeador sobre un toro. Después aplicó el término a 1.024 pesos de una capa densa oculta dentro de una CNN de 1,6 millones de parámetros. La red se entrenó con entropía cruzada estándar sobre MNIST durante cinco épocas y 2.345 pasos de SGD. La pérdida auxiliar fue cero en todos los pasos. La precisión de prueba llegó al 98,80 %, frente al 98,84 % de una línea base sin modificar. Los 1.024 pesos ejecutaron 2.345 generaciones del Juego de la vida sin errores en las células.

El artículo incluye una máquina de estados de PyTorch con dos parámetros. Comienza en `[1, 0]` y, con una tasa de aprendizaje de 1, recorre `[0, 1]`, `[-1, 0]`, `[0, -1]` y vuelve a `[1, 0]`. La pérdida mostrada es `0.000000` en cada paso. Los gradientes construidos son `[1, -1]`, `[1, 1]`, `[-1, 1]` y `[-1, -1]`.

Litman propone varias formas de ocultar la transición. Una función llamada `_manifold_curvature_penalty` puede usar `F.conv2d`, `KERNEL_EIGENVECTORS` y dos llamadas a `torch.clamp` con umbrales 2.0 y 4.0. En una revisión de código, la operación puede parecer un regularizador geométrico aunque calcule un autómata celular. Los valores del kernel y el factor `2**30` pueden guardarse en `empirically_tuned_layer_coefficients.json`, con una documentación que los atribuya a una búsqueda bayesiana de hiperparámetros.

Una extensión CUDA compilada oculta todavía más la lógica. Paquetes como `flash-attn` y `bitsandbytes` ya distribuyen bibliotecas `.so` precompiladas para arquitecturas GPU concretas. Un envoltorio de Python puede importar `ultraloss_cuda_backend` y exponer `UltraLossRegularizer(nn.Module)`. Su método forward llama a `compute_spectral_penalty(params)`. La función de transición, el truco de redondeo y la resta residual pueden quedar integrados en PTX y SASS en lugar de aparecer en el código Python.

Las consecuencias de seguridad no se limitan a SGD. Como el Juego de la vida de Conway es Turing-completo, los pesos pueden actuar como memoria y los pasos del optimizador como reloj. Para Adam, Litman menciona una máquina de dos contadores de Minsky, un acumulador continuo que avance en `+eta` o `-eta`, y un reloj distribuido en varios ciclos. Una transición puede repartirse entre 100 o 1.000 iteraciones. Los entrenamientos modernos suelen ofrecer decenas o cientos de miles de pasos.

La carga no necesita interferir con los pesos que usa la tarea activa. Llama 3 tiene 128.256 tokens de vocabulario, y decenas de miles pueden no aparecer en un conjunto de datos concreto de fine-tuning. Los routers de modelos Mixture-of-Experts pueden dejar expertos sin seleccionar en un batch. Las filas inactivas de `embed_tokens.weight` podrían servir de memoria hasta la generación `N`. Después, la función de transición podría escribir la carga terminada en una proyección de atención activa o en una cabeza de clasificación.

El método supera dos defensas habituales. Los escáneres estáticos buscan firmas en el checkpoint antes del entrenamiento, pero el ejemplo parte de un checkpoint oficial verificado mediante hash y construye la carga más tarde. Las sandbox de contenedores bloquean la red, el sistema de archivos y los subprocesos. El cálculo descrito permanece, sin embargo, dentro de kernels normales de tensores GPU y no necesita invocar un shell ni el sistema operativo.

La parte final examina el comportamiento de los compiladores. `torch.compile`, XLA o Triton podrían simplificar algebraicamente `u - ((C + u) - C)` hasta cero y eliminar el efecto. Si conservan el orden de las operaciones, el mecanismo permanece. La simplificación no es inocua en software numérico, porque la suma compensada, la transformación TwoSum sin error de Knuth y la aritmética double-double dependen del redondeo de coma flotante. Por eso opciones como `-ffast-math` no suelen estar activadas por defecto en código numérico. Tratar los flotantes como números reales puede dañar los cálculos de solucionadores de EDP, dinámica molecular, mecánica orbital y modelos climáticos. El análisis sitúa la dificultad en la frontera entre la semántica IEEE 754 y la diferenciación automática simbólica, tanto para las auditorías de seguridad como para la optimización de compiladores.