Cuantización de vectores comparada: int8 frente a binaria en recall y almacenamiento
Mide cómo cambian el recall y el almacenamiento de los embeddings con la cuantización int8 y la binaria, por qué la binaria puede hundirse y cómo elegir un nivel de compresión seguro.
Reducir el tamaño de los embeddings suena a dinero gratis, y muchas guías se quedan ahí. Una prueba rápida demuestra que la compresión "segura" y la "temeraria" están mucho más lejos una de otra de lo que esas guías reconocen.
Lo esencial es esto. La cuantización int8 suele conservar el 95% o más de la precisión de recuperación y reduce el almacenamiento a la cuarta parte. La cuantización binaria ofrece una reducción espectacular de 32 veces, pero salvo que el modelo de embeddings se haya entrenado para ello, puedes ver desaparecer un tercio o más de esa precisión. No son dos puntos de una misma escala. Se comportan de forma tan distinta que elegir la equivocada para tu modelo puede arruinar en silencio la calidad de la búsqueda.
La cuantización consiste en guardar cada dimensión con menos precisión. Pasas de floats de 32 bits a enteros de 8 bits (cuantización escalar), o directamente a un bit por dimensión (cuantización binaria). El ahorro es real: el uso de RAM baja hasta 24 veces con la binaria y 3,75 veces con la escalar, y el efecto crece cuando el corpus supera los millones de vectores, algo cada vez más habitual en la memoria de agentes y en el RAG empresarial de 2026.
Cómo son los números
El corpus de prueba era sintético: 2.000 vectores de 768 dimensiones, agrupados en 20 clústeres poco definidos para imitar una estructura semántica real. La precisión de recuperación se midió como recall@10 frente a la referencia en float32 sin comprimir, tras aplicar cada uno de los dos cuantizadores. Primero int8, que escala por el mayor valor absoluto y redondea al rango de int8:
def quantize_int8(vecs):
scale = np.max(np.abs(vecs))
q = np.round(vecs / scale * 127).astype(np.int8)
return q.astype(np.float32) / 127 * scale
Y la binaria, que no guarda nada más que el signo de cada valor:
def quantize_binary(vecs):
return (vecs > 0).astype(np.float32) * 2 - 1
Los resultados fueron estos:
Format | Recall@10 vs float32 | Storage (MB) | Reduction
----------------------------------------------------------------------
float32 | 1.000 (baseline) | 6.144 | 1x
int8 | 0.961 | 1.536 | 4.0x
binary | 0.326 | 0.192 | 32.0x
Int8 conservó el 96% de la precisión de recuperación con un almacenamiento cuatro veces menor, un buen trato para la mayoría de los sistemas en producción. La binaria, sobre esta simulación de embeddings genéricos que no se diseñaron pensando en la cuantización, cayó a un 32,6% de recall pese a su llamativa compresión de 32 veces. No es un error de redondeo. Aproximadamente dos de cada tres resultados relevantes que habría devuelto el índice en float desaparecen.
Por qué la binaria se rompe mucho más
Todo depende de cuánta información sobrevive. Int8 guarda un número con buena granularidad para cada dimensión, resolución suficiente para preservar la mayor parte de la geometría relativa entre vectores. La binaria guarda solo el signo y descarta por completo la magnitud, una simplificación del espacio de embeddings mucho más brusca.
Aun así, no es una ley de la naturaleza. Depende del modelo. Pruebas independientes han visto que algunas arquitecturas de embeddings apenas notan la cuantización binaria y mantienen alrededor del 98% de la calidad en float, mientras que otras familias se resienten más y se quedan cerca del 87-92%, según el tamaño del modelo. Lo que las separa es si el modelo se entrenó para aguantar la cuantización.
Cuál elegir
- Usa int8 por defecto. Cedes un poco de precisión de una forma fácil de prever, y el espacio que ahorras sigue siendo lo bastante grande como para notarse en un índice grande.
- Usa la binaria solo con un modelo pensado para ella. Los proveedores ya publican variantes de embeddings preparadas para la cuantización precisamente porque los embeddings genéricos se degradan mucho con la compresión binaria. Comprueba que tu modelo concreto está validado para ello antes de desplegar, y no des por hecho que la técnica es segura en cualquier caso.
- Considera el truncado de dimensiones al estilo Matryoshka como vía intermedia. Algunos modelos recientes se pueden recortar a menos dimensiones (de 2.048 a 256, por ejemplo) y conservar la mayor parte de su calidad de recuperación, lo que te da un compromiso ajustable entre el ahorro modesto de int8 y el agresivo pero arriesgado de la binaria.
- Mide primero el recall en tu propio corpus. Lo agrupados que estén tus embeddings reales y cuántos resultados casi duplicados toleren tus consultas moverán estas cifras en un sentido u otro respecto a lo que diga cualquier benchmark genérico, este incluido.
Preguntas frecuentes
¿Cuánta precisión cuesta int8? Normalmente muy poca, por lo general menos de un 5% de recall perdido frente al float32 completo, a cambio de un almacenamiento unas cuatro veces menor.
¿Es segura la cuantización binaria? Solo con modelos de embeddings entrenados para tolerarla. Los modelos genéricos pueden perder un tercio o más de la precisión de recuperación, mientras que los preparados para la cuantización pueden conservar el 98% o más de la calidad en float.
¿Cuánto almacenamiento se ahorra? Unas cuatro veces con int8. La binaria puede llegar a 24-32 veces, con un coste en precisión mucho mayor y menos previsible.
¿Hay que volver a generar todos los embeddings? No necesariamente. Puedes cuantizar a posteriori los embeddings en float32 que ya tienes, como hace el código de arriba. Eso sí, los modelos entrenados pensando en la compresión suelen superar a un embedding estándar cuantizado después.
Una última advertencia: el corpus de este artículo es sintético y está estructurado en clústeres, solo a modo de ilustración. El recall real depende de tu modelo de embeddings, de la estructura de tus datos y de tus patrones de consulta, así que haz pruebas con tus propios datos antes de decidir un nivel de compresión.