<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cuantización on kenji.blog</title><link>http://kenji.blog/es/tags/cuantizaci%C3%B3n/</link><description>Recent content in Cuantización on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 00:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/cuantizaci%C3%B3n/index.xml" rel="self" type="application/rss+xml"/><item><title>Explicación del mecanismo de la tecnología de cuantización (GGUF) de llama.cpp</title><link>http://kenji.blog/es/p/llama-cpp-quantization-gguf/</link><pubDate>Fri, 11 Sep 2026 00:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/llama-cpp-quantization-gguf/</guid><description>&lt;img src="http://kenji.blog/p/llama-cpp-quantization-gguf/img/eyecatch.jpg" alt="Featured image of post Explicación del mecanismo de la tecnología de cuantización (GGUF) de llama.cpp" />&lt;h2 id="1-introducción-por-qué-los-llm-necesitan-cuantización">1. Introducción: ¿Por qué los LLM necesitan cuantización?
&lt;/h2>&lt;p>El reciente avance de los modelos de lenguaje grande (LLM: Large Language Models) ha sido notable, pero detrás de escena han surgido problemas graves como el &amp;ldquo;agotamiento de recursos de cómputo&amp;rdquo; y el &amp;ldquo;cuello de botella del ancho de banda de memoria&amp;rdquo;. Por ejemplo, si se carga en memoria un modelo de 70B (70 mil millones) de parámetros como Llama 3 utilizando el estándar de coma flotante de 16 bits (FP16), solo los parámetros consumirán unos 140 GB de VRAM/RAM. Si a esto le sumamos el contexto durante la inferencia (caché KV), el modelo no podrá funcionar sin agrupar en un clúster múltiples GPU de gama alta para centros de datos (como NVIDIA A100 de 80 GB o H100 de 80 GB).&lt;/p>
&lt;p>Como salvador para ejecutar LLMs en dispositivos edge (como MacBooks o PCs de gaming comunes) y para desarrolladores individuales, apareció &lt;strong>llama.cpp&lt;/strong> y su tecnología principal, la &lt;strong>cuantización (Quantization)&lt;/strong>. En particular, el formato de archivo &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong> y el avanzado algoritmo de cuantización por bloques llamado &lt;strong>k-quants&lt;/strong> son métodos innovadores que comprimen el tamaño del modelo a una fracción de su original, minimizando al máximo la degradación en la precisión (Perplejidad) del mismo.&lt;/p>
&lt;p>En este artículo, explicaremos exhaustivamente desde los fundamentos matemáticos de la cuantización en llama.cpp, la diferencia con el formato GGML, la estructura detallada del formato GGUF, hasta los mecanismos internos de k-quants.&lt;/p>
&lt;hr>
&lt;h2 id="2-fundamentos-matemáticos-de-la-cuantización-quantization">2. Fundamentos matemáticos de la cuantización (Quantization)
&lt;/h2>&lt;p>En el contexto de los LLM, la cuantización se refiere a la operación de mapear valores continuos (o números de coma flotante de alta precisión) a valores discretos representados con menos bits (INT8, INT4, INT3, etc.).&lt;/p>
&lt;h3 id="21-fórmulas-básicas-de-la-cuantización-lineal">2.1. Fórmulas básicas de la cuantización lineal
&lt;/h3>&lt;p>El enfoque más simple es la cuantización lineal (cuantización Min-Max). Sea $W$ el tensor de pesos original de alta precisión y $W_q$ el tensor de enteros cuantizado.&lt;/p>
$$ W_q = \text{round}\left( \frac{W}{S} \right) + Z $$
&lt;p>Donde:&lt;/p>
&lt;ul>
&lt;li>$S$ es el &lt;strong>Factor de escala (Scale Factor)&lt;/strong>, que determina el tamaño del paso de cuantización (resolución).&lt;/li>
&lt;li>$Z$ es el &lt;strong>Punto cero (Zero-point)&lt;/strong>, un valor de sesgo (bias) para desplazar a qué valor entero cuantizado corresponde el número real $0.0$.&lt;/li>
&lt;li>$\text{round}(\cdot)$ es la función de redondeo al entero más cercano.&lt;/li>
&lt;/ul>
&lt;p>Mediante la descuantización (Dequantization), se restaura el peso real aproximado $\tilde{W}$ durante la inferencia.&lt;/p>
$$ \tilde{W} = S \times (W_q - Z) $$
&lt;h3 id="22-cuantización-simétrica-vs-cuantización-asimétrica">2.2. Cuantización simétrica vs Cuantización asimétrica
&lt;/h3>&lt;p>Dependiendo del tratamiento del punto cero $Z$, se divide principalmente en dos métodos:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Cuantización asimétrica (Asymmetric Quantization)&lt;/strong>
Mapea utilizando el valor mínimo $W_{\min}$ y el valor máximo $W_{\max}$ de los datos.
&lt;/p>
$$ S = \frac{W_{\max} - W_{\min}}{2^b - 1}, \quad Z = \text{round}\left(-\frac{W_{\min}}{S}\right) $$
&lt;p>
Donde $b$ es el número de bits de cuantización (ej: $2^4-1 = 15$ para 4 bits). Debido a que es necesario almacenar $Z$, la sobrecarga de cálculo y de memoria aumenta ligeramente.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Cuantización simétrica (Symmetric Quantization)&lt;/strong>
Mapea los valores alrededor del cero utilizando el valor máximo absoluto de los datos ($Z=0$).
&lt;/p>
$$ S = \frac{\max(|W_{\max}|, |W_{\min}|)}{2^{b-1} - 1}, \quad Z = 0 $$
&lt;p>
Las versiones iniciales de la cuantización de llama.cpp (por ejemplo, el formato heredado Q4_0) adoptaron la cuantización simétrica. La falta del término $Z$ tiene la gran ventaja de acelerar significativamente el cálculo del producto escalar con instrucciones SIMD.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-evolución-de-ggml-a-gguf-y-estructura-de-archivos">3. Evolución de GGML a GGUF y estructura de archivos
&lt;/h2>&lt;p>Al hablar de llama.cpp, es imprescindible mencionar la biblioteca de operaciones con tensores escrita en C++, &lt;strong>GGML&lt;/strong>, y el formato de archivo derivado de ella, &lt;strong>GGUF&lt;/strong>.&lt;/p>
&lt;h3 id="31-problemas-de-ggml">3.1. Problemas de GGML
&lt;/h3>&lt;p>Las versiones iniciales de llama.cpp usaban el formato &lt;code>ggml&lt;/code> (y variantes como &lt;code>ggjt&lt;/code>). Sin embargo, estos presentaban los siguientes problemas:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Falta de extensibilidad:&lt;/strong> Los números mágicos y los hiperparámetros estaban codificados rígidamente (hardcoded) en un orden y longitud fijos. Cada vez que se agregaba una nueva arquitectura de modelo (ej: Llama, Falcon, Mixtral, etc.) o un nuevo tokenizador, se producían cambios destructivos (breaking changes).&lt;/li>
&lt;li>&lt;strong>Pérdida de retrocompatibilidad:&lt;/strong> El formato se actualizaba con tanta frecuencia que los archivos de modelos antiguos no se podían leer en las versiones recientes de llama.cpp de forma recurrente.&lt;/li>
&lt;/ul>
&lt;h3 id="32-nacimiento-del-formato-gguf">3.2. Nacimiento del formato GGUF
&lt;/h3>&lt;p>Introducido en agosto de 2023, &lt;strong>GGUF&lt;/strong> es un formato altamente versátil diseñado para resolver estos problemas. Su característica más importante es la adopción de una &lt;strong>estructura de metadatos basada en clave-valor (Key-Value)&lt;/strong>.&lt;/p>
&lt;p>El siguiente diagrama Mermaid abstrae la estructura de un archivo GGUF.&lt;/p>
&lt;div class="mermaid">graph TD
A["Archivo GGUF"] --> B["Cabecera (Magia, Versión)"]
A --> C["Metadatos (Pares Clave-Valor)"]
A --> D["Info del Tensor (Nombre, Forma, Desplazamiento)"]
A --> E["Datos del Tensor (Carga útil binaria)"]
C --> C1["general.architecture: llama"]
C --> C2["llama.context_length: 4096"]
C --> C3["tokenizer.ggml.tokens: [...]"]
E --> E1["Pesos de la Capa 0"]
E --> E2["Pesos de la Capa 1"]
E --> E3["..."]&lt;/div>
&lt;p>&lt;strong>Principales ventajas de GGUF:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Flexibilidad:&lt;/strong> Todos los hiperparámetros del modelo, la configuración de RoPE (Rotary Positional Embedding), los datos del vocabulario del tokenizador, etc., se almacenan como pares Clave-Valor con nombre. Las claves desconocidas se ignoran, lo que facilita agregar nuevas funciones.&lt;/li>
&lt;li>&lt;strong>Independencia del Endianness:&lt;/strong> GGUF adopta Little Endian de forma predeterminada, pero incluye un indicador explícito, por lo que es portátil de manera segura incluso entre arquitecturas diferentes.&lt;/li>
&lt;li>&lt;strong>Optimización para mmap (mapeo de memoria):&lt;/strong> Los datos de los tensores están alineados (padded) en límites específicos de la memoria, permitiendo mapearlos directamente desde el disco al espacio de memoria mediante la llamada al sistema &lt;code>mmap()&lt;/code> del SO. Esto reduce el tiempo de inicialización de carga del modelo prácticamente a cero.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-las-profundidades-de-k-quants-cuantización-avanzada-por-bloques">4. Las profundidades de k-quants: Cuantización avanzada por bloques
&lt;/h2>&lt;p>El verdadero potencial del formato GGUF radica en el mecanismo llamado &lt;strong>k-quants (K-quantization)&lt;/strong>, que se encarga de la compresión de los pesos del modelo.&lt;/p>
&lt;p>En general, los pesos de una red neuronal tienen una forma cercana a una distribución normal cuando se observan a nivel de capa completa, pero localmente existen valores atípicos (Outliers). Si se cuantizan los pesos de toda una capa con un único factor de escala $S$ uniforme, los valores atípicos arrastrarán la escala y la información de los pesos más pequeños se perderá por completo.&lt;/p>
&lt;p>Para evitar esto, llama.cpp realiza una &lt;strong>cuantización por bloques (Block-wise Quantization)&lt;/strong>. Consiste en dividir el tensor de pesos en bloques pequeños (por ejemplo, 32 o 256 elementos) y asignar a cada bloque su propio factor de escala (y punto cero).&lt;/p>
&lt;h3 id="41-límites-de-la-cuantización-heredada-q4_0-q4_1">4.1. Límites de la cuantización heredada (Q4_0, Q4_1)
&lt;/h3>&lt;p>El inicial &lt;code>Q4_0&lt;/code> agrupaba 32 pesos FP16 en un solo bloque y compartía 1 factor de escala FP16.&lt;/p>
&lt;ul>
&lt;li>Tamaño del bloque: 32&lt;/li>
&lt;li>Memoria: 1 escala (16 bits) + 32 pesos de 4 bits (128 bits) = 144 bits&lt;/li>
&lt;li>Bits efectivos por peso (bpw: bits per weight): $144 / 32 = 4.5$ bpw&lt;/li>
&lt;/ul>
&lt;p>Aunque esto es bastante bueno, los límites entre la precisión y la tasa de compresión se hicieron evidentes. Así es como surgieron los &lt;strong>k-quants&lt;/strong>, que tienen una estructura jerárquica mucho más compleja y sofisticada.&lt;/p>
&lt;h3 id="42-estructura-jerárquica-de-superbloques-y-subbloques-ejemplo-de-q4_k_m">4.2. Estructura jerárquica de superbloques y subbloques (Ejemplo de Q4_K_M)
&lt;/h3>&lt;p>k-quants tiene una estructura jerárquica compuesta por &amp;ldquo;Superbloques (Super-blocks)&amp;rdquo; grandes y pequeños &amp;ldquo;Subbloques (Sub-blocks)&amp;rdquo; contenidos en su interior. Mediante esto, también cuantiza los propios metadatos (como los valores de escala), reduciendo los bpw al mínimo mientras mantiene la precisión.&lt;/p>
&lt;p>Veamos la estructura de la configuración más popular, &lt;strong>Q4_K_M&lt;/strong>. En Q4_K_M, se utiliza un superbloque de 256 elementos.&lt;/p>
&lt;div class="mermaid">graph TD
A["Superbloque (256 pesos)"] --> B["Metadatos de escala (FP16/INT8)"]
A --> C["Subbloque 0 (32 pesos, 4-bit)"]
A --> D["Subbloque 1 (32 pesos, 4-bit)"]
A --> E["..."]
A --> F["Subbloque 7 (32 pesos, 4-bit)"]
B --> B1["Super-escala (FP16)"]
B --> B2["Sub-escalas (8 x 6-bit)"]
B --> B3["Sub-mínimos (8 x 6-bit)"]&lt;/div>
&lt;p>La estructura real en C++ (GGML) se define conceptualmente de la siguiente manera:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Estructura conceptual de block_q4_K en llama.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define QK_K 256
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">block_q4_K&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Súper-escala de todo el superbloque (FP16 x 2, etc.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">scales&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Datos empaquetados de escalas de 6-bit y mínimos de 6-bit (puntos cero) para 8 subbloques (32 elementos cada uno)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">qs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">QK_K&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Datos de pesos cuantizados a 4-bit (256 elementos / 2 = 128 bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Proceso matemático de descuantización (Dequantization):&lt;/strong>&lt;/p>
&lt;p>El valor real aproximado $\tilde{W}_{i, j}$ del elemento $j$ ($0 \le j &lt; 32$) dentro del subbloque $i$ ($0 \le i &lt; 8$) se calcula de la siguiente manera:&lt;/p>
$$ \tilde{W}_{i, j} = S_{\text{super}} \times s_i \times (w_{i, j} - m_i) $$
&lt;ul>
&lt;li>$S_{\text{super}}$: Escala de punto flotante de todo el superbloque&lt;/li>
&lt;li>$s_i$: Escala cuantizada a 6-bit para el subbloque $i$&lt;/li>
&lt;li>$m_i$: Valor mínimo cuantizado a 6-bit (punto cero) para el subbloque $i$&lt;/li>
&lt;li>$w_{i, j}$: Peso cuantizado a 4-bit ($0 \dots 15$)&lt;/li>
&lt;/ul>
&lt;p>Gracias a esta estructura jerárquica, se mantiene la adaptabilidad a los valores atípicos, a la vez que se reduce drásticamente la cantidad de memoria ocupada por los propios factores de escala. En conjunto, Q4_K_M logra un aproximado de &lt;strong>4.8 bpw&lt;/strong>.&lt;/p>
&lt;h3 id="43-diversas-opciones-de-k-quants">4.3. Diversas opciones de k-quants
&lt;/h3>&lt;p>llama.cpp ofrece múltiples variaciones según el objetivo. Los sufijos después de la &amp;ldquo;K&amp;rdquo; (S, M, L) indican el tamaño.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Formato&lt;/th>
&lt;th style="text-align:center">BPW (Bits por Peso)&lt;/th>
&lt;th style="text-align:left">Descripción y características&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q2_K&lt;/strong>&lt;/td>
&lt;td style="text-align:center">2.5～3.3&lt;/td>
&lt;td style="text-align:left">Compresión extrema. La precisión se degrada considerablemente, pero es útil en entornos con muy poca VRAM.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q3_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">3.3&lt;/td>
&lt;td style="text-align:left">Estándar para la cuantización de 3 bits. Se degrada más que Q4, pero a menudo se mantiene dentro de márgenes aceptables.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q4_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">4.8&lt;/td>
&lt;td style="text-align:left">&lt;strong>El punto óptimo recomendado (sweet spot)&lt;/strong>. Equilibra la reducción a la mitad del tamaño del modelo manteniendo una buena precisión.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q5_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">5.5&lt;/td>
&lt;td style="text-align:left">Cuando se requiere mayor precisión. Una posición intermedia entre Q4 y FP16.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q6_K&lt;/strong>&lt;/td>
&lt;td style="text-align:center">6.6&lt;/td>
&lt;td style="text-align:left">Mantiene una Perplejidad casi equivalente a FP16, pero el tamaño del archivo es mayor.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q8_0&lt;/strong>&lt;/td>
&lt;td style="text-align:center">8.5&lt;/td>
&lt;td style="text-align:left">Equivalente a INT8. Principalmente usado para tensores intermedios durante la inferencia o utilizado solo en la última capa.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>※ El BPW real se promedia sobre el modelo completo, ya que internamente se realiza una cuantización mixta (Mixed Quantization) dependiendo del tensor del modelo (por ejemplo, proyecciones Q/K/V de Attention frente a los pesos FFN). Se aplican optimizaciones internas, como cuantizar los tensores importantes en Q6 y el resto en Q4.&lt;/p>
&lt;hr>
&lt;h2 id="5-optimización-del-rendimiento-en-la-inferencia-arquitecturas-simd-y-cuda">5. Optimización del rendimiento en la inferencia: Arquitecturas SIMD y CUDA
&lt;/h2>&lt;p>El solo hecho de cargar un modelo GGUF en memoria no hace que la inferencia sea rápida. La mayor parte de la inferencia de un LLM es una &amp;ldquo;multiplicación de matriz-vector (Matrix-Vector Multiplication, abreviado GEMV, o Matriz-Matriz, GEMM)&amp;rdquo;. La clave reside en cómo acelerar la operación de suma de productos (dot product) entre los pesos cuantizados y las activaciones (datos de entrada) retenidas en FP16 (o FP32).&lt;/p>
&lt;h3 id="51-aprovechamiento-de-instrucciones-simd-en-entornos-de-cpu">5.1. Aprovechamiento de instrucciones SIMD en entornos de CPU
&lt;/h3>&lt;p>La sorprendente velocidad que presume llama.cpp en la inferencia por CPU se debe a su optimización &lt;strong>SIMD (Single Instruction, Multiple Data)&lt;/strong> a nivel de ensamblador.
Por ejemplo, en procesadores Intel/AMD, aprovecha al máximo los conjuntos de instrucciones &lt;strong>AVX2&lt;/strong> o &lt;strong>AVX-512&lt;/strong>, y en Apple Silicon, las instrucciones &lt;strong>ARM NEON&lt;/strong>.&lt;/p>
&lt;p>Durante la inferencia, no se convierte (Dequantize) intencionalmente $W_q$ de vuelta a FP32 para luego realizar la multiplicación.
El lado de las activaciones también se cuantiza dinámicamente por bloques (Dynamic Quantization, generalmente hacia INT8), y se calcula el producto en bloque mediante aritmética de enteros &lt;strong>INT8 $\times$ INT4&lt;/strong> utilizando instrucciones SIMD especiales para el producto escalar (ej. &lt;code>vdpaddd&lt;/code> o &lt;code>_mm256_madd_epi16&lt;/code>). Al devolver el acumulador final a FP32 y multiplicarlo por el factor de escala, se logra un rendimiento asombroso.&lt;/p>
&lt;h3 id="52-descarga-offload-en-entornos-de-gpu-cublas--cuda">5.2. Descarga (Offload) en entornos de GPU (cuBLAS / CUDA)
&lt;/h3>&lt;p>Recientemente, llama.cpp no solo soporta CPUs, sino que cuenta con un poderoso soporte para GPUs de NVIDIA (CUBLAS / CUDA).
Es posible descargar (offload) partes o todas las capas del archivo GGUF a la VRAM (mediante la opción &lt;code>--n-gpu-layers&lt;/code>).&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as Usuario
participant CPU_RAM as CPU &amp; RAM (mmap)
participant VRAM as GPU VRAM
participant Compute as Tensor Cores
User->>CPU_RAM: Cargar GGUF (mmap)
CPU_RAM->>VRAM: Descargar (Offload) capas (ej. 30/32 capas)
Note over CPU_RAM, VRAM: Los datos permanecen cuantizados en VRAM
User->>Compute: Paso hacia adelante (Tokens de entrada)
Compute->>VRAM: Obtener pesos cuantizados
Compute->>Compute: Descuantizar a FP16 sobre la marcha en SRAM
Compute->>Compute: Multiplicación de matrices (cuBLAS / Kernels personalizados)
Compute->>User: Logits de salida&lt;/div>
&lt;p>Cuando se realizan cálculos en la GPU, el ancho de banda de la memoria de video (Memory Bandwidth) se convierte en el mayor cuello de botella. Dado que los pesos están comprimidos con k-quants, la transferencia de datos desde la VRAM a las unidades de cálculo de la GPU (SM: Streaming Multiprocessor o Tensor Cores) se reduce a un tercio o a un cuarto. En el instante exacto en que los pesos llegan a la unidad de cómputo, se descuantizan (expanden) a FP16 sobre la marcha, y utilizando los Tensor Cores, se ejecutan las multiplicaciones de matrices a velocidad ultra alta.
En otras palabras, la cuantización &lt;strong>se realiza no para &amp;ldquo;reducir la cantidad de cálculos&amp;rdquo;, sino para &amp;ldquo;reducir la cantidad de transferencia de memoria&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="6-ejemplos-concretos-del-balance-trade-off-entre-uso-de-memoria-y-rendimiento">6. Ejemplos concretos del balance (trade-off) entre uso de memoria y rendimiento
&lt;/h2>&lt;p>Tomemos como ejemplo el modelo Llama 3 de 8B para ver los requisitos técnicos según el nivel de cuantización de GGUF. (Las cifras son una estimación orientativa).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Modelo/Cuantización&lt;/th>
&lt;th style="text-align:left">Tamaño del archivo&lt;/th>
&lt;th style="text-align:left">VRAM/RAM necesaria&lt;/th>
&lt;th style="text-align:left">Velocidad de inferencia (est.)&lt;/th>
&lt;th style="text-align:left">Degradación de Perplejidad&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (FP16)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Aprox. 16 GB&lt;/td>
&lt;td style="text-align:left">18 GB o más&lt;/td>
&lt;td style="text-align:left">Referencia&lt;/td>
&lt;td style="text-align:left">Ninguna (Base)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q8_0)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Aprox. 8.5 GB&lt;/td>
&lt;td style="text-align:left">10 GB o más&lt;/td>
&lt;td style="text-align:left">Rápida&lt;/td>
&lt;td style="text-align:left">Casi nula&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q6_K)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Aprox. 6.6 GB&lt;/td>
&lt;td style="text-align:left">8 GB o más&lt;/td>
&lt;td style="text-align:left">Muy rápida&lt;/td>
&lt;td style="text-align:left">Mínima&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q4_K_M)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Aprox. 4.9 GB&lt;/td>
&lt;td style="text-align:left">6.5 GB o más&lt;/td>
&lt;td style="text-align:left">La más rápida / Óptima&lt;/td>
&lt;td style="text-align:left">Aceptable / Minúscula&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q3_K_M)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Aprox. 3.9 GB&lt;/td>
&lt;td style="text-align:left">5.5 GB o más&lt;/td>
&lt;td style="text-align:left">La más rápida&lt;/td>
&lt;td style="text-align:left">Algo notable&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q2_K)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Aprox. 3.0 GB&lt;/td>
&lt;td style="text-align:left">4.5 GB o más&lt;/td>
&lt;td style="text-align:left">Rápida&lt;/td>
&lt;td style="text-align:left">Degradación evidente&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Punto de atención (El impacto de la caché KV):&lt;/strong>
Durante la inferencia de un LLM, a medida que la longitud del contexto (número de tokens en el prompt) aumenta, el consumo de memoria se incrementa de manera explosiva no solo por los pesos del modelo, sino también por la &lt;strong>Caché KV&lt;/strong>, que guarda el estado histórico de Attention.
Por ejemplo, si el contexto tiene 8192 tokens, la caché KV por sí sola consumirá varios gigabytes. Por tanto, en la operación real, es necesario asegurar un margen (Headroom) de &lt;code>Tamaño del archivo del modelo + Aprox. 1.5GB ~ 3GB&lt;/code>. La razón por la que se recomienda Q4_K_M es porque, incluso reservando este espacio para la caché KV, representa el equilibrio ideal para funcionar con seguridad en una GPU común de 8 GB de VRAM (como una RTX 3060 / 4060).&lt;/p>
&lt;p>En las versiones recientes de llama.cpp, se ha añadido también &lt;strong>la función de cuantizar la propia Caché KV a Q8_0 o Q4_0&lt;/strong>, de manera que se investiga incesantemente para poder extender aún más la longitud de los contextos.&lt;/p>
&lt;hr>
&lt;h2 id="7-conclusión">7. Conclusión
&lt;/h2>&lt;p>En este artículo, hemos profundizado en la estructura interna del formato GGUF y la tecnología de cuantización k-quants, que conforman el núcleo de llama.cpp.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>La flexibilidad de GGUF:&lt;/strong> Su estructura de metadatos tipo clave-valor ha construido un sólido ecosistema capaz de seguir el rápido ritmo evolutivo de los LLM (con la aparición de nuevas arquitecturas de modelos) sin sufrir cambios destructivos.&lt;/li>
&lt;li>&lt;strong>Compresión extrema mediante k-quants:&lt;/strong> A través de la gestión jerárquica de los factores de escala en superbloques y subbloques, se ha logrado una asombrosa compresión promedio de 4.8 bits por peso (en Q4_K_M), conservando al mismo tiempo la información de los valores atípicos.&lt;/li>
&lt;li>&lt;strong>Resolución del cuello de botella en el ancho de banda de memoria:&lt;/strong> Mediante el uso de sofisticadas implementaciones de kernels en SIMD y CUDA, y al realizar la descuantización al vuelo mientras se calcula, se reduce la cantidad de transferencia hacia/desde la VRAM, lo que mejora drásticamente la velocidad de inferencia.&lt;/li>
&lt;/ol>
&lt;p>Se podría decir, sin exagerar, que el poder tecnológico detrás de llama.cpp, que está impulsando la democratización de la IA, va mucho más allá de ser una simple herramienta y representa una de las cumbres de la ingeniería de software moderna. Al comprender los algoritmos de cuantización y los mecanismos del formato GGUF, podrá seleccionar el modelo ideal y ajustar el rendimiento (performance tuning) de forma más precisa para su propio entorno.&lt;/p>
&lt;h3 id="enlaces-de-referencia">Enlaces de referencia
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>Repositorio en GitHub de llama.cpp&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/ggml/blob/master/docs/gguf.md" target="_blank" rel="noopener"
>Especificación del formato GGUF&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp/pull/1684" target="_blank" rel="noopener"
>PR de la implementación de K-quants&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>(Fin)&lt;/p></description></item></channel></rss>