<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Machine Learning on kenji.blog</title><link>http://kenji.blog/es/categories/machine-learning/</link><description>Recent content in Machine Learning on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/categories/machine-learning/index.xml" rel="self" type="application/rss+xml"/><item><title>【Para Principiantes】Descifrando la estructura matemática del modelo Transformer</title><link>http://kenji.blog/es/p/transformer-mathematical-structure/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/transformer-mathematical-structure/</guid><description>&lt;img src="http://kenji.blog/p/transformer-mathematical-structure/img/eyecatch.jpg" alt="Featured image of post 【Para Principiantes】Descifrando la estructura matemática del modelo Transformer" />&lt;h1 id="introducción-por-qué-aprender-las-matemáticas-de-transformer">Introducción: ¿Por qué aprender las matemáticas de Transformer?
&lt;/h1>&lt;p>No sería exagerado decir que &amp;ldquo;Transformer&amp;rdquo; es la arquitectura que reescribió la historia del procesamiento del lenguaje natural (NLP) moderno y de toda la IA en general. Propuesto por primera vez en 2017 por investigadores de Google en el artículo &amp;ldquo;Attention Is All You Need&amp;rdquo;, este modelo funciona actualmente como el corazón de los grandes modelos de lenguaje (LLM) que dominan el mundo, como la serie GPT de OpenAI (la tecnología base de ChatGPT), BERT de Google y Claude de Anthropic.&lt;/p>
&lt;p>Sin embargo, aunque es común encontrar explicaciones cualitativas sobre cómo funciona Transformer, como &amp;ldquo;utiliza un mecanismo de Attention (atención) para comprender el contexto&amp;rdquo;, en realidad hay muy pocas explicaciones profundas dirigidas a principiantes sobre la &lt;strong>estructura matemática&lt;/strong> que hay detrás. Para comprender verdaderamente cómo la IA procesa las &amp;ldquo;palabras&amp;rdquo; como &amp;ldquo;fórmulas matemáticas&amp;rdquo; y genera oraciones asombrosamente naturales, es indispensable descifrar su mecanismo matemático.&lt;/p>
&lt;p>En este artículo, dirigido a personas con conocimientos básicos de matemáticas y programación (aquellos que comprenden conceptos de nivel de secundaria como matrices y derivadas), desentrañaremos de manera exhaustiva y fácil de entender la estructura matemática de los componentes centrales de Transformer: el &amp;ldquo;Mecanismo de Self-Attention&amp;rdquo;, el &amp;ldquo;Modelo de Consulta, Clave y Valor (Q/K/V)&amp;rdquo;, la &amp;ldquo;Normalización con la función Softmax&amp;rdquo; y el &amp;ldquo;Positional Encoding&amp;rdquo;.&lt;/p>
&lt;p>Es posible que te sientas abrumado por la cantidad de fórmulas matemáticas, pero cada cálculo tiene un &amp;ldquo;significado&amp;rdquo; claro. Para cuando termines de leer este artículo, deberías poder comprender que Transformer no es simplemente una caja negra mágica, sino un cristal de matemáticas y estadísticas diseñado con gran precisión.&lt;/p>
&lt;hr>
&lt;h1 id="1-limitaciones-de-los-métodos-tradicionales-y-la-innovación-de-transformer">1. Limitaciones de los métodos tradicionales y la innovación de Transformer
&lt;/h1>&lt;p>Antes de la aparición de Transformer, la corriente principal en el procesamiento del lenguaje natural eran las Redes Neuronales Recurrentes (RNN) y su derivado, LSTM (Long Short-Term Memory). Las RNN están diseñadas para procesar datos de series temporales y leen el texto palabra por palabra, secuencialmente desde el principio.&lt;/p>
&lt;p>Sin embargo, las RNN tenían dos debilidades fatales:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Dificultad para aprender dependencias a largo plazo&lt;/strong>: A medida que las oraciones se vuelven más largas, la información de las primeras palabras ingresadas se desvanece antes de llegar al final (problema del desvanecimiento del gradiente).&lt;/li>
&lt;li>&lt;strong>Imposibilidad de realizar cálculos en paralelo&lt;/strong>: Dado que las palabras deben procesarse en orden, es difícil realizar cálculos paralelos a gran escala utilizando GPUs, lo que hace que el entrenamiento lleve una enorme cantidad de tiempo.&lt;/li>
&lt;/ol>
&lt;p>Transformer abandonó por completo la estructura de las RNN y provocó un cambio de paradigma al capturar el contexto utilizando únicamente &amp;ldquo;Attention&amp;rdquo;. Esto hizo posible que, sin importar cuán larga sea la secuencia, no haya pérdida de información y se puedan paralelizar los cálculos para aprovechar al máximo el rendimiento de la GPU.&lt;/p>
&lt;hr>
&lt;h1 id="2-arquitectura-general-de-transformer">2. Arquitectura general de Transformer
&lt;/h1>&lt;p>Primero, demos un vistazo general a la arquitectura de Transformer. Transformer se compone principalmente de dos bloques: el &amp;ldquo;Encoder&amp;rdquo; (codificador) y el &amp;ldquo;Decoder&amp;rdquo; (decodificador). Tomando como ejemplo una tarea de traducción, el Encoder convierte el idioma de entrada (ej: inglés) en una representación vectorial matemática, y el Decoder genera el idioma de salida (ej: japonés) basándose en esa representación vectorial.&lt;/p>
&lt;p>El siguiente diagrama es una simplificación de la estructura interna del bloque Encoder.&lt;/p>
&lt;div class="mermaid">graph TD
A["Tokens de Entrada"] --> B["Incrustación de Entrada"]
B --> C["Codificación Posicional"]
C --> D["Autoatención Multicabezal"]
D --> E["Suma y Normalización de Capa"]
E --> F["Red Neuronal Prealimentada"]
F --> G["Suma y Normalización de Capa"]
G --> H["Salida a la Siguiente Capa"]
C -.->|"Conexión Residual"| E
E -.->|"Conexión Residual"| G&lt;/div>
&lt;p>A partir de aquí, veamos paso a paso las operaciones matemáticas que se realizan en cada componente.&lt;/p>
&lt;hr>
&lt;h1 id="3-vectorización-de-palabras-y-codificación-posicional-positional-encoding">3. Vectorización de palabras y codificación posicional (Positional Encoding)
&lt;/h1>&lt;p>Las computadoras no pueden entender el texto tal cual. El texto de entrada primero se divide en unidades llamadas &amp;ldquo;Tokens&amp;rdquo;, y cada uno se convierte en un vector de longitud fija. Esto es el &lt;strong>Input Embedding&lt;/strong> (Incrustación de Entrada).&lt;/p>
&lt;h2 id="31-las-matemáticas-de-input-embedding">3.1 Las matemáticas de Input Embedding
&lt;/h2>&lt;p>Supongamos que el tamaño del vocabulario es $V$ y la dimensionalidad de los vectores de incrustación es $d_{model}$ (en el artículo original $d_{model} = 512$). Cada palabra $w_i$ se convierte en un vector $x_i \in \mathbb{R}^{d_{model}}$ utilizando una matriz de incrustación $W_E \in \mathbb{R}^{V \times d_{model}}$.&lt;/p>
$$ x_i = W_E \cdot \text{one\_hot}(w_i) $$
&lt;p>Con esto, toda la oración se representa como una matriz $X \in \mathbb{R}^{N \times d_{model}}$ ($N$ es la longitud de la oración).&lt;/p>
&lt;h2 id="32-la-necesidad-y-fórmulas-de-positional-encoding-codificación-posicional">3.2 La necesidad y fórmulas de Positional Encoding (Codificación Posicional)
&lt;/h2>&lt;p>A diferencia de las RNN, Transformer no procesa las palabras en orden secuencial, sino que procesa todas las palabras de forma simultánea y paralela. Esto es una gran ventaja en términos de velocidad de cálculo, pero al mismo tiempo causa el problema de que &lt;strong>se pierde la importante información del &amp;ldquo;orden de las palabras&amp;rdquo;&lt;/strong>. Por ejemplo, &amp;ldquo;El perro muerde al hombre&amp;rdquo; y &amp;ldquo;El hombre muerde al perro&amp;rdquo; tienen el mismo conjunto de palabras de entrada, pero significados completamente diferentes.&lt;/p>
&lt;p>Para proporcionar esta información del orden de las palabras al modelo, se ideó el &lt;strong>Positional Encoding&lt;/strong>.
El Positional Encoding $PE$ para la dimensión $i$-ésima de una palabra en la posición $pos$ se calcula utilizando las siguientes funciones trigonométricas.&lt;/p>
$$ PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
$$ PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) $$
&lt;p>Donde $pos$ es la posición de la palabra ($0, 1, 2, \dots, N-1$) e $i$ es el índice de la dimensión del vector ($0, 1, \dots, d_{model}/2 - 1$).&lt;/p>
&lt;h3 id="por-qué-usar-seno-y-coseno">¿Por qué usar seno y coseno?
&lt;/h3>&lt;p>A simple vista parece una fórmula extremadamente compleja y extraña, pero hay una profunda razón matemática para esto. Al usar funciones trigonométricas, el modelo puede aprender fácilmente no solo la &lt;strong>&amp;ldquo;posición absoluta&amp;rdquo;&lt;/strong>, sino también &lt;strong>la diferencia de &amp;ldquo;posición relativa&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Recuerda los teoremas de suma de funciones trigonométricas que se aprenden en la escuela secundaria.
&lt;/p>
$$ \sin(\alpha + \beta) = \sin\alpha \cos\beta + \cos\alpha \sin\beta $$
$$ \cos(\alpha + \beta) = \cos\alpha \cos\beta - \sin\alpha \sin\beta $$
&lt;p>El Positional Encoding de una posición $pos + k$, que está desplazada por un offset $k$ de una posición $pos$, se puede expresar como una combinación lineal del Positional Encoding de la posición $pos$. Es decir, utilizando una matriz $M_k$, se puede escribir de la siguiente manera:&lt;/p>
$$ PE_{pos+k} = M_k \cdot PE_{pos} $$
&lt;p>De esta manera, el mecanismo de Attention puede reconocer fácilmente la distancia relativa entre las palabras (&amp;ldquo;qué tan lejos están&amp;rdquo;) a través del cálculo del producto punto. Además, al combinar múltiples ondas senoidales y cosenoidales con diferentes longitudes de onda, existe la ventaja de poder generar vectores de posición únicos sin importar cuán larga sea la oración.&lt;/p>
&lt;p>La matriz de entrada final $X_{input}$ será la suma de los vectores de incrustación de las palabras y esta codificación posicional.&lt;/p>
$$ X_{input} = X + PE $$
&lt;hr>
&lt;h1 id="4-las-matemáticas-profundas-de-self-attention-mecanismo-de-autoatención">4. Las matemáticas profundas de Self-Attention (Mecanismo de Autoatención)
&lt;/h1>&lt;p>Por fin, nos adentramos en el componente más importante de Transformer: &lt;strong>Self-Attention&lt;/strong>. El propósito de Self-Attention es &amp;ldquo;calcular el grado de relación entre todas las palabras dentro de una oración y actualizar el vector de cada palabra a una representación más rica que tenga en cuenta el contexto&amp;rdquo;.&lt;/p>
&lt;p>Aquí se utiliza la analogía de un &amp;ldquo;sistema de búsqueda&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Query (Q)&lt;/strong>: Consulta (Término de búsqueda). &amp;ldquo;¿Qué información estoy buscando ahora?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Key (K)&lt;/strong>: Clave (Encabezado). &amp;ldquo;¿Qué información poseo?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Value (V)&lt;/strong>: Valor (Entidad). &amp;ldquo;¿Qué información proporciono realmente?&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 id="41-generación-de-las-matrices-q-k-v">4.1 Generación de las matrices $Q, K, V$
&lt;/h2>&lt;p>Para una matriz de entrada $X \in \mathbb{R}^{N \times d_{model}}$ (ignoraremos el tamaño del lote aquí para simplificar), multiplicamos por las matrices de pesos aprendibles $W^Q, W^K, W^V \in \mathbb{R}^{d_{model} \times d_k}$ para calcular la consulta $Q$, la clave $K$ y el valor $V$. (Normalmente $d_k = d_v = d_{model} / h$)&lt;/p>
$$ Q = X W^Q $$
$$ K = X W^K $$
$$ V = X W^V $$
&lt;p>Aquí, $Q, K, V$ son todas matrices de $\mathbb{R}^{N \times d_k}$.&lt;/p>
&lt;h2 id="42-cálculo-del-attention-score-producto-punto">4.2 Cálculo del Attention Score (Producto Punto)
&lt;/h2>&lt;p>Para medir qué tan relacionada está la Query de cada palabra con la Key de todas las demás palabras, calculamos el &lt;strong>producto punto&lt;/strong> de los vectores. Expresado en operaciones de matrices, queda de la siguiente manera:&lt;/p>
$$ \text{Scores} = Q K^T $$
&lt;p>Cada elemento $s_{ij}$ de la matriz resultante $\text{Scores} \in \mathbb{R}^{N \times N}$ obtenida por este cálculo representa el producto punto entre la Query de la palabra $i$ y la Key de la palabra $j$, es decir, la &amp;ldquo;fuerza de la relación&amp;rdquo;.&lt;/p>
&lt;h2 id="43-escalamiento-scale">4.3 Escalamiento (Scale)
&lt;/h2>&lt;p>Hay un problema con el cálculo de la puntuación mediante el producto punto. Cuando la dimensionalidad $d_k$ de los vectores se vuelve grande, el valor del producto punto puede volverse extremadamente grande o pequeño.&lt;/p>
&lt;p>Demostremos esto matemáticamente.
Supongamos que cada elemento de la consulta $q \sim \mathcal{N}(0, 1)$ y cada elemento de la clave $k \sim \mathcal{N}(0, 1)$ siguen distribuciones normales estándar independientes.
Encontremos la media y la varianza del producto punto $q \cdot k = \sum_{i=1}^{d_k} q_i k_i$.
Media: Dado que $\mathbb{E}[q_i k_i] = \mathbb{E}[q_i] \mathbb{E}[k_i] = 0 \times 0 = 0$, la media de la suma también es $0$.
Varianza: La varianza de $q_i k_i$ es, debido a la independencia, $\text{Var}(q_i k_i) = \mathbb{E}[(q_i k_i)^2] - (\mathbb{E}[q_i k_i])^2 = 1 \times 1 - 0 = 1$.
Por lo tanto, la varianza de todo el producto punto será igual a la dimensionalidad $d_k$.&lt;/p>
$$ \text{Var}(q \cdot k) = d_k $$
&lt;p>Si la varianza se vuelve grande, al aplicar la función Softmax más adelante, el gradiente para todo valor que no sea el máximo se volverá extremadamente pequeño (lo que se conoce como &amp;ldquo;desvanecimiento del gradiente&amp;rdquo;), lo que impedirá que el aprendizaje avance.
Para evitar esto, dividimos (escalamos) las puntuaciones por $\sqrt{d_k}$ para mantener la varianza siempre en $1$.&lt;/p>
$$ \text{Scaled Scores} = \frac{Q K^T}{\sqrt{d_k}} $$
&lt;h2 id="44-probabilización-mediante-la-función-softmax">4.4 Probabilización mediante la función Softmax
&lt;/h2>&lt;p>Para convertir las puntuaciones obtenidas en una distribución de probabilidad (pesos) cuya suma sea $1$, aplicamos la &lt;strong>función Softmax&lt;/strong> fila por fila.&lt;/p>
$$ a_{ij} = \text{softmax}(s_i)_j = \frac{\exp(s_{ij} / \sqrt{d_k})}{\sum_{m=1}^N \exp(s_{im} / \sqrt{d_k})} $$
&lt;p>La matriz $A \in \mathbb{R}^{N \times N}$ se denomina matriz de Attention Weight (Pesos de Atención). Al observar cada fila $i$ de esta matriz, podemos ver expresado como un valor de 0 a 1 &amp;ldquo;a qué otras palabras $j$ y en qué medida se debe prestar atención (Attention) para comprender la palabra $i$&amp;rdquo;.&lt;/p>
&lt;h2 id="45-suma-ponderada-de-value">4.5 Suma ponderada de Value
&lt;/h2>&lt;p>Finalmente, utilizamos la matriz de Attention Weight $A$ obtenida para calcular la suma ponderada de la matriz Value $V$.&lt;/p>
$$ \text{Output} = A V = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>La matriz $Z \in \mathbb{R}^{N \times d_v}$ obtenida mediante esta operación es un conjunto de &amp;ldquo;representaciones vectoriales de palabras actualizadas teniendo en cuenta el contexto&amp;rdquo;.
Esta es la totalidad del &lt;strong>Scaled Dot-Product Attention&lt;/strong> definido en el artículo.&lt;/p>
&lt;hr>
&lt;h1 id="5-multi-head-attention-atención-multicabezal">5. Multi-Head Attention (Atención Multicabezal)
&lt;/h1>&lt;p>Con un solo cálculo de Attention (cabezal único), existe la posibilidad de que solo se capture el contexto desde un único punto de vista (por ejemplo, la &amp;ldquo;relación gramatical&amp;rdquo;). Por lo tanto, para capturar simultáneamente diversas relaciones semánticas y sintácticas del lenguaje (como &amp;ldquo;sujeto y predicado&amp;rdquo;, &amp;ldquo;pronombre y su referente&amp;rdquo;, etc.), se introdujo el &lt;strong>Multi-Head Attention&lt;/strong>.&lt;/p>
&lt;p>Realizamos la generación anterior de $Q, K, V$ y el cálculo de Attention en paralelo $h$ veces (número de cabezales, $h=8$ en el artículo original).&lt;/p>
$$ \text{head}_i = \text{Attention}(X W_i^Q, X W_i^K, X W_i^V) $$
&lt;p>Aquí, $W_i^Q, W_i^K, W_i^V \in \mathbb{R}^{d_{model} \times d_k}$ son matrices de pesos aprendibles dedicadas para el cabezal $i$-ésimo.&lt;/p>
&lt;p>Los resultados emitidos desde cada cabezal $\text{head}_i \in \mathbb{R}^{N \times d_v}$ se concatenan horizontalmente (Concatenate).&lt;/p>
$$ \text{Concat}(\text{head}_1, \dots, \text{head}_h) \in \mathbb{R}^{N \times (h \cdot d_v)} $$
&lt;p>Normalmente se configura para que $h \cdot d_v = d_{model}$, de modo que la dimensionalidad después de la concatenación vuelva a ser $d_{model}$ como en la entrada. Finalmente, multiplicamos esta matriz por una matriz de pesos $W^O \in \mathbb{R}^{d_{model} \times d_{model}}$ para obtener la salida final.&lt;/p>
$$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) W^O $$
&lt;div class="mermaid">graph TD
X["Entrada X"] --> Q1["Q1"]
X --> K1["K1"]
X --> V1["V1"]
Q1 &amp; K1 &amp; V1 --> H1["Cabezal 1"]
X --> Q2["Q2"]
X --> K2["K2"]
X --> V2["V2"]
Q2 &amp; K2 &amp; V2 --> H2["Cabezal 2"]
X --> QN["..."]
X --> KN["..."]
X --> VN["..."]
QN &amp; KN &amp; VN --> HN["Cabezal h"]
H1 &amp; H2 &amp; HN --> C["Concatenar"]
C --> WO["Multiplicar por WO"]
WO --> OUT["Salida Multicabezal"]&lt;/div>
&lt;hr>
&lt;h1 id="6-feed-forward-neural-network-ffn">6. Feed-Forward Neural Network (FFN)
&lt;/h1>&lt;p>La salida de Multi-Head Attention se introduce a continuación en una &lt;strong>Position-wise Feed-Forward Network (FFN)&lt;/strong>.
Esta es una red neuronal totalmente conectada de dos capas que se aplica &amp;ldquo;independientemente para cada posición (palabra)&amp;rdquo; en la secuencia.&lt;/p>
&lt;p>Expresado matemáticamente, se ve de la siguiente manera:&lt;/p>
$$ \text{FFN}(x) = \max(0, x W_1 + b_1) W_2 + b_2 $$
&lt;p>Aquí, $\max(0, z)$ representa la función de activación ReLU (Rectified Linear Unit) (en los modelos recientes se usan a menudo GELU o SwiGLU).&lt;/p>
&lt;p>El papel de esta red es sumamente importante. Mientras que el mecanismo de Attention aprende &amp;ldquo;las relaciones entre palabras (relaciones espaciales/secuenciales)&amp;rdquo;, la FFN se encarga de &amp;ldquo;la transformación no lineal de características de cada vector de palabra en sí&amp;rdquo;.
Normalmente, la dimensionalidad se expande significativamente en la primera capa mediante los pesos $W_1$ (por ejemplo, de $d_{model}=512$ a $d_{ff}=2048$, expandiéndose 4 veces), se realizan cálculos complejos en el espacio de características y, luego, en la segunda capa se vuelve a la dimensión original utilizando los pesos $W_2$. Esta &amp;ldquo;expansión y reducción de dimensiones&amp;rdquo; aumenta dramáticamente el poder de representación del modelo.&lt;/p>
&lt;hr>
&lt;h1 id="7-conexiones-residuales-residual-connection-y-normalización-de-capas-layer-normalization">7. Conexiones Residuales (Residual Connection) y Normalización de Capas (Layer Normalization)
&lt;/h1>&lt;p>En el aprendizaje profundo, al hacer que las redes sean más profundas, los gradientes tienden a desvanecerse o explotar durante el entrenamiento, lo que provoca que el aprendizaje falle. Para prevenir esto, alrededor de cada subcapa (Attention y FFN) de Transformer, se han colocado una &lt;strong>Conexión Residual (Residual Connection)&lt;/strong> y una &lt;strong>Normalización de Capas (Layer Normalization)&lt;/strong>.&lt;/p>
&lt;p>Matemáticamente, la salida de la subcapa se procesa de la siguiente manera:&lt;/p>
$$ \text{Output} = \text{LayerNorm}(x + \text{Sublayer}(x)) $$
&lt;h2 id="71-conexión-residual-x--textsublayerx">7.1 Conexión Residual ($x + \text{Sublayer}(x)$)
&lt;/h2>&lt;p>La entrada $x$ se suma directamente a la salida de la subcapa. Esto permite que el gradiente viaje directamente a las capas más superficiales a través del atajo durante la retropropagación, estabilizando así el aprendizaje incluso si se añaden más capas.&lt;/p>
&lt;h2 id="72-las-matemáticas-de-layer-normalization">7.2 Las matemáticas de Layer Normalization
&lt;/h2>&lt;p>La Layer Normalization es una técnica que calcula la media y la varianza a lo largo de la dimensión de las características y normaliza los datos. En una entrada con tamaño de lote $B$, longitud de secuencia $N$ y dimensionalidad $d_{model}$, la normalización se realiza para un único vector de palabra $x \in \mathbb{R}^{d_{model}}$.&lt;/p>
&lt;p>Se calculan la media $\mu$ y la varianza $\sigma^2$:
&lt;/p>
$$ \mu = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} x_i $$
$$ \sigma^2 = \frac{1}{d_{model}} \sum_{i=1}^{d_{model}} (x_i - \mu)^2 $$
&lt;p>Y se obtiene la salida normalizada $\hat{x}$:
&lt;/p>
$$ \text{LN}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta $$
&lt;p>
(Donde $\epsilon$ es una constante minúscula para evitar la división por cero. $\gamma$ y $\beta$ son parámetros de escala y desplazamiento aprendibles).&lt;/p>
&lt;p>La razón por la que se adoptó la normalización a lo largo de las capas (Layer Normalization) en lugar de la normalización a lo largo de los lotes (Batch Normalization) es que las estadísticas entre lotes tienden a volverse inestables al procesar datos de secuencia de longitudes variables, como las oraciones. Con Layer Normalization, Transformer puede realizar un aprendizaje estable de forma independiente del tamaño del lote.&lt;/p>
&lt;hr>
&lt;h1 id="8-estructuras-específicas-del-decoder-masked-attention-y-cross-attention">8. Estructuras específicas del Decoder: Masked Attention y Cross-Attention
&lt;/h1>&lt;p>La estructura que hemos explicado hasta ahora es la del Encoder. En el bloque del Decoder que genera las oraciones, la estructura es un poco diferente.&lt;/p>
&lt;h2 id="81-masked-multi-head-attention">8.1 Masked Multi-Head Attention
&lt;/h2>&lt;p>El papel del Decoder es &amp;ldquo;predecir la siguiente palabra a partir de las palabras pasadas&amp;rdquo;. Por lo tanto, si &amp;ldquo;mira las palabras futuras&amp;rdquo; durante el entrenamiento, sería hacer trampa. La operación matemática para prevenir esto es el &lt;strong>Masking&lt;/strong> (Enmascaramiento).&lt;/p>
&lt;p>A la matriz de puntuaciones $Q K^T$, le sumamos una matriz de máscara $M$ que establece un valor extremadamente pequeño cercano a $-\infty$ en la parte triangular superior (correspondiente a la información futura).&lt;/p>
$$ M_{ij} = \begin{cases} 0 &amp; (i \le j) \\ -\infty &amp; (i > j) \end{cases} $$
$$ \text{Masked Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T + M}{\sqrt{d_k}}\right) V $$
&lt;p>Al calcular la función Softmax, dado que $\exp(-\infty) = 0$, el Attention Weight para las palabras futuras se vuelve completamente $0$. Esto permite una generación autorregresiva manteniendo la causalidad (Causality).&lt;/p>
&lt;h2 id="82-encoder-decoder-cross-attention">8.2 Encoder-Decoder Cross-Attention
&lt;/h2>&lt;p>La segunda subcapa del Decoder es el &lt;strong>Cross-Attention&lt;/strong>, que hace referencia a la salida del Encoder.
Aquí, $Q$ se genera a partir de la capa anterior del Decoder, mientras que $K$ y $V$ se generan a partir de la salida de la capa final del Encoder.&lt;/p>
$$ Q_{decoder} = X_{dec} W^Q $$
$$ K_{encoder} = X_{enc} W^K $$
$$ V_{encoder} = X_{enc} W^V $$
&lt;p>Con este cálculo, en tareas como la traducción, el modelo puede aprender &amp;ldquo;a qué parte de la oración original extranjera está fuertemente relacionada la palabra que se está traduciendo ahora&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h1 id="9-complejidad-computacional-y-las-matemáticas-de-la-optimización-moderna">9. Complejidad computacional y las matemáticas de la optimización moderna
&lt;/h1>&lt;p>Aunque Transformer es un modelo maravilloso, también tiene &amp;ldquo;debilidades&amp;rdquo; debidas a su estructura matemática.
Preste atención a la complejidad computacional del Self-Attention. En el cálculo de la matriz de puntuación $Q K^T$, multiplicamos una matriz de $(N \times d_k)$ por una matriz de $(d_k \times N)$, por lo que la complejidad es &lt;strong>$O(N^2 \cdot d_{model})$&lt;/strong>.&lt;/p>
&lt;p>Es decir, &lt;strong>el costo computacional y el uso de memoria aumentan de manera cuadrática con respecto a la longitud de la secuencia $N$&lt;/strong>.
Cuando las oraciones son cortas esto no es un problema, pero si intentas ingresar a un LLM un contexto extremadamente largo, como el de un libro entero, $N$ alcanza las decenas o cientos de miles, y el cálculo tradicional de Attention agotará inmediatamente la memoria de la GPU.&lt;/p>
&lt;p>Para romper esta maldición de $O(N^2)$, en los últimos años se han propuesto varias optimizaciones desde perspectivas matemáticas y de hardware.
El ejemplo más representativo es &lt;strong>FlashAttention&lt;/strong>. FlashAttention es un algoritmo que divide el cálculo de Attention en mosaicos (Tiling) para minimizar la transferencia de datos (accesos a memoria) entre la jerarquía de memoria de la GPU (SRAM y HBM). A pesar de arrojar matemáticamente el mismo resultado exacto que el Attention estándar (Exact Attention), ha logrado una aceleración y reducción de memoria dramáticas gracias a la optimización a nivel de hardware, haciendo posible la realización de modelos de contexto largo como GPT-4.&lt;/p>
&lt;p>Además, se están investigando activamente otras aproximaciones de la complejidad computacional como $O(N \log N)$ y $O(N)$, tales como Sparse Attention y Linear Attention.&lt;/p>
&lt;hr>
&lt;h1 id="10-imagen-de-implementación-pseudocódigo-al-estilo-pytorch">10. Imagen de implementación (Pseudocódigo al estilo PyTorch)
&lt;/h1>&lt;p>Si trasladamos la estructura matemática explicada hasta aquí a un código de programación real (Python / PyTorch), veremos que se puede escribir de una forma asombrosamente simple. Aquí mostramos el pseudocódigo del núcleo de Self-Attention.&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;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch.nn.functional&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">F&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">math&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">scaled_dot_product_attention&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mask&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># formas de q, k, v: [batch_size, num_heads, seq_length, d_k]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d_k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. Cálculo de puntuación mediante producto punto: Q * K^T&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Se calculan los productos de las matrices transponiendo las dos últimas dimensiones&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">transpose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. Escalamiento&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">math&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sqrt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">d_k&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. Enmascaramiento (En el caso de Masked Attention)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">mask&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">scores&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">masked_fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mask&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mf">1e9&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. Probabilización mediante Softmax&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attention_weights&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">softmax&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scores&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dim&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. Multiplicación por la matriz Value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">matmul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attention_weights&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attention_weights&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Podemos ver que el $Q K^T / \sqrt{d_k}$ expresado matemáticamente, se implementa de manera intuitiva como &lt;code>torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)&lt;/code>. Es un aspecto sumamente interesante del aprendizaje profundo que la teoría matemática pueda materializarse en unas pocas líneas de código con la ayuda de bibliotecas de optimización avanzadas.&lt;/p>
&lt;hr>
&lt;h1 id="conclusión-la-forma-de-la-inteligencia-vista-desde-las-fórmulas-matemáticas">Conclusión: La forma de la &amp;ldquo;Inteligencia&amp;rdquo; vista desde las fórmulas matemáticas
&lt;/h1>&lt;p>En este artículo, hemos desentrañado la estructura matemática profunda del modelo Transformer.&lt;/p>
&lt;p>El Embedding que asigna palabras a un espacio vectorial multidimensional, el Positional Encoding que expresa la información de posición como la composición de ondas trigonométricas, y el mecanismo de Self-Attention, un cálculo de producto punto de matrices nacido de una analogía con la recuperación de información. Cada uno de estos componentes no es más que una acumulación de matemáticas fundamentales como el álgebra lineal, el cálculo diferencial e integral, y la probabilidad y estadística.&lt;/p>
&lt;p>Sin embargo, cuando estas simples operaciones matriciales se apilan en numerosas capas y aprenden patrones de gigantescos conjuntos de datos a través de miles de millones o billones de parámetros, surge una &amp;ldquo;forma de inteligencia&amp;rdquo; que parece comprender nuestras &amp;ldquo;palabras&amp;rdquo;, realizar deducciones lógicas y, a veces, generar ideas creativas.&lt;/p>
&lt;p>Como sugiere el provocador título &amp;ldquo;Attention Is All You Need&amp;rdquo;, la belleza de esta arquitectura, que descarta los complejos procesamientos recurrentes o convolucionales y se especializa en el puro cálculo de &amp;ldquo;Atención&amp;rdquo; (grado de relación), reside precisamente en su simplicidad matemática.&lt;/p>
&lt;p>En el futuro, podrían surgir nuevas arquitecturas que superen a Transformer (como los State Space Models, por ejemplo, Mamba), pero el marco matemático de &amp;ldquo;comprensión del contexto mediante Attention&amp;rdquo; forjado por Transformer quedará grabado para siempre en la historia de la IA.&lt;/p>
&lt;p>Si en el futuro tienes la oportunidad de usar LLMs como ChatGPT o Claude, imagina los billones de multiplicaciones de matrices de $Q K^T$ por segundo y las funciones Softmax calculando probabilidades en segundo plano. Esto aumentará tu resolución sobre la tecnología y hará que el mundo de la IA te parezca aún más fascinante.&lt;/p>
&lt;h3 id="referencias">Referencias
&lt;/h3>&lt;ul>
&lt;li>Vaswani, A., et al. (2017). &amp;ldquo;Attention Is All You Need.&amp;rdquo; &lt;em>Advances in Neural Information Processing Systems&lt;/em>.&lt;/li>
&lt;li>Alammar, J. (2018). &amp;ldquo;The Illustrated Transformer.&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;em>Este artículo fue escrito como una guía para aquellos que aprenden los fundamentos matemáticos del procesamiento de lenguaje natural y la inteligencia artificial. ¡Si tienes alguna pregunta o discusión, por favor háznoslo saber en la sección de comentarios!&lt;/em>&lt;/p></description></item><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>