ELAI S.r.l.

IA de 8 bits: cuando la suma desborda el acumulador

Valores pequeños no garantizan sumas seguras. Límites INT32 de un producto escalar INT8, con punto cero, sesgo y sumas intermedias.

IA de 8 bits: cuando la suma desborda el acumulador

El modelo es pequeño, ¿y el resultado?

Imaginemos una red en un dispositivo embedded, un ordenador integrado en una máquina o sensor. Pesos y activaciones usan enteros de 8 bits y ocupan poco. Pero una neurona multiplica muchas parejas y suma productos. La suma va a un acumulador: una variable capaz de contener resultados mayores que los datos individuales. ¿Cómo saber antes de ejecutar si su rango basta?

Resumen. Partimos de un producto escalar cuantizado: suma de productos de códigos enteros que representan valores reales. Derivamos un límite conservador para acumulador de 32 bits con signo y lo superamos con entradas válidas de 8 bits. Distinguimos redondeo, saturación y desbordamiento; analizamos sesgo, pesos y orden. Son cálculos exactos y modelos aritméticos en Python, no medidas de microcontrolador ni pruebas de backend TensorFlow Lite.

El código entero todavía no es el valor

La cuantización afín representa x≈s(q−z): q es código entero, z representa cero real y s es escala positiva. Con z=−128, q=127 está a 255 pasos de cero. Restar dos valores válidos de 8 bits puede exceder el rango con signo de 8 bits. Hay que ampliar el tipo antes de restar. Almacenar compacto no implica operar siempre con igual anchura.

Usamos activaciones qₐ entre −128 y 127, pesos q𝑤 entre −127 y 127 y punto cero de pesos igual a cero, coherente con la especificación INT8 consultada de TensorFlow Lite. Activaciones son valores intermedios; pesos, coeficientes aprendidos. Para un canal de salida y escalas sₐ, s𝑤, A acumula antes de convertir al formato de salida.

A = Σᵢ₌₁ᴷ (qₐ,ᵢ − zₐ) q𝑤,ᵢ + b_int y ≈ sₐ s𝑤 A

K cuenta productos de esa salida. b_int es el término constante de la neurona en escala sₐs𝑤. A, códigos y sesgo entero son conteos sin unidad física; y tiene las unidades del modelo y escalas. Cambiar escala de pesos de un canal cambia la del sesgo. Estudiamos A; convertir después a INT8 añade redondeo y recorte, que no reparan una suma errónea.

¿Cuánto puede crecer una suma?

Un entero con signo de 32 bits va de −2.147.483.648 a 2.147.483.647. Para una garantía inicial ignoramos cancelaciones y acotamos valores absolutos. |qₐ−zₐ| llega a 255 y |q𝑤| a 127: cada producto vale como máximo 32.385 en módulo. La desigualdad triangular acota el módulo de la suma por la suma de módulos.

|A| ≤ K·32385 + |b_int| K ≤ floor((2147483647 − |b_int|) / 32385)

La segunda línea es condición suficiente, no necesaria, y supone margen no negativo tras el sesgo. Usamos límite positivo para ambos signos, renunciando al valor negativo extra. Sin sesgo, K≤66.311 garantiza la suma directa y sus parciales para toda entrada admitida. K=66.312 no falla siempre: deja de estar garantizado por esta cota universal.

Un caso que supera el límite

Tomamos activaciones 127, punto cero −128 y pesos 127: valores válidos con producto 32.385. Con 65.536 términos la suma 2.122.383.360 cabe en INT32. Con 66.311 es 2.147.481.735; un producto más da 2.147.514.120 y supera el máximo. No es pérdida gradual de decimales, sino imposibilidad de representar el entero.

KSuma exactaVuelta modular modeladaSaturación modelada
65536212238336021223833602122383360
66311214748173521474817352147481735
663122147514120−21474531762147483647
700002266950000−20280172962147483647

La tabla compara tres aritméticas calculadas explícitamente. Python conserva el entero exacto. La vuelta modular conserva los 32 bits bajos y los interpreta con signo: puede volver negativa una suma positiva. Saturar limita al máximo representable. Ninguna recupera la suma exacta. No atribuimos estos comportamientos a un kernel: hay que verificar contrato, instrucciones e implementación.

Suma exacta y vuelta INT32 modelada al crecer K. Eje vertical en miles de millones de conteos enteros, no tiempo ni energía. La discontinuidad es aritmética, no rendimiento de una placa.
Suma exacta y vuelta INT32 modelada al crecer K. Eje vertical en miles de millones de conteos enteros, no tiempo ni energía. La discontinuidad es aritmética, no rendimiento de una placa.

En C++ no debe suponerse que una suma ordinaria con signo fuera de rango dé vuelta modular: el borrador consultado define comportamiento indefinido para esa expresión aritmética. Instrucciones de acelerador pueden tener otras reglas. Usamos enteros Python y fórmula modular explícita, sin provocar overflow nativo y confundir una observación con regla portable.

Sesgo y pesos cambian el margen

Un sesgo de 2.000.000.000 cabe en INT32, pero consume gran parte del margen positivo: quedan 4.554 productos extremos garantizados. Comprobar tipos por separado no basta; hay que acotar su suma. Con punto cero de activaciones cero, el máximo producto absoluto baja a 128·127=16.256 y la cota sin sesgo sube a 132.104 términos. Geometría de capa y cuantización se analizan juntas.

La cota global suele ser excesiva con pesos conocidos. Para peso fijo wᵢ y activación entre lᵢ y uᵢ, calculamos productos extremos y tomamos mínimo y máximo. Un peso negativo invierte el extremo mayor. Sumar mínimos y máximos encierra todas las sumas, incluso si ciertas entradas no coexisten. La garantía depende de intervalos válidos; no predice frecuencia de errores.

Lᵢ = min((lᵢ−zₐ)wᵢ, (uᵢ−zₐ)wᵢ) Uᵢ = max((lᵢ−zₐ)wᵢ, (uᵢ−zₐ)wᵢ) b_int + Σᵢ Lᵢ ≤ A ≤ b_int + Σᵢ Uᵢ

Con pesos (127,−127,64,−64), zₐ=−128 y rango INT8 completo, los intervalos son [0,32385], [−32385,0], [0,16320], [−16320,0]. Sin sesgo, A queda en [−48705,48705]; la cota global |A|≤129540 es válida pero imprecisa. Usamos coeficientes reales y todas las entradas, no promedios ni esperanza de cancelación. Debe analizarse también cada suma parcial del programa.

Una suma final válida no protege pasos intermedios

Un kernel puede reescribir A como Σqₐq𝑤−zₐΣq𝑤+b_int. La equivalencia algebraica no elimina almacenamiento intermedio. Con K=150.000, qₐ=zₐ=−128 y q𝑤=127, cada término centrado y A son cero. Expandidos, los aportes son −2.438.400.000 y +2.438.400.000: ninguno cabe en INT32. No demuestra defecto de una biblioteca, sino necesidad de seguir el cálculo implementado, incluidas correcciones precalculadas.

Saturar cada adición cambia el problema. En 8 bits, 100+100−100 vale exactamente 100; saturando pasos obtenemos 100,127,27. Reordenar 100−100+100 devuelve 100. Ilustra dependencia del orden, no recomienda acumular redes en 8 bits. La aritmética modular pura conserva sumas y restas módulo la base: no debe atribuirse al wraparound el comportamiento de saturación.

¿Qué alternativa resuelve cada problema?

Ampliar a 64 bits aumenta margen si productos, correcciones y conversiones usan tipos adecuados. No inferimos ciclos ni energía sin medir plataforma. Dividir en bloques limita sumas locales, pero aún deben combinarse sin overflow. Reescalar y redondear cada bloque introduce otro error que exige análisis. Cambiar precisión, escalas o arquitectura puede ayudar, pero cambia el compromiso numérico y requiere evaluar calidad del modelo.

Una futura prueba debe identificar capa y canal, extraer pesos, sesgo, escalas y puntos cero reales, reconstruir K y aritmética del kernel, comparar referencia precisa con casos ordinarios y extremos, y registrar biblioteca, compilador, opciones y hardware. Aquí solo ejecutamos referencia matemática. No informamos latencia, RAM pico, potencia ni energía por inferencia porque no se midieron en placa.

Respuesta: contar bits de datos no basta

Un modelo INT8 puede necesitar sumas mucho más amplias que sus entradas. Se verifican número de productos, rangos tras restar punto cero, sesgo y pasos intermedios. La cota conservadora garantiza; analizar pesos la ajusta; inspeccionar kernel confirma que describe el programa. Convertimos una pregunta vaga sobre precisión en condiciones verificables antes de publicar, sin sospechar de toda red cuantizada.

Fuentes y programa reproducible

TensorFlow — TensorFlow Lite 8-bit quantization specification, definitions and CONV_2D requirements.

C++ working draft — Expressions, arithmetic range and evaluation rules.

Las fuentes definen representación y semántica; cotas, contraejemplos y figuras se derivaron y ejecutaron para esta monografía. El código encuentra el último K garantizado y compara tres reglas. Es determinista, sin semilla. El archivo incluye sesgo, intervalos por peso, cancelación expandida y saturación. No son resultados de producto embedded EL-AI ni investigación original revisada por pares.

LIMIT = 2**31 - 1
product = (127 - (-128)) * 127
safe_k = LIMIT // product
for k in (safe_k, safe_k + 1, 70000):
    exact = k * product  # Python arbitrary-precision integer
    wrapped = (exact + 2**31) % 2**32 - 2**31
    saturated = min(LIMIT, max(-2**31, exact))
    print(k, exact, wrapped, saturated)
# Explicit mathematical models, NOT execution of an embedded kernel.

Código, datos e instrucciones · JSON. Cálculos didácticos ejecutados con Python 3.14.0; figuras con Matplotlib 3.11.2. Análisis asistido por IA, sin afirmar revisión por pares ni humana. Portada original ImageGen ilustrativa: no documenta personas, sedes ni instalaciones de EL-AI. Fuentes consultadas el 29 de septiembre de 2026.