<?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/pt/categories/machine-learning/</link><description>Recent content in Machine Learning on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>pt</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/pt/categories/machine-learning/index.xml" rel="self" type="application/rss+xml"/><item><title>【Para Iniciantes】Desvendando a Estrutura Matemática do Modelo Transformer</title><link>http://kenji.blog/pt/p/transformer-mathematical-structure/</link><pubDate>Fri, 11 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Iniciantes】Desvendando a Estrutura Matemática do Modelo Transformer" />&lt;h1 id="introdução-por-que-aprender-a-matemática-do-transformer">Introdução: Por que aprender a matemática do Transformer?
&lt;/h1>&lt;p>Não é exagero dizer que a arquitetura &amp;ldquo;Transformer&amp;rdquo; reescreveu a história do Processamento de Linguagem Natural (NLP) e da IA moderna como um todo. Proposto pela primeira vez no artigo &amp;ldquo;Attention Is All You Need&amp;rdquo;, publicado por pesquisadores do Google em 2017, este modelo atua como o coração dos Grandes Modelos de Linguagem (LLMs) que dominam o mundo hoje, como a série GPT da OpenAI (a tecnologia base do ChatGPT), o BERT do Google e o Claude da Anthropic.&lt;/p>
&lt;p>No entanto, embora vejamos frequentemente explicações qualitativas sobre o funcionamento do Transformer, como &amp;ldquo;usar Attention (mecanismo de atenção) para entender o contexto&amp;rdquo;, a realidade é que existem surpreendentemente poucas explicações aprofundadas para iniciantes sobre a &lt;strong>estrutura matemática&lt;/strong> por trás disso. Para realmente entender como a IA processa &amp;ldquo;palavras&amp;rdquo; como &amp;ldquo;fórmulas matemáticas&amp;rdquo; e gera textos incrivelmente naturais, é essencial desvendar seus mecanismos matemáticos.&lt;/p>
&lt;p>Neste artigo, direcionado a pessoas com conhecimento básico em matemática e programação (que compreendem conceitos de matrizes e derivadas em nível de ensino médio), explicaremos de forma completa e acessível as estruturas matemáticas que formam o núcleo do Transformer: o mecanismo de &amp;ldquo;Self-Attention&amp;rdquo;, o modelo &amp;ldquo;Query, Key, Value (Q/K/V)&amp;rdquo;, a &amp;ldquo;Normalização com a função Softmax&amp;rdquo; e o &amp;ldquo;Positional Encoding&amp;rdquo;.&lt;/p>
&lt;p>Você pode se sentir sobrecarregado pela sucessão de fórmulas, mas cada cálculo tem um &amp;ldquo;significado&amp;rdquo; claro. Quando terminar de ler este artigo, você deverá compreender que o Transformer não é apenas uma caixa preta mágica, mas uma cristalização de matemática e estatística meticulosamente projetada.&lt;/p>
&lt;hr>
&lt;h1 id="1-limitações-dos-métodos-tradicionais-e-a-inovação-do-transformer">1. Limitações dos métodos tradicionais e a inovação do Transformer
&lt;/h1>&lt;p>Antes do surgimento do Transformer, a corrente principal do processamento de linguagem natural eram as Redes Neurais Recorrentes (RNN) e sua derivação, o LSTM (Long Short-Term Memory). As RNNs são projetadas para processar dados de séries temporais, lendo o texto sequencialmente, palavra por palavra, a partir do início.&lt;/p>
&lt;p>No entanto, as RNNs tinham duas fraquezas fatais:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Dificuldade em aprender dependências de longo prazo&lt;/strong>: À medida que a frase se torna mais longa, as informações das palavras inseridas no início desaparecem quando chegam ao fim (problema do desaparecimento do gradiente).&lt;/li>
&lt;li>&lt;strong>Impossibilidade de computação paralela&lt;/strong>: Como as palavras devem ser processadas sequencialmente, a computação paralela em larga escala usando GPUs é difícil, e o treinamento exige uma quantidade enorme de tempo.&lt;/li>
&lt;/ol>
&lt;p>O Transformer causou uma mudança de paradigma ao abandonar completamente a estrutura da RNN e usar apenas a &amp;ldquo;Attention&amp;rdquo; para capturar o contexto. Isso tornou possível processar sequências de qualquer tamanho sem perda de informação e, ao mesmo tempo, paralelizar os cálculos para maximizar o desempenho das GPUs.&lt;/p>
&lt;hr>
&lt;h1 id="2-arquitetura-geral-do-transformer">2. Arquitetura geral do Transformer
&lt;/h1>&lt;p>Primeiro, vamos dar uma visão geral da arquitetura do Transformer. O Transformer é amplamente dividido em dois blocos: &amp;ldquo;Encoder&amp;rdquo; (Codificador) e &amp;ldquo;Decoder&amp;rdquo; (Decodificador). Tomando uma tarefa de tradução como exemplo, o Encoder converte o idioma de entrada (ex.: inglês) em uma representação vetorial matemática, e o Decoder usa essa representação vetorial para gerar o idioma de saída (ex.: japonês).&lt;/p>
&lt;p>O diagrama abaixo é uma versão simplificada da estrutura interna do bloco Encoder.&lt;/p>
&lt;div class="mermaid">graph TD
A["Tokens de Entrada"] --> B["Input Embedding"]
B --> C["Positional Encoding"]
C --> D["Multi-Head Self-Attention"]
D --> E["Add &amp; Layer Normalization"]
E --> F["Feed Forward Network"]
F --> G["Add &amp; Layer Normalization"]
G --> H["Saída para a Próxima Camada"]
C -.->|"Conexão Residual"| E
E -.->|"Conexão Residual"| G&lt;/div>
&lt;p>A partir daqui, vamos analisar passo a passo as operações matemáticas que ocorrem em cada componente.&lt;/p>
&lt;hr>
&lt;h1 id="3-vetorização-de-palavras-e-positional-encoding-codificação-posicional">3. Vetorização de palavras e Positional Encoding (Codificação Posicional)
&lt;/h1>&lt;p>Os computadores não podem entender o texto como ele é. O texto de entrada é primeiro dividido em unidades chamadas &amp;ldquo;Tokens&amp;rdquo;, e cada um é convertido em um vetor de comprimento fixo. Este é o &lt;strong>Input Embedding&lt;/strong>.&lt;/p>
&lt;h2 id="31-a-matemática-do-input-embedding">3.1 A matemática do Input Embedding
&lt;/h2>&lt;p>Seja $V$ o tamanho do vocabulário e $d_{model}$ a dimensão do vetor de embedding (no artigo original, $d_{model} = 512$). Cada palavra $w_i$ é convertida em um vetor $x_i \in \mathbb{R}^{d_{model}}$ usando a matriz de embedding $W_E \in \mathbb{R}^{V \times d_{model}}$.&lt;/p>
$$ x_i = W_E \cdot \text{one\_hot}(w_i) $$
&lt;p>Desta forma, o texto inteiro é representado como uma matriz $X \in \mathbb{R}^{N \times d_{model}}$ (onde $N$ é o comprimento da frase).&lt;/p>
&lt;h2 id="32-a-necessidade-e-as-fórmulas-do-positional-encoding">3.2 A necessidade e as fórmulas do Positional Encoding
&lt;/h2>&lt;p>O Transformer não processa palavras sequencialmente como uma RNN, mas processa todas as palavras simultaneamente em paralelo. Esta é uma grande vantagem em termos de velocidade de cálculo, mas ao mesmo tempo causa o problema de &lt;strong>perda da informação crucial sobre a &amp;ldquo;ordem das palavras&amp;rdquo;&lt;/strong>. Por exemplo, &amp;ldquo;o cachorro morde o homem&amp;rdquo; e &amp;ldquo;o homem morde o cachorro&amp;rdquo; têm o mesmo conjunto de palavras de entrada, mas os significados são completamente diferentes.&lt;/p>
&lt;p>O que foi idealizado para fornecer essa informação sobre a ordem das palavras ao modelo é o &lt;strong>Positional Encoding&lt;/strong>.
O Positional Encoding $PE$ para a $i$-ésima dimensão de uma palavra na posição $pos$ é calculado usando as seguintes funções 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>Aqui, $pos$ é a posição da palavra ($0, 1, 2, \dots, N-1$) e $i$ é o índice da dimensão do vetor ($0, 1, \dots, d_{model}/2 - 1$).&lt;/p>
&lt;h3 id="por-que-usar-seno-e-cosseno">Por que usar seno e cosseno?
&lt;/h3>&lt;p>À primeira vista, parecem fórmulas muito complexas e estranhas, mas há uma razão matemática profunda para isso. Ao usar funções trigonométricas, o modelo pode aprender facilmente &lt;strong>não apenas a &amp;ldquo;posição absoluta&amp;rdquo;, mas também a diferença na &amp;ldquo;posição relativa&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Lembre-se do teorema de adição para funções trigonométricas aprendido na matemática do ensino médio:
&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>O Positional Encoding de uma posição $pos + k$ deslocada por um offset $k$ a partir de uma certa posição $pos$ pode ser expresso como uma combinação linear do Positional Encoding da posição $pos$. Em outras palavras, pode ser escrito da seguinte forma usando uma matriz $M_k$:&lt;/p>
$$ PE_{pos+k} = M_k \cdot PE_{pos} $$
&lt;p>Com isso, o mecanismo de Attention pode reconhecer facilmente a distância relativa, ou seja, &amp;ldquo;o quão distantes&amp;rdquo; as palavras estão umas das outras, através do cálculo do produto escalar. Além disso, ao combinar múltiplas ondas de seno e cosseno com comprimentos de onda diferentes, há a vantagem de poder gerar vetores de posição únicos, não importa quão longa seja a frase.&lt;/p>
&lt;p>A matriz de entrada final $X_{input}$ é a soma dos vetores de embedding das palavras e deste Positional Encoding.&lt;/p>
$$ X_{input} = X + PE $$
&lt;hr>
&lt;h1 id="4-a-profunda-matemática-do-self-attention-mecanismo-de-auto-atenção">4. A profunda matemática do Self-Attention (Mecanismo de Auto-Atenção)
&lt;/h1>&lt;p>Agora vamos entrar no componente mais importante do Transformer: o &lt;strong>Self-Attention&lt;/strong>. O objetivo do Self-Attention é &amp;ldquo;calcular o grau de relacionamento entre todas as palavras na frase e atualizar o vetor de cada palavra para uma representação mais rica que leve em conta o contexto&amp;rdquo;.&lt;/p>
&lt;p>Aqui, usamos a analogia de um &amp;ldquo;sistema de busca&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Query (Q)&lt;/strong>: Consulta (termo de busca). &amp;ldquo;Qual informação estou procurando agora?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Key (K)&lt;/strong>: Chave (título). &amp;ldquo;Quais informações eu tenho?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>Value (V)&lt;/strong>: Valor (entidade). &amp;ldquo;Qual informação eu realmente forneço?&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 id="41-geração-das-matrizes-q-k-v">4.1 Geração das matrizes $Q, K, V$
&lt;/h2>&lt;p>Para a matriz de entrada $X \in \mathbb{R}^{N \times d_{model}}$ (ignoraremos o tamanho do batch aqui por simplicidade), calculamos as consultas $Q$, chaves $K$ e valores $V$ multiplicando pelas matrizes de pesos treináveis $W^Q, W^K, W^V \in \mathbb{R}^{d_{model} \times d_k}$. (Geralmente $d_k = d_v = d_{model} / h$)&lt;/p>
$$ Q = X W^Q $$
$$ K = X W^K $$
$$ V = X W^V $$
&lt;p>Aqui, $Q, K, V$ são todas matrizes em $\mathbb{R}^{N \times d_k}$.&lt;/p>
&lt;h2 id="42-cálculo-do-attention-score-produto-escalar">4.2 Cálculo do Attention Score (Produto Escalar)
&lt;/h2>&lt;p>Para medir o quão relacionada está a Query de cada palavra com as Keys de todas as outras palavras, calculamos o &lt;strong>produto escalar&lt;/strong> dos vetores. Escrito como uma operação de matriz, fica da seguinte forma:&lt;/p>
$$ \text{Scores} = Q K^T $$
&lt;p>Cada elemento $s_{ij}$ da matriz $\text{Scores} \in \mathbb{R}^{N \times N}$ obtida por este cálculo representa o produto escalar da Query da $i$-ésima palavra e a Key da $j$-ésima palavra, ou seja, a &amp;ldquo;força da relação&amp;rdquo;.&lt;/p>
&lt;h2 id="43-dimensionamento-scale">4.3 Dimensionamento (Scale)
&lt;/h2>&lt;p>Existe um problema com o cálculo da pontuação por produto escalar. Quando a dimensão do vetor $d_k$ aumenta, o valor do produto escalar pode se tornar extremamente grande ou pequeno.&lt;/p>
&lt;p>Vamos provar isso matematicamente.
Suponha que cada elemento de uma consulta $q \sim \mathcal{N}(0, 1)$ e cada elemento de uma chave $k \sim \mathcal{N}(0, 1)$ sigam distribuições normais padrão independentes.
Calculamos a média e a variância do produto escalar $q \cdot k = \sum_{i=1}^{d_k} q_i k_i$.
Média: Como $\mathbb{E}[q_i k_i] = \mathbb{E}[q_i] \mathbb{E}[k_i] = 0 \times 0 = 0$, a média da soma também é $0$.
Variância: Devido à independência, a variância de $q_i k_i$ é $\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$.
Portanto, a variância de todo o produto escalar será igual à dimensão $d_k$.&lt;/p>
$$ \text{Var}(q \cdot k) = d_k $$
&lt;p>Se a variância se tornar grande, na função Softmax aplicada posteriormente, o gradiente para valores além do valor máximo se tornará extremamente pequeno (ocorrendo o &amp;ldquo;desaparecimento do gradiente&amp;rdquo;), e o aprendizado não progredirá.
Para evitar isso, dividimos as pontuações por $\sqrt{d_k}$ (escalonamento) para manter a variância sempre em $1$.&lt;/p>
$$ \text{Scaled Scores} = \frac{Q K^T}{\sqrt{d_k}} $$
&lt;h2 id="44-transformação-em-probabilidades-com-a-função-softmax">4.4 Transformação em probabilidades com a função Softmax
&lt;/h2>&lt;p>Aplicamos a &lt;strong>função Softmax&lt;/strong> a cada linha para converter as pontuações obtidas em uma distribuição de probabilidade (pesos) cuja soma seja $1$.&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>A matriz $A \in \mathbb{R}^{N \times N}$ é chamada de matriz de Attention Weight (Pesos de Atenção). Observando cada linha $i$ desta matriz, vemos expressa em valores de 0 a 1 &amp;ldquo;quanta atenção (Attention) deve ser dada a qualquer outra palavra $j$ a fim de compreender a palavra $i$&amp;rdquo;.&lt;/p>
&lt;h2 id="45-soma-ponderada-dos-values">4.5 Soma ponderada dos Values
&lt;/h2>&lt;p>Finalmente, usando a matriz de Attention Weight $A$ obtida, calculamos a soma ponderada da matriz Value $V$.&lt;/p>
$$ \text{Output} = A V = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>A matriz $Z \in \mathbb{R}^{N \times d_v}$ gerada por esta operação é um conjunto de &amp;ldquo;representações vetoriais de palavras atualizadas considerando o contexto&amp;rdquo;.
Esta é toda a estrutura do &lt;strong>Scaled Dot-Product Attention&lt;/strong> definida no artigo.&lt;/p>
&lt;hr>
&lt;h1 id="5-multi-head-attention-atenção-multi-cabeças">5. Multi-Head Attention (Atenção Multi-Cabeças)
&lt;/h1>&lt;p>Com apenas um cálculo de Attention (single head), o contexto pode ser capturado sob apenas uma perspectiva (por exemplo, &amp;ldquo;relação gramatical&amp;rdquo;). Portanto, para capturar simultaneamente as diversas relações semânticas e sintáticas da linguagem (como &amp;ldquo;sujeito e predicado&amp;rdquo;, &amp;ldquo;pronome e seu referente&amp;rdquo;, etc.), o &lt;strong>Multi-Head Attention&lt;/strong> foi introduzido.&lt;/p>
&lt;p>O processo de geração de $Q, K, V$ e o cálculo de Attention vistos anteriormente são realizados em paralelo $h$ vezes (o número de cabeças. No artigo original, $h=8$).&lt;/p>
$$ \text{head}_i = \text{Attention}(X W_i^Q, X W_i^K, X W_i^V) $$
&lt;p>Aqui, $W_i^Q, W_i^K, W_i^V \in \mathbb{R}^{d_{model} \times d_k}$ são matrizes de pesos treináveis dedicadas à $i$-ésima cabeça.&lt;/p>
&lt;p>Os resultados produzidos por cada cabeça, $\text{head}_i \in \mathbb{R}^{N \times d_v}$, são concatenados (Concatenate) horizontalmente.&lt;/p>
$$ \text{Concat}(\text{head}_1, \dots, \text{head}_h) \in \mathbb{R}^{N \times (h \cdot d_v)} $$
&lt;p>Normalmente, isso é configurado de modo que $h \cdot d_v = d_{model}$, para que a dimensão após a concatenação volte a ser $d_{model}$, assim como na entrada. Por fim, multiplicamos esta matriz por uma matriz de pesos $W^O \in \mathbb{R}^{d_{model} \times d_{model}}$ para obter a saída 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["Cabeça 1"]
X --> Q2["Q2"]
X --> K2["K2"]
X --> V2["V2"]
Q2 &amp; K2 &amp; V2 --> H2["Cabeça 2"]
X --> QN["..."]
X --> KN["..."]
X --> VN["..."]
QN &amp; KN &amp; VN --> HN["Cabeça h"]
H1 &amp; H2 &amp; HN --> C["Concatenar"]
C --> WO["Multiplicar por WO"]
WO --> OUT["Saída Multi-Head"]&lt;/div>
&lt;hr>
&lt;h1 id="6-feed-forward-neural-network-ffn">6. Feed-Forward Neural Network (FFN)
&lt;/h1>&lt;p>A saída do Multi-Head Attention é então inserida em uma &lt;strong>Position-wise Feed-Forward Network (FFN)&lt;/strong>.
Esta é uma rede neural de duas camadas totalmente conectada aplicada &amp;ldquo;independentemente a cada posição (palavra)&amp;rdquo; da sequência.&lt;/p>
&lt;p>Expresso em uma fórmula matemática, isso se parece com o seguinte:&lt;/p>
$$ \text{FFN}(x) = \max(0, x W_1 + b_1) W_2 + b_2 $$
&lt;p>Aqui, $\max(0, z)$ representa a função de ativação ReLU (Rectified Linear Unit) (em modelos recentes, GELU ou SwiGLU são frequentemente utilizados).&lt;/p>
&lt;p>O papel dessa rede é extremamente importante. O mecanismo de Attention aprende &amp;ldquo;as relações entre as palavras (relações espaciais e sequenciais)&amp;rdquo;, enquanto a FFN é responsável pela &amp;ldquo;transformação não-linear de características de cada vetor de palavra em si&amp;rdquo;.
Normalmente, a dimensão é primeiro bastante expandida pelos pesos da primeira camada $W_1$ (por exemplo, multiplicada por 4, de $d_{model}=512$ para $d_{ff}=2048$), e depois de realizar cálculos complexos no espaço de características, ela retorna à dimensão original através dos pesos da segunda camada $W_2$. Com essa &amp;ldquo;expansão e contração de dimensões&amp;rdquo;, a expressividade do modelo aumenta drasticamente.&lt;/p>
&lt;hr>
&lt;h1 id="7-conexão-residual-residual-connection-e-layer-normalization-normalização-de-camada">7. Conexão Residual (Residual Connection) e Layer Normalization (Normalização de Camada)
&lt;/h1>&lt;p>No deep learning, à medida que a rede se torna mais profunda, pode ocorrer o problema do desaparecimento ou da explosão dos gradientes durante o treinamento, o que impede a aprendizagem adequada. Para evitar isso, uma &lt;strong>Conexão Residual (Residual Connection)&lt;/strong> e uma &lt;strong>Layer Normalization (Normalização de Camada)&lt;/strong> são colocadas em torno de cada subcamada (Attention e FFN) do Transformer.&lt;/p>
&lt;p>Matematicamente, a saída de uma subcamada é processada da seguinte forma:&lt;/p>
$$ \text{Output} = \text{LayerNorm}(x + \text{Sublayer}(x)) $$
&lt;h2 id="71-conexão-residual-x--textsublayerx">7.1 Conexão Residual ($x + \text{Sublayer}(x)$)
&lt;/h2>&lt;p>A entrada $x$ é adicionada diretamente à saída da subcamada. Com isso, os gradientes durante a retropropagação passam por um atalho (shortcut) e são transmitidos diretamente para as camadas mais superficiais, permitindo que o treinamento seja estabilizado mesmo com um aumento na profundidade das camadas.&lt;/p>
&lt;h2 id="72-a-matemática-da-layer-normalization">7.2 A matemática da Layer Normalization
&lt;/h2>&lt;p>Layer Normalization é uma técnica para normalizar os dados, calculando a média e a variância ao longo da dimensão das características. Para uma entrada com um tamanho de batch $B$, comprimento da sequência $N$ e dimensão $d_{model}$, a normalização é feita de forma independente para cada vetor de palavra $x \in \mathbb{R}^{d_{model}}$.&lt;/p>
&lt;p>Calculamos a média $\mu$ e a variância $\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>E então obtemos a saída normalizada $\hat{x}$:
&lt;/p>
$$ \text{LN}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta $$
&lt;p>
(Onde $\epsilon$ é uma pequena constante para evitar a divisão por zero. $\gamma$ e $\beta$ são parâmetros treináveis de escala e deslocamento)&lt;/p>
&lt;p>A razão pela qual se adotou a Layer Normalization em vez da Batch Normalization (normalização ao longo da dimensão do batch) é que estatísticas entre os batches tendem a ficar instáveis ao lidar com dados sequenciais de comprimentos variados, como textos. Graças à Layer Normalization, o Transformer pode alcançar um aprendizado estável, independentemente do tamanho do batch.&lt;/p>
&lt;hr>
&lt;h1 id="8-estrutura-específica-do-decoder-masked-attention-e-cross-attention">8. Estrutura específica do Decoder: Masked Attention e Cross-Attention
&lt;/h1>&lt;p>A estrutura explicada até agora diz respeito ao codificador (encoder). O bloco decodificador (decoder), que gera o texto, possui uma estrutura ligeiramente diferente.&lt;/p>
&lt;h2 id="81-masked-multi-head-attention">8.1 Masked Multi-Head Attention
&lt;/h2>&lt;p>A função do decodificador é &amp;ldquo;prever a próxima palavra com base nas palavras passadas&amp;rdquo;. Consequentemente, olhar para &amp;ldquo;palavras futuras&amp;rdquo; durante o treinamento seria trapaça. A operação matemática para evitar isso é o &lt;strong>Masking (Mascaramento)&lt;/strong>.&lt;/p>
&lt;p>À matriz de pontuações $Q K^T$, adicionamos uma matriz de máscara $M$ que define a parte triangular superior (correspondente às informações futuras) com valores extremamente pequenos, próximos de $-\infty$.&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>Quando a função Softmax é calculada, como $\exp(-\infty) = 0$, os pesos de atenção (Attention Weights) para as palavras futuras tornam-se completamente $0$. Dessa forma, é possível realizar uma geração autorregressiva que preserva a causalidade (Causality).&lt;/p>
&lt;h2 id="82-encoder-decoder-cross-attention">8.2 Encoder-Decoder Cross-Attention
&lt;/h2>&lt;p>A segunda subcamada no decodificador é a &lt;strong>Cross-Attention&lt;/strong>, que faz referência à saída do codificador.
Aqui, a Query $Q$ é gerada pela camada de decodificação anterior, mas as chaves $K$ e os valores $V$ são gerados a partir da saída da camada final do codificador.&lt;/p>
$$ Q_{decoder} = X_{dec} W^Q $$
$$ K_{encoder} = X_{enc} W^K $$
$$ V_{encoder} = X_{enc} W^V $$
&lt;p>Este cálculo permite que o modelo aprenda, por exemplo em tarefas de tradução, &amp;ldquo;a qual parte da frase no idioma estrangeiro original a palavra sendo traduzida agora está fortemente associada&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h1 id="9-complexidade-computacional-e-a-matemática-das-otimizações-modernas">9. Complexidade computacional e a matemática das otimizações modernas
&lt;/h1>&lt;p>O Transformer é um modelo excelente, mas as &amp;ldquo;fraquezas&amp;rdquo; inerentes à sua estrutura matemática também existem.
Considere a complexidade computacional do Self-Attention. O cálculo da matriz de pontuação $Q K^T$ envolve multiplicar uma matriz de tamanho $(N \times d_k)$ por uma de tamanho $(d_k \times N)$, então sua complexidade computacional é &lt;strong>$O(N^2 \cdot d_{model})$&lt;/strong>.&lt;/p>
&lt;p>Em outras palavras, &lt;strong>a quantidade de cálculo e o uso de memória crescem quadraticamente em relação ao comprimento da sequência $N$&lt;/strong>.
Quando as frases são curtas, isso não é um problema, mas se você tentar alimentar um LLM com um contexto vasto como o conteúdo de um livro inteiro, $N$ atinge dezenas a centenas de milhares, e o cálculo tradicional da Attention esgotará imediatamente a memória da GPU.&lt;/p>
&lt;p>Para quebrar essa maldição do $O(N^2)$, várias otimizações têm sido propostas nos últimos anos com base em abordagens matemáticas e de hardware.
Um exemplo representativo é o &lt;strong>FlashAttention&lt;/strong>. FlashAttention é um algoritmo que minimiza a transferência de dados (acesso à memória) entre a hierarquia de memória da GPU (SRAM e HBM) ao realizar cálculos de Attention particionados em blocos (Tiling). Embora matematicamente produza exatamente os mesmos resultados que a Attention padrão (Exact Attention), otimizações no nível do hardware proporcionaram acelerações drásticas e reduções de memória, tornando viáveis modelos de contexto longo como o GPT-4.&lt;/p>
&lt;p>Existem também muitas pesquisas ativas focadas em Sparse Attention e Linear Attention, que buscam aproximar a complexidade computacional em $O(N \log N)$ e $O(N)$.&lt;/p>
&lt;hr>
&lt;h1 id="10-exemplo-de-implementação-pseudocódigo-no-estilo-pytorch">10. Exemplo de implementação (Pseudocódigo no estilo PyTorch)
&lt;/h1>&lt;p>Quando traduzimos a estrutura matemática descrita até agora para um código de programação real (Python / PyTorch), percebemos que ela pode ser escrita de forma surpreendentemente simples. O código a seguir mostra o pseudocódigo para a parte central do 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"># Formato 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 da pontuação pelo produto escalar: Q * K^T&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Transpondo as últimas duas dimensões para calcular o produto de matrizes&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. Dimensionamento (Scaling)&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. Mascaramento (para o 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. Transformação em probabilidades pelo 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. Multiplicação pela 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 intuitivamente que a fórmula matemática $Q K^T / \sqrt{d_k}$ é implementada como &lt;code>torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)&lt;/code>. É um aspecto muito fascinante do deep learning como a teoria matemática ganha vida em apenas algumas linhas de código com o poder de bibliotecas avançadas de otimização.&lt;/p>
&lt;hr>
&lt;h1 id="conclusão-a-forma-da-inteligência-vista-através-da-matemática">Conclusão: A forma da &amp;ldquo;inteligência&amp;rdquo; vista através da matemática
&lt;/h1>&lt;p>Neste artigo, desvendamos a profunda estrutura matemática que reside dentro do modelo Transformer.&lt;/p>
&lt;p>O Embedding, que mapeia palavras em um espaço vetorial multidimensional, o Positional Encoding, que expressa a informação posicional ao combinar ondas trigonométricas, e o mecanismo de Self-Attention, que é o cálculo de produto escalar de matrizes nascido de uma analogia de recuperação de informação. Cada um desses componentes nada mais é do que o empilhamento de conceitos matemáticos básicos, como álgebra linear, cálculo diferencial, probabilidade e estatística.&lt;/p>
&lt;p>No entanto, quando essas simples operações de matrizes são acumuladas em várias camadas e, através de bilhões a centenas de bilhões de parâmetros, padrões são extraídos de bancos de dados massivos, surge uma &amp;ldquo;forma de inteligência&amp;rdquo; capaz de compreender nossas &amp;ldquo;palavras&amp;rdquo;, fazer raciocínios lógicos e, ocasionalmente, conceber ideias criativas.&lt;/p>
&lt;p>Como o título provocativo &amp;ldquo;Attention Is All You Need&amp;rdquo; sugere, a beleza dessa arquitetura - que descarta o processamento recursivo complexo e o processamento convolucional para se especializar totalmente no cálculo puro de &amp;ldquo;atenção (grau de relevância)&amp;rdquo; - encontra-se justamente na sua simplicidade matemática.&lt;/p>
&lt;p>Pode ser que no futuro surjam novas arquiteturas que superem o Transformer (como a Mamba, que é um State Space Model), mas o framework matemático de &amp;ldquo;compreensão de contexto através da Attention&amp;rdquo;, pavimentado pelo Transformer, certamente ficará gravado na história da IA para sempre.&lt;/p>
&lt;p>Se você tiver a oportunidade de usar LLMs como ChatGPT e Claude no futuro, tente imaginar os trilhões de multiplicações de matrizes $Q K^T$ sendo calculadas nos bastidores a cada segundo, bem como as probabilidades sendo geradas pela função Softmax. Sua resolução tecnológica aumentará, e você sem dúvida achará o mundo da IA ainda mais interessante.&lt;/p>
&lt;h3 id="referências">Referências
&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 artigo foi escrito como um guia para aqueles que desejam aprender os fundamentos matemáticos do processamento de linguagem natural e da IA. Sinta-se à vontade para deixar suas dúvidas ou comentários na seção de discussão abaixo!&lt;/em>&lt;/p></description></item><item><title>Explicando o mecanismo da tecnologia de quantização (GGUF) do llama.cpp</title><link>http://kenji.blog/pt/p/llama-cpp-quantization-gguf/</link><pubDate>Fri, 11 Sep 2026 00:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Explicando o mecanismo da tecnologia de quantização (GGUF) do llama.cpp" />&lt;h2 id="1-introdução-por-que-os-llms-precisam-de-quantização">1. Introdução: Por que os LLMs precisam de quantização?
&lt;/h2>&lt;p>O recente avanço dos grandes modelos de linguagem (LLMs: Large Language Models) tem sido notável, mas nos bastidores surgiram problemas sérios: o &amp;ldquo;esgotamento dos recursos computacionais&amp;rdquo; e o &amp;ldquo;gargalo da largura de banda da memória&amp;rdquo;. Por exemplo, se você carregar um modelo de 70B (70 bilhões) de parâmetros como o Llama 3 na memória usando o padrão de ponto flutuante de 16 bits (FP16), apenas os parâmetros consumirão cerca de 140 GB de VRAM/RAM. Quando o contexto (cache KV) durante a inferência é adicionado a isso, ele não funcionará a menos que se utilize múltiplos clusters de GPUs de ponta para data centers (como NVIDIA A100 80GB ou H100 80GB).&lt;/p>
&lt;p>O &lt;strong>llama.cpp&lt;/strong> e sua tecnologia central de &lt;strong>Quantização (Quantization)&lt;/strong> surgiram como salvadores para permitir que desenvolvedores individuais e dispositivos de borda (MacBooks e PCs gamers comuns) executem LLMs. Em particular, o formato de arquivo chamado &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong> e o algoritmo avançado de quantização baseado em blocos chamado &lt;strong>k-quants&lt;/strong> são métodos revolucionários que comprimem o tamanho do modelo para uma fração do seu original, enquanto minimizam a degradação da precisão do modelo (Perplexidade) ao limite absoluto.&lt;/p>
&lt;p>Neste artigo, explicaremos de forma abrangente desde os fundamentos matemáticos da quantização no llama.cpp, as diferenças em relação ao formato GGML, a estrutura detalhada do formato GGUF, até o mecanismo interno do k-quants.&lt;/p>
&lt;hr>
&lt;h2 id="2-fundamentos-matemáticos-da-quantização-quantization">2. Fundamentos Matemáticos da Quantização (Quantization)
&lt;/h2>&lt;p>No contexto de LLMs, a quantização refere-se à operação de mapear valores contínuos (ou números de ponto flutuante de alta precisão) em valores discretos com um número menor de bits (INT8, INT4, INT3, etc.).&lt;/p>
&lt;h3 id="21-fórmulas-básicas-da-quantização-linear">2.1. Fórmulas Básicas da Quantização Linear
&lt;/h3>&lt;p>A abordagem mais simples é a quantização linear (Quantização Min-Max). Suponha que o tensor de pesos original de alta precisão seja $W$, e o tensor de inteiros quantizado seja $W_q$.&lt;/p>
$$ W_q = \text{round}\left( \frac{W}{S} \right) + Z $$
&lt;p>Onde,&lt;/p>
&lt;ul>
&lt;li>$S$ é o &lt;strong>Fator de Escala (Scale Factor)&lt;/strong>, que determina o tamanho do passo (resolução) da quantização.&lt;/li>
&lt;li>$Z$ é o &lt;strong>Ponto Zero (Zero-point)&lt;/strong>, que é um valor de viés para deslocar para qual valor inteiro o número real $0.0$ corresponde após a quantização.&lt;/li>
&lt;li>$\text{round}(\cdot)$ é a função de arredondamento para o número inteiro mais próximo.&lt;/li>
&lt;/ul>
&lt;p>Através da dequantização (Dequantization), um peso real aproximado $\tilde{W}$ é restaurado durante a inferência.&lt;/p>
$$ \tilde{W} = S \times (W_q - Z) $$
&lt;h3 id="22-quantização-simétrica-vs-quantização-assimétrica">2.2. Quantização Simétrica vs Quantização Assimétrica
&lt;/h3>&lt;p>Dependendo do tratamento do ponto zero $Z$, os métodos podem ser amplamente divididos em dois tipos.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Quantização Assimétrica (Asymmetric Quantization)&lt;/strong>
Mapeia os dados usando o valor mínimo $W_{\min}$ e o valor máximo $W_{\max}$.
&lt;/p>
$$ S = \frac{W_{\max} - W_{\min}}{2^b - 1}, \quad Z = \text{round}\left(-\frac{W_{\min}}{S}\right) $$
&lt;p>
Aqui, $b$ é o número de bits da quantização (ex: para 4 bits, $2^4-1 = 15$). Como é necessário manter $Z$, a sobrecarga de cálculo e memória aumenta ligeiramente.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Quantização Simétrica (Symmetric Quantization)&lt;/strong>
Mapeia ao redor de zero usando o valor máximo absoluto dos dados ($Z=0$).
&lt;/p>
$$ S = \frac{\max(|W_{\max}|, |W_{\min}|)}{2^{b-1} - 1}, \quad Z = 0 $$
&lt;p>
A quantização inicial do llama.cpp (por exemplo, o antigo Q4_0) adotava a quantização simétrica e, por não haver o termo $Z$, tinha a vantagem de tornar o cálculo do produto escalar com instruções SIMD extremamente rápido.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-a-evolução-do-ggml-para-o-gguf-e-a-estrutura-de-arquivos">3. A Evolução do GGML para o GGUF e a Estrutura de Arquivos
&lt;/h2>&lt;p>Indispensável ao falar sobre o llama.cpp é o &lt;strong>GGML&lt;/strong>, uma biblioteca de operações de tensores escrita em C++, e o formato de arquivo &lt;strong>GGUF&lt;/strong> derivado dela.&lt;/p>
&lt;h3 id="31-os-desafios-do-ggml">3.1. Os Desafios do GGML
&lt;/h3>&lt;p>As versões iniciais do llama.cpp usavam o formato &lt;code>ggml&lt;/code> (e variantes como &lt;code>ggjt&lt;/code>). No entanto, eles tinham os seguintes problemas:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Falta de extensibilidade:&lt;/strong> Números mágicos e hiperparâmetros eram codificados diretamente com comprimentos e ordens fixas, o que causava alterações que quebravam a compatibilidade sempre que uma nova arquitetura de modelo (ex: Llama, Falcon, Mixtral, etc.) ou um novo tokenizador era adicionado.&lt;/li>
&lt;li>&lt;strong>Perda de compatibilidade com versões anteriores:&lt;/strong> O formato era atualizado com frequência, ocorrendo muitas situações em que arquivos de modelos mais antigos não podiam mais ser carregados nas versões mais recentes do llama.cpp.&lt;/li>
&lt;/ul>
&lt;h3 id="32-o-nascimento-do-formato-gguf">3.2. O Nascimento do Formato GGUF
&lt;/h3>&lt;p>Introduzido em agosto de 2023, o &lt;strong>GGUF&lt;/strong> é um formato altamente versátil projetado para resolver esses problemas. Sua maior característica é a adoção de uma &lt;strong>estrutura de metadados baseada em Chave-Valor (Key-Value)&lt;/strong>.&lt;/p>
&lt;p>O diagrama Mermaid a seguir é uma abstração da estrutura de um arquivo GGUF.&lt;/p>
&lt;div class="mermaid">graph TD
A["Arquivo GGUF"] --> B["Cabeçalho (Magia, Versão)"]
A --> C["Metadados (Pares Chave-Valor)"]
A --> D["Informações do Tensor (Nome, Forma, Deslocamento)"]
A --> E["Dados do Tensor (Carga binária)"]
C --> C1["general.architecture: llama"]
C --> C2["llama.context_length: 4096"]
C --> C3["tokenizer.ggml.tokens: [...]"]
E --> E1["Pesos da Camada 0"]
E --> E2["Pesos da Camada 1"]
E --> E3["..."]&lt;/div>
&lt;p>&lt;strong>Principais vantagens do GGUF:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Flexibilidade:&lt;/strong> Todos os hiperparâmetros do modelo, configurações de RoPE (Rotary Positional Embedding), dados de vocabulário do tokenizador, etc., são armazenados como pares de Chave-Valor nomeados. Chaves desconhecidas são ignoradas, facilitando a adição de novos recursos.&lt;/li>
&lt;li>&lt;strong>Independência de Endianness:&lt;/strong> O GGUF adota o little-endian por padrão, mas possui um sinalizador (flag) explícito, de modo que é portável de forma segura entre arquiteturas diferentes.&lt;/li>
&lt;li>&lt;strong>Otimização para mmap (mapeamento de memória):&lt;/strong> Os dados do tensor são alinhados (com preenchimento) em limites específicos e podem ser mapeados diretamente do disco para o espaço de memória usando a chamada de sistema &lt;code>mmap()&lt;/code> do SO. Com isso, o tempo de inicialização para carregar o modelo é virtualmente zero.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-o-abismo-dos-k-quants-quantização-avançada-baseada-em-blocos">4. O Abismo dos k-quants: Quantização Avançada Baseada em Blocos
&lt;/h2>&lt;p>O verdadeiro valor do formato GGUF é o mecanismo chamado &lt;strong>k-quants (K-quantization)&lt;/strong>, que é responsável por comprimir os pesos do modelo.&lt;/p>
&lt;p>Geralmente, os pesos de uma rede neural têm uma forma próxima de uma distribuição normal quando se observa toda a camada, mas localmente existem valores atípicos (Outliers). Se você quantizar os pesos de toda a camada com um fator de escala uniforme $S$, as informações dos pesos menores serão completamente perdidas, arrastadas pelos valores atípicos.&lt;/p>
&lt;p>Para evitar isso, o llama.cpp realiza a &lt;strong>quantização baseada em blocos (Block-wise Quantization)&lt;/strong>. Os tensores de pesos são divididos em pequenos blocos (por exemplo, 32 elementos ou 256 elementos), e cada bloco tem seu próprio fator de escala (e ponto zero).&lt;/p>
&lt;h3 id="41-limitações-da-quantização-legada-q4_0-q4_1">4.1. Limitações da Quantização Legada (Q4_0, Q4_1)
&lt;/h3>&lt;p>O &lt;code>Q4_0&lt;/code> inicial agrupava 32 pesos FP16 em um bloco e compartilhava um fator de escala FP16.&lt;/p>
&lt;ul>
&lt;li>Tamanho do bloco: 32&lt;/li>
&lt;li>Memória: 1 escala (16 bits) + 32 pesos de 4 bits (128 bits) = 144 bits&lt;/li>
&lt;li>Bits efetivos por elemento (bpw: bits per weight): $144 / 32 = 4.5$ bpw&lt;/li>
&lt;/ul>
&lt;p>Embora isso já seja bastante bom, os limites de precisão e taxa de compressão tornaram-se evidentes. Foi então que surgiram os &lt;strong>k-quants&lt;/strong>, que possuem uma estrutura hierárquica mais complexa e refinada.&lt;/p>
&lt;h3 id="42-estrutura-hierárquica-de-super-blocos-e-sub-blocos-exemplo-do-q4_k_m">4.2. Estrutura Hierárquica de Super-blocos e Sub-blocos (Exemplo do Q4_K_M)
&lt;/h3>&lt;p>O k-quants tem uma estrutura hierárquica com um grande &amp;ldquo;Super-bloco (Super-block)&amp;rdquo; e pequenos &amp;ldquo;Sub-blocos (Sub-blocks)&amp;rdquo; contidos dentro dele. Isso permite quantizar até mesmo os próprios metadados (como os valores de escala), mantendo a precisão enquanto reduz o bpw ao limite extremo.&lt;/p>
&lt;p>Vejamos a estrutura do &lt;strong>Q4_K_M&lt;/strong>, a configuração mais popular. O Q4_K_M usa super-blocos de 256 elementos.&lt;/p>
&lt;div class="mermaid">graph TD
A["Super-bloco (256 pesos)"] --> B["Metadados de escala (FP16/INT8)"]
A --> C["Sub-bloco 0 (32 pesos, 4 bits)"]
A --> D["Sub-bloco 1 (32 pesos, 4 bits)"]
A --> E["..."]
A --> F["Sub-bloco 7 (32 pesos, 4 bits)"]
B --> B1["Super-escala (FP16)"]
B --> B2["Sub-escalas (8 x 6 bits)"]
B --> B3["Sub-mínimos (8 x 6 bits)"]&lt;/div>
&lt;p>A estrutura real em C++ (GGML) é definida conceitualmente da seguinte forma:&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">// Estrutura conceitual de block_q4_K no 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">// Super-escala de todo o super-bloco (ex: FP16 x 2)
&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">// Dados empacotados com a escala de 6 bits e valor mínimo de 6 bits (ponto zero) de 8 sub-blocos (32 elementos cada)
&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">// Dados de pesos quantizados em 4 bits (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>Processo matemático de dequantização:&lt;/strong>&lt;/p>
&lt;p>O valor real aproximado $\tilde{W}_{i, j}$ do elemento $j$ ($0 \le j &lt; 32$) dentro do sub-bloco $i$ ($0 \le i &lt; 8$) é calculado da seguinte forma:&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 ponto flutuante de todo o super-bloco&lt;/li>
&lt;li>$s_i$: Escala quantizada em 6 bits para o sub-bloco $i$&lt;/li>
&lt;li>$m_i$: Valor mínimo quantizado em 6 bits (ponto zero) para o sub-bloco $i$&lt;/li>
&lt;li>$w_{i, j}$: Peso quantizado de 4 bits ($0 \dots 15$)&lt;/li>
&lt;/ul>
&lt;p>Com essa estrutura hierárquica, mantendo a capacidade de adaptação a valores atípicos, a quantidade de memória ocupada pelos próprios fatores de escala é drasticamente reduzida. O Q4_K_M alcança no geral cerca de &lt;strong>4.8 bpw&lt;/strong>.&lt;/p>
&lt;h3 id="43-diversas-opções-dos-k-quants">4.3. Diversas Opções dos k-quants
&lt;/h3>&lt;p>O llama.cpp oferece muitas variações dependendo do propósito. O sufixo após o &amp;ldquo;K&amp;rdquo; (S, M, L) indica o tamanho.&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">Visão Geral e 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">Compressão extrema. Queda significativa na precisão, mas ideal para ambientes com VRAM extremamente baixa.&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">Padrão para quantização de 3 bits. Piora em relação ao Q4, mas frequentemente fica dentro de limites aceitáveis.&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>Ponto ideal recomendado&lt;/strong>. Equilibra a redução pela metade do tamanho do modelo com a manutenção da precisão.&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">Quando é necessária maior precisão. Posição intermediária entre o Q4 e o 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">Mantém uma Perplexidade quase equivalente à do FP16, mas com um tamanho de arquivo maior.&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 ao INT8. Usado principalmente para tensores intermediários em cálculos durante a inferência ou apenas nas camadas finais.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;em>Nota: O BPW real é nivelado (média) em todo o modelo através de uma Quantização Mista (Mixed Quantization) dependendo do tipo do tensor no modelo (por exemplo, se é uma projeção Q/K/V da Attention ou os pesos da FFN). Otimizações são feitas internamente, como quantizar tensores importantes em Q6 e o resto em Q4.&lt;/em>&lt;/p>
&lt;hr>
&lt;h2 id="5-otimização-do-desempenho-de-inferência-simd-e-arquitetura-cuda">5. Otimização do Desempenho de Inferência: SIMD e Arquitetura CUDA
&lt;/h2>&lt;p>Apenas carregar um modelo GGUF na memória não torna a inferência rápida. A maior parte da inferência de LLMs é a &amp;ldquo;Multiplicação de Matrizes (Matrix-Vector Multiplication, abreviado como GEMV, ou Matrix-Matrix, GEMM)&amp;rdquo;. A chave é como acelerar a operação de produto e soma dos pesos quantizados com as ativações (dados de entrada) mantidas em FP16 (ou FP32).&lt;/p>
&lt;h3 id="51-utilização-de-instruções-simd-em-ambiente-cpu">5.1. Utilização de Instruções SIMD em Ambiente CPU
&lt;/h3>&lt;p>A razão pela qual o llama.cpp possui uma velocidade incrível na inferência por CPU se deve à otimização &lt;strong>SIMD (Single Instruction, Multiple Data)&lt;/strong> em nível de assembly.
Por exemplo, as CPUs Intel/AMD utilizam totalmente os conjuntos de instruções &lt;strong>AVX2&lt;/strong> ou &lt;strong>AVX-512&lt;/strong>, enquanto o Apple Silicon utiliza as instruções &lt;strong>ARM NEON&lt;/strong>.&lt;/p>
&lt;p>Durante a inferência, $W_q$ não é intencionalmente convertido de volta para FP32 (dequantizado) antes de fazer a multiplicação.
O lado das ativações também é quantizado dinamicamente bloco a bloco (Dynamic Quantization, geralmente para INT8), e a operação com inteiros de &lt;strong>INT8 $\times$ INT4&lt;/strong> é calculada de uma vez usando instruções SIMD especiais de produto escalar (ex: &lt;code>vdpaddd&lt;/code> ou &lt;code>_mm256_madd_epi16&lt;/code>). Ao converter de volta para FP32 no acumulador final e multiplicar pelo fator de escala, alcança-se um throughput incrível.&lt;/p>
&lt;h3 id="52-descarregamento-offload-em-ambiente-gpu-cublas--cuda">5.2. Descarregamento (Offload) em Ambiente GPU (cuBLAS / CUDA)
&lt;/h3>&lt;p>Recentemente, o llama.cpp não possui apenas suporte para CPU, mas também um suporte robusto para GPUs NVIDIA (CUBLAS / CUDA).
É possível descarregar parte ou a totalidade das camadas do arquivo GGUF para a VRAM (através da opção &lt;code>--n-gpu-layers&lt;/code>).&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as Usuário
participant CPU_RAM as CPU &amp; RAM (mmap)
participant VRAM as VRAM da GPU
participant Compute as Tensor Cores
User->>CPU_RAM: Carregar GGUF (mmap)
CPU_RAM->>VRAM: Descarregar Camadas (ex: 30/32 camadas)
Note over CPU_RAM, VRAM: Os dados permanecem quantizados na VRAM
User->>Compute: Forward Pass (Tokens de Entrada)
Compute->>VRAM: Buscar Pesos Quantizados
Compute->>Compute: Dequantizar on-the-fly para FP16 na SRAM
Compute->>Compute: Multiplicação de Matrizes (cuBLAS / Kernels Personalizados)
Compute->>User: Saída (Logits)&lt;/div>
&lt;p>Ao processar na GPU, a largura de banda de memória (Memory Bandwidth) da VRAM se torna o maior gargalo. Como os pesos estão comprimidos com k-quants, a quantidade de dados transferida da VRAM para as unidades de computação da GPU (SM: Streaming Multiprocessor ou Tensor Cores) é reduzida a cerca de 1/3 ~ 1/4. No exato momento em que os pesos chegam à unidade de cálculo, eles são dequantizados (descompactados) on-the-fly para FP16, e a multiplicação de matrizes é executada em altíssima velocidade usando o Tensor Core.
Em outras palavras, pode-se dizer que a quantização não é feita para &amp;ldquo;reduzir a quantidade de cálculos&amp;rdquo;, mas sim para &lt;strong>&amp;ldquo;reduzir a quantidade de transferências de memória&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="6-exemplo-concreto-do-trade-off-entre-uso-de-memória-e-desempenho">6. Exemplo Concreto do Trade-off entre Uso de Memória e Desempenho
&lt;/h2>&lt;p>Abaixo, usando o modelo Llama 3 8B como exemplo, vejamos as especificações exigidas por nível de quantização do GGUF. (Os valores são apenas estimativas aproximadas)&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Modelo/Quantização&lt;/th>
&lt;th style="text-align:left">Tamanho do Arquivo&lt;/th>
&lt;th style="text-align:left">VRAM/RAM Necessária&lt;/th>
&lt;th style="text-align:left">Velocidade de Inferência (Estimativa)&lt;/th>
&lt;th style="text-align:left">Degradação da Perplexidade&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 ou mais&lt;/td>
&lt;td style="text-align:left">Base&lt;/td>
&lt;td style="text-align:left">Nenhuma (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 ou mais&lt;/td>
&lt;td style="text-align:left">Rápida&lt;/td>
&lt;td style="text-align:left">Quase zero&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 ou mais&lt;/td>
&lt;td style="text-align:left">Muito 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 ou mais&lt;/td>
&lt;td style="text-align:left">Mais rápida e ideal&lt;/td>
&lt;td style="text-align:left">Aceitável e pequena&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 ou mais&lt;/td>
&lt;td style="text-align:left">Mais rápida&lt;/td>
&lt;td style="text-align:left">Um pouco perceptível&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 ou mais&lt;/td>
&lt;td style="text-align:left">Rápida&lt;/td>
&lt;td style="text-align:left">Degradação óbvia&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Atenção (O impacto do cache KV):&lt;/strong>
Na inferência de LLMs, conforme o tamanho do contexto (número de tokens no prompt) aumenta, o consumo de memória do &lt;strong>cache KV&lt;/strong>, que guarda os estados passados da Attention além dos pesos do modelo, aumenta explosivamente.
Por exemplo, com um contexto de 8192 tokens, apenas o cache KV já consome vários GBs. Portanto, para o uso real, é necessário garantir uma margem (Headroom) de &lt;code>tamanho do arquivo do modelo + aprox. 1.5GB a 3GB&lt;/code>. O motivo pelo qual o Q4_K_M é recomendado é que ele atinge a medida exata para rodar com segurança numa placa de vídeo comum com 8GB de VRAM (como RTX 3060 / 4060) mesmo com essa alocação de cache KV.&lt;/p>
&lt;p>Em versões recentes do llama.cpp, foi adicionada a funcionalidade para &lt;strong>quantizar o próprio cache KV em Q8_0 ou Q4_0&lt;/strong>, de modo que há um esforço constante para possibilitar contextos ainda mais longos.&lt;/p>
&lt;hr>
&lt;h2 id="7-conclusão">7. Conclusão
&lt;/h2>&lt;p>Neste artigo, exploramos profundamente a estrutura interna do formato GGUF e da tecnologia de quantização k-quants, que formam o coração do llama.cpp.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Flexibilidade do GGUF:&lt;/strong> O uso de uma estrutura de metadados do tipo chave-valor construiu um ecossistema robusto, capaz de acompanhar a rápida evolução dos LLMs (surgimento de novas arquiteturas) sem causar quebras de compatibilidade.&lt;/li>
&lt;li>&lt;strong>Compressão Extrema via k-quants:&lt;/strong> O gerenciamento hierárquico dos fatores de escala através de super-blocos e sub-blocos permitiu uma incrível taxa de compressão, alcançando em média 4.8 bits por peso (Q4_K_M), mantendo as informações de valores atípicos.&lt;/li>
&lt;li>&lt;strong>Resolução do Gargalo da Largura de Banda de Memória:&lt;/strong> Implementações avançadas de kernels no SIMD e em CUDA permitiram a realização dos cálculos combinados com a dequantização on-the-fly, o que reduziu drasticamente o tráfego da VRAM e impulsionou imensamente a velocidade de inferência.&lt;/li>
&lt;/ol>
&lt;p>Pode-se dizer sem exagero que a destreza técnica do llama.cpp em promover a democratização da IA vai além da simples criação de uma ferramenta, situando-se em um dos mais altos níveis da engenharia de software contemporânea. Ao compreender a mecânica dos algoritmos de quantização e o formato GGUF, você poderá selecionar o modelo mais adequado e ajustar com precisão a performance de acordo com o seu ambiente.&lt;/p>
&lt;h3 id="links-de-referência">Links de Referência
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>Repositório GitHub do 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"
>Especificação do 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 Implementação dos K-quants&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>(Fim)&lt;/p></description></item></channel></rss>