Que os pesos entrem na VRAM não significa que o sistema funcione
O erro mais habitual ao escolher um modelo local é comparar o tamanho do ficheiro quantizado com a memória da GPU e dar a decisão por resolvida. Esse cálculo só cobre, de forma aproximada, os pesos. Durante a inferência intervêm também a cache de chaves e valores —cache KV—, as ativações e buffers temporários, o espaço reservado pelo runtime, o contexto de cada pedido e, num serviço, os pedidos simultâneos. Uma configuração pode carregar o modelo, responder a uma pergunta curta e ainda assim falhar quando recebe um documento longo ou vários pedidos ao mesmo tempo.
A consequência prática é importante: «cabe» deve significar que completa a carga máxima prevista com uma margem medida, não que inicia uma sessão isolada. Se a margem desaparecer, o resultado pode ser um erro de memória, uma redução automática do contexto, transferência de parte do trabalho para a RAM do sistema ou uma latência muito irregular. Qual desses comportamentos ocorra depende do runtime e da sua configuração; não deve assumir-se sem o verificar.
A quantização é uma de várias alavancas. Reduzir os bits dos pesos costuma libertar memória e pode permitir usar um modelo maior, mas não elimina por si só o custo crescente da cache KV ao ampliar o contexto ou a concorrência. Também pode modificar a qualidade, o desempenho ou as rotas de execução disponíveis consoante o formato e o backend. Por isso não existe uma equivalência universal entre «4 bits», «6 bits» e «8 bits».
Este guia parte de uma carga de trabalho concreta: que comprimento de entrada deve ser aceite, quantos tokens são gerados, quantos pedidos coexistirão, que latência é útil e que erros seriam inaceitáveis. Se ainda está a escolher a família de modelos, convém rever primeiro o guia de modelos locais e, se a dúvida for entre modelos base distintos, usar a secção de comparação antes de atribuir à quantização diferenças que procedem do modelo.
Regra de decisão
Não adote uma quantização porque o modelo se carregue. Adote-a apenas se manter o contexto, a concorrência e a qualidade objetivo sob uma carga repetível, com memória livre suficiente para absorver a variação do runtime.
O que significa quantizar e que partes de um modelo se quantizam
Quantizar é representar valores reais com um conjunto pequeno e discreto de números para usar menos bits e operar com eles mais depressa, a troco de introduzir um erro de arredondamento [1]. Na quantização linear de 8 bits, a documentação do ONNX Runtime expressa-a como valor_fp32 = escala × (valor_quantizado − ponto_zero): a escala é um número real positivo e o ponto zero é o valor quantizado que representa exatamente o zero. Na variante simétrica a escala calcula-se com o maior valor absoluto; na assimétrica, com o intervalo entre o mínimo e o máximo [3]. O trabalho de Wu et al. da NVIDIA revê como escolher esses parâmetros e propõe um fluxo de 8 bits que manteve a precisão a menos de 1 % da referência em ponto flutuante em todas as redes que estudaram [2].
Num LLM não se quantiza uma única coisa, mas sim três, e cada uma comporta-se de forma distinta. Os pesos são fixos, podem ser convertidos uma vez e ocupam a maior parte da memória ao carregar. As ativações mudam com cada entrada e são mais difíceis de quantizar porque contêm valores atípicos muito grandes. A cache KV cresce com o contexto e a concorrência. A notação WxAy resume a combinação: W4A16 significa pesos de 4 bits com ativações de 16 bits, e W8A8 significa pesos e ativações de 8 bits; o vLLM, por exemplo, documenta FP8 W8A8, INT8 W8A8 e INT4 W4A16 como esquemas distintos [27].
Quantizar apenas os pesos reduz a memória e, como a geração token a token está limitada pela leitura de memória e não pelo cálculo, também pode reduzir a latência. O GPTQ e o AWQ documentam-no: o primeiro atribui as suas acelerações a um menor movimento de dados, não a operar com inteiros [5], e o segundo aponta que, em FP16, a geração tem uma intensidade aritmética de aproximadamente 1 operação por byte lido [6]. Quantizar também as ativações permite usar unidades de cálculo de inteiros ou de FP8, mas obriga a tratar os valores atípicos: no SmoothQuant, algumas ativações são cerca de 100 vezes maiores que a maioria dos valores [7], e no LLM.int8() anular as poucas dimensões atípicas piora a perplexidade entre 600 % e 1000 % [4].
A granularidade importa tanto como o número de bits. Um único fator de escala por tensor é barato, mas um único valor extremo estraga a resolução de todos os outros. Por isso os formatos práticos usam uma escala por canal ou por grupo de pesos, por exemplo a cada 128 [5][6]. Dettmers e Zettlemoyer encontraram que reduzir o tamanho do bloco, ou seja, dividir os parâmetros em blocos que se quantizam separadamente, é uma das poucas melhorias claras do equilíbrio entre bits e precisão [9]. A consequência é que «4 bits» nunca são exatamente 4 bits: o Q4_K armazena 4,5 bits por peso e o Q6_K, 6,5625 [22], e o Q4_K_M medido pelo llama.cpp no Llama 3.1 8B chega a 4,8944 bits por peso [21].
As rubricas de memória que há que separar
Uma estimação útil começa por decompor a memória em rubricas que se possam observar por separado. A primeira são os pesos do modelo. O seu tamanho depende do número de parâmetros, da representação quantizada e de metadados próprios do formato, como escalas, blocos ou estruturas auxiliares. Portanto, dividir simplesmente os parâmetros por oito, seis ou quatro oferece uma orientação, mas não substitui o tamanho real informado pelo formato e pelo runtime.
A segunda rubrica é a cache KV. Num decodificador autoregressivo, o sistema conserva chaves e valores dos tokens já processados para não os recalcular em cada token gerado. A documentação dos Transformers descreve tensores de cache com dimensões de lote, cabeças, comprimento da sequência e dimensão da cabeça. Há chaves e valores, e este armazenamento repete-se por camada. À mesma arquitetura, crescer o contexto, o lote ou a concorrência aumenta a memória necessária.
A terceira rubrica reúne ativações e buffers temporários. O seu tamanho depende do backend, da precisão de cálculo, dos kernels, do prefill de entradas longas, do comprimento da geração e de como se agrupem os pedidos. Não convém substituí-la por uma constante universal. O quarto componente é a memória não atribuída diretamente ao modelo, como contexto de execução, bibliotecas, alocadores e fragmentação. O quinto é uma margem operacional explícita: memória que não se atribui deliberadamente para tolerar picos, diferenças entre medições e carga real.
Num servidor, o lote e a concorrência merecem uma precisão adicional. Um lote pode ser o número de sequências processadas juntas num passo, enquanto a concorrência é o número de pedidos vivos. Consoante o planeador, ambos os números podem relacionar-se, mas não são intercambiáveis. Para estimar a cache deve contar-se a soma dos tokens vivos das sequências que coexistem, não apenas o comprimento máximo de um pedido.
Rubricas que devem registar-se antes de decidir
| Rubrica | O que a determina | Como verificá-la |
|---|---|---|
| Pesos | Modelo base, formato e quantização | Memória após carregar o modelo ou relatório do runtime |
| Cache KV | Camadas, cabeças KV, dimensão da cabeça, tokens vivos, dtype | Capacidade ou uso de cache e comprimento efetivo atendido |
| Ativações e temporários | Prefill, geração, lote, kernels e backend | Pico de memória durante carga representativa |
| Memória do runtime | Bibliotecas, alocador, contexto do dispositivo e fragmentação | Memória antes e depois de iniciar o processo |
| Margem | Variabilidade e carga máxima prevista | Memória livre mínima observada em testes repetidos |
Estimar pesos e cache KV antes de descarregar ou implantar
A estimação não pretende prever cada byte: pretende descartar configurações inviáveis e definir que testes merecem ser executados. Para os pesos, use o tamanho informado para o artefato concreto que se pensa carregar, não um número genérico para a família do modelo. Se só se conhecer o número de parâmetros, trate o resultado como um mínimo teórico incompleto. Os formatos de quantização armazenam informação adicional e alguns runtimes convertem ou duplicam estruturas durante a carga.
Para uma arquitetura tipo Llama, uma aproximação conceptual da cache KV por sequência é: camadas multiplicadas por dois, multiplicadas por cabeças de chave-valor, multiplicadas por comprimento da sequência, multiplicadas por dimensão da cabeça e por bytes de cada elemento de cache. O fator dois representa K e V. Para várias sequências simultâneas, somam-se os tokens residentes de cada uma. Se o runtime usar uma cache estática, pode reservar capacidade até um máximo mesmo quando o uso instantâneo for menor; se usar uma cache dinâmica, o uso pode crescer com o pedido. Ambas as estratégias requerem medição.
É crucial empregar o número de cabeças de chave-valor, nem sempre o número total de cabeças de atenção. Na atenção multi-query ou grouped-query, várias cabeças de consulta partilham projeções KV. Isto pode reduzir de forma substancial a cache face a uma arquitetura com uma projeção KV por cabeça de atenção. A configuração exata do modelo deve fornecer camadas, cabeças KV e dimensão da cabeça; não devem deduzir-se de um nome comercial.
Os bytes por elemento da cache também devem verificar-se. Quantizar os pesos não implica automaticamente quantizar a cache KV. Alguns ambientes permitem escolher um tipo de dados de cache quantizado, outros usam por omissão uma precisão distinta, e outros aplicam offloading. Estas opções mudam o orçamento de memória e podem afetar o desempenho ou o comportamento numérico. Anote a configuração real do runtime, não apenas os bits que aparecem no nome do ficheiro.
Exemplo conceptual de cálculo de cache
Suponha um modelo com 32 camadas, 8 cabeças KV, dimensão da cabeça 128 e cache de 2 bytes por elemento. Para uma sequência de 8.192 tokens: 32 × 2 × 8 × 8.192 × 128 × 2 bytes, aproximadamente 1 GiB de cache KV. Duas sequências simultâneas com esse comprimento duplicariam aproximadamente essa rubrica. É uma estimação da cache, não de toda a VRAM: faltam pesos, temporários, memória do runtime e margem. Se a cache se guardar com outro tipo de dados ou se pré-alocar, o resultado efetivo muda.
Quantizar também a cache KV: quando compensa
A fórmula anterior dá a cache KV com 2 bytes por elemento. Para ver a ordem de grandeza com arquiteturas reais, a tabela seguinte aplica a mesma fórmula aos parâmetros publicados nos ficheiros de configuração de cada modelo [30]. A diferença entre o Llama 2 7B (32 cabeças KV) e o Llama 3 8B (8 cabeças KV) é um fator 4 por token, que corresponde à atenção por grupos de consultas (GQA): várias cabeças de consulta partilham cada cabeça de chave e valor [20].
Cache KV por sequência em 16 bits, calculada com a fórmula deste artigo
| Modelo | Camadas / cabeças KV / dimensão | KV por token | Contexto | Cache por sequência |
|---|---|---|---|---|
| Llama 2 7B | 32 / 32 / 128 | 512 KiB | 4.096 tokens | 2 GiB |
| Llama 3 8B | 32 / 8 / 128 | 128 KiB | 8.192 tokens | 1 GiB |
| Llama 3.1 8B | 32 / 8 / 128 | 128 KiB | 131.072 tokens | 16 GiB |
| Llama 3.1 70B | 80 / 8 / 128 | 320 KiB | 131.072 tokens | 40 GiB |
Um detalhe da tabela importa para o dimensionamento: com 131.072 tokens de contexto, a cache de uma única sequência do Llama 3.1 8B ocupa mais que os pesos do mesmo modelo em Q4_K_M (4,58 GiB), de modo que baixar os bits dos pesos não resolve o contexto longo. No Llama 3.1 70B, 40 GiB de cache por sequência equivalem a algo mais que metade de uma GPU de 80 GB.
Quantizar a cache ataca esse termo. O KIVI propõe uma quantização assimétrica de 2 bits sem ajuste fino: as chaves quantizam-se por canal e os valores por token, porque as suas distribuições são distintas. Os seus autores informam de um uso de memória máxima 2,6 vezes menor, incluindo os pesos, lotes até 4 vezes maiores e um desempenho de entre 2,35 e 3,47 vezes em cargas reais [17]. O KVQuant combina quantização por canal das chaves, quantização antes da posição rotacional e valores atípicos isolados por vetor; com 3 bits informa de uma degradação de perplexidade inferior a 0,1 no Wikitext-2 e no C4, e de que o LLaMA-7B pode atender um contexto de 1 milhão de tokens numa única A100 de 80 GB [18]. São resultados dos autores, com os seus próprios núcleos e modelos: servem para saber o que é possível, não para prometer o que obterá no seu runtime.
As ferramentas já o expõem. Os Transformers incluem uma cache quantizada com os backends hqq (de 1 a 8 bits) e quanto (2 e 4 bits), e avisam de que pode piorar a latência quando o contexto é curto e a GPU tem memória de sobra [25]. O TensorRT-LLM permite uma cache KV em FP8 e, com quantização prévia, em NVFP4 [28], e o vLLM lista a cache KV quantizada entre as suas opções [27]. Se o seu problema for a fragmentação e o desperdício da cache, e não o tamanho de cada elemento, a abordagem é outra: o PagedAttention gere a cache KV em blocos para aproveitar melhor a memória em serviço [19]. Em qualquer caso, a regra do artigo mantém-se: verifique separadamente se a quantização afeta os pesos, a cache KV ou ambos, e meça a qualidade com contexto longo.
Métodos de quantização: de arredondar para o mais próximo a GPTQ, AWQ e GGUF
Há duas famílias. A quantização pós-treino (PTQ) converte um modelo já treinado, com pouco ou nenhum retreino e, por vezes, com um pequeno conjunto de calibração. A quantização consciente do treino (QAT) volta a treinar ou destila o modelo simulando a quantização. O ONNX Runtime recomenda recorrer à QAT quando nenhuma PTQ atinge a precisão desejada [3]. Para LLM, o LLM-QAT observa que os métodos PTQ funcionam bem até 8 bits e quebram com menos, e propõe uma destilação sem dados com gerações do próprio modelo [31]. Para quem executa modelos de terceiros em local, a decisão prática quase sempre é escolher entre artefatos já quantizados com PTQ.
A linha de base é o arredondamento para o valor mais próximo (RTN). Os outros métodos tentam reduzir o erro que o RTN comete nos poucos pesos ou ativações que mais importam. A tabela resume os mais citados e que dado verificável fornece a sua fonte.
Métodos de quantização de LLM e o que documenta cada fonte
| Método | O que quantiza | Ideia central | Dado da fonte |
|---|---|---|---|
| LLM.int8() [4] | Pesos e ativações a 8 bits | Multiplicação com escalas por vetor e decomposição mista: as dimensões atípicas (cerca de 0,1 %) mantêm-se em 16 bits | Os atípicos aparecem de forma sistemática a partir de 6.700 milhões de parâmetros; a memória baixa cerca de 50 % |
| GPTQ [5] | Apenas pesos, 3 ou 4 bits | Quantização camada a camada com informação de segunda ordem, sem retreino | Quantiza modelos de 175.000 milhões de parâmetros em cerca de 4 horas de GPU; calibra com 128 segmentos de 2.048 tokens do C4 |
| AWQ [6] | Apenas pesos, 4 bits | Protege entre 0,1 % e 1 % de canais de saída, escolhidos pela magnitude das ativações, mediante escalamento por canal, sem retropropagação | Usa um conjunto de calibração muito menor que o GPTQ na sua comparação: 16 frente a 192 sequências |
| SmoothQuant [7] | Pesos e ativações a 8 bits (W8A8) | Transfere a dificuldade das ativações para os pesos com um fator por canal; α = 0,5 é o valor habitual | Até 1,56 vezes mais rápido e cerca da metade da memória |
| QLoRA e NF4 [8] | Pesos a 4 bits para ajuste fino | Tipo NormalFloat de 4 bits, dupla quantização e otimizadores paginados | Ajusta um modelo de 65.000 milhões de parâmetros numa única GPU de 48 GB |
| k-quants de GGUF [21][22] | Pesos, por superblocos | Superblocos formados por blocos de 16 ou 32 pesos, cada um com a sua escala (de 4 a 8 bits consoante o tipo) | O Q4_K ocupa 4,5 bits por peso e o Q6_K, 6,5625 [22] |
| FP8 e formatos MX [13][14] | Pesos e ativações | Ponto flutuante de 8 bits (E4M3 e E5M2) e formatos com um fator de escala por bloco | O FP8 propõe um formato de intercâmbio de 8 bits para treinar e inferir; o MX combina escala por bloco com tipos de elemento estreitos |
| AQLM e BitNet b1.58 [15][16] | Pesos a 1–3 bits | AQLM: quantização aditiva com vários livros de códigos; BitNet b1.58: pesos ternários {−1, 0, 1} treinados assim desde o início | O BitNet b1.58 não é uma conversão de um modelo já treinado, mas sim uma receita de treino |
Três lições repetem-se nestes trabalhos. A primeira é que os pesos são a parte fácil e as ativações atípicas, a difícil: o LLM.int8() e o SmoothQuant partem do mesmo problema e resolvem-no de formas opostas, isolando os atípicos ou repartindo a sua magnitude com os pesos [4][7]. A segunda é que proteger uma fração minúscula de canais basta para recuperar grande parte da qualidade de 4 bits [6]. A terceira é que a melhoria de precisão costuma custar complexidade: quanto menos bits se usam, mais o método necessita de calibração, núcleos especializados ou ambos, e por isso o formato deve ser compatível com o seu motor, como se detalha mais à frente.
O que muda ao passar de 8 para 6 ou 4 bits
Como regra geral, reduzir a precisão dos pesos reduz a sua pegada de memória face a uma representação de maior precisão do mesmo modelo. Isto pode tornar viável uma GPU menor, deixar mais orçamento para o contexto ou permitir mais pedidos concorrentes. No entanto, a redução observada não tem por que seguir uma proporção exata de oito para seis para quatro. O empacotamento, as escalas por grupo, o formato do ficheiro, as conversões internas e os buffers do backend mudam o resultado.
A qualidade também não depende apenas do número de bits. Importam o algoritmo de quantização, o tamanho do grupo, que tensores recebem um tratamento especial, o modelo base e a tarefa. Uma quantização de 4 bits de um método pode conservar bem uma tarefa e outra de 6 bits de outro método pode não o fazer; o sentido inverso também é possível. Por essa razão, os bits servem para formular hipóteses de teste, não para certificar precisão.
A velocidade requer a mesma cautela. Menos memória pode reduzir transferências e melhorar a viabilidade num dispositivo limitado, mas um formato pode carecer de kernels eficientes num backend concreto ou requerer conversões. Aumentar o contexto pode deslocar o gargalo para a gestão da cache e o prefill. O desempenho deve medir-se com duas métricas separadas: tempo até ao primeiro token para entradas representativas e taxa de geração posterior. Um único número de tokens por segundo oculta diferenças relevantes.
Como ponto de partida, teste 8 bits quando a qualidade crítica e o orçamento o permitam; 6 bits quando for preciso recuperar uma parte significativa de memória sem saltar diretamente para a opção mais agressiva; e 4 bits quando a VRAM for a restrição dominante ou quando os testes demonstrarem que não há perda inaceitável. Estas são prioridades de ensaio, não recomendações universais.
Que poupança e que perda se mediram
Os números seguintes são medições publicadas, não estimativas deste artigo. Começam pelo tamanho: a tabela reproduz a documentação do llama.cpp para o Llama 3.1 8B. A coluna de geração procede do mesmo README, que não indica o hardware desses testes, de modo que só serve para comparar linhas entre si.
Llama 3.1 8B em GGUF: tamanho e velocidade medidos pelo llama.cpp [21]
| Formato | Bits por peso | Tamanho | Relativamente a F16 | Geração (tokens/s) |
|---|---|---|---|---|
| IQ1_S | 2,0042 | 1,87 GiB | 12,5 % | 79,73 |
| Q2_K | 3,1593 | 2,95 GiB | 19,7 % | 79,85 |
| Q3_K_M | 3,9960 | 3,74 GiB | 25,0 % | 71,68 |
| Q4_K_M | 4,8944 | 4,58 GiB | 30,6 % | 71,93 |
| Q5_K_M | 5,7036 | 5,33 GiB | 35,6 % | 67,23 |
| Q6_K | 6,5633 | 6,14 GiB | 41,0 % | 58,67 |
| Q8_0 | 8,5008 | 7,95 GiB | 53,1 % | 50,93 |
| F16 | 16,0005 | 14,96 GiB | 100 % | 29,17 |
Duas observações. A primeira é que o Q8_0 ocupa algo mais que metade do F16 e o Q4_K_M, menos de um terço: a poupança de memória segue os bits por peso. A segunda é que a velocidade não segue os bits: o IQ1_S e o Q2_K geram quase à mesma velocidade (79,73 e 79,85 tokens/s) apesar de o Q2_K usar 1,2 bits a mais por peso, e o Q3_K_M gera a uma velocidade quase idêntica à do Q4_K_M (71,68 e 71,93). Ainda assim, todos os quantizados superam com clareza o F16, que gera 29,17 tokens/s nesse mesmo teste. O estudo de Jin et al. resume-o assim: a quantização poupa memória, mas também pode retardar a inferência, e tirar partido dela exige trabalho de engenharia e suporte de hardware [12].
Um segundo conjunto de medições, feitas com Transformers sobre um modelo de cerca de 15.500 milhões de parâmetros, mostra a memória máxima real face à regra de dois bytes por parâmetro.
Memória máxima de GPU de bigcode/octocoder em Transformers [26]
| Precisão | Pico de memória | Nota |
|---|---|---|
| bfloat16 | 29,03 GiB | Cerca de 2 bytes por parâmetro |
| 8 bits (LLM.int8()) | 15,22 GiB | O guia vê-o viável em GPU de consumo como a 4090; algo mais lento |
| 4 bits (NF4) | 9,54 GiB | Mudança leve na saída de exemplo; algo mais lento que 8 bits |
O guia do Hugging Face que contém essa tabela expõe uma regra útil: os pesos de um modelo de X milhares de milhões de parâmetros ocupam cerca de 2 × X GB em bfloat16 e cerca de 4 × X GB em float32 [26]. Por aritmética, com 8 bits o fator baixa para 1 × X e com 4 bits para 0,5 × X, antes de adicionar escalas, cache KV e memória do runtime.
Agora, a perda de qualidade. A tabela reúne quatro estudos com métricas distintas, pelo que as suas linhas não são comparáveis entre si: cada linha compara o método com a sua própria referência de 16 bits.
Perda de qualidade publicada pelos autores de cada método
| Estudo | Modelo e métrica | Referência 16 bits | Resultado quantizado |
|---|---|---|---|
| GPTQ [5] | OPT-175B, perplexidade no WikiText2 | 8,34 | 4 bits: RTN 10,54 e GPTQ 8,37. 3 bits: GPTQ 8,68 e RTN 7,3 × 10³ |
| AWQ [6] | Llama-2-7B, perplexidade no WikiText | 5,47 | INT4 g128: RTN 5,73, GPTQ 5,69, AWQ 5,60. INT3 g128: RTN 6,66, GPTQ 6,43, AWQ 6,24 |
| SmoothQuant [7] | OPT-175B, precisão média em tarefas | 66,9 % | W8A8 ingénuo 35,5 %; LLM.int8() 66,7 %; SmoothQuant 66,8 % |
| QLoRA [8] | MMLU médio após ajuste fino | BF16: 53,0 | NF4 com dupla quantização: 53,1; FP4: 52,2 |
Os estudos comparativos mais recentes olham para modelos e tarefas mais próximos do uso real. Kurtic et al., com mais de 500.000 avaliações sobre toda a família Llama 3.1, concluem que o FP8 (W8A8) é praticamente sem perda a qualquer tamanho, que o INT8 W8A8 bem ajustado perde entre 1 % e 3 % de precisão e que o INT4 de apenas pesos é mais competitivo do que se esperava, ao nível dos 8 bits [10]. Lee et al., com modelos de 1.000 milhões a 405.000 milhões de parâmetros e 13 conjuntos de dados, observam que os modelos pequenos podem sofrer quedas severas a 4 bits, que os de 70.000 milhões mantêm-se estáveis, que o AWQ tende a superar o GPTQ em quantização apenas de pesos e que os modelos quantizados costumam falhar mais ao seguir instruções e ao detetar alucinações [11]. Dettmers e Zettlemoyer, com mais de 35.000 experimentos, concluem que 4 bits é quase universalmente a precisão ótima para os bits totais do modelo e a sua precisão zero-shot: um modelo de 30.000 milhões de parâmetros a 8 bits e outro de 60.000 milhões a 4 bits ocupam o mesmo, mas podem render de forma muito distinta, de modo que importam ao mesmo tempo o tamanho do modelo e os bits [9].
Estes números não devem aplicar-se sem mais. Os do GPTQ, AWQ e SmoothQuant procedem de modelos de 2022 e 2023; a perplexidade no WikiText não mede o seguimento de instruções nem a ausência de alucinações; e as diferenças entre métodos podem mudar com cada família de modelos. A sua utilidade é fixar uma ordem de grandeza —8 bits quase sem perda, 4 bits aceitáveis em modelos grandes, 3 bits ou menos apenas com métodos específicos— que o seu próprio teste deve confirmar.
Árvore de decisão resumida
| Situação observada | Primeira ação | O que não deve assumir-se |
|---|---|---|
| Os pesos não cabem com margem | Testar menor precisão ou um modelo menor | Que baixar bits resolverá o custo de contexto |
| Os pesos cabem, mas falha com entradas longas | Reduzir contexto objetivo, rever cache KV ou usar mais VRAM | Que o tamanho do ficheiro prevê a capacidade de contexto |
| Falha com vários pedidos | Dimensionar por tokens vivos concorrentes e lote real | Que um teste de uma única sessão representa o serviço |
| A qualidade cai em tarefas críticas | Subir precisão, mudar método ou usar modelo menor com mais bits | Que mais parâmetros compensam qualquer perda |
| A latência é instável | Medir prefill, geração, offloading e memória livre | Que a média de tokens por segundo basta |
Procedimento de decisão: da restrição ao candidato viável
Defina primeiro o contrato operacional. Escreva o comprimento máximo de entrada que realmente deve suportar, uma reserva de tokens de saída, o número máximo de pedidos vivos, o objetivo de latência e as tarefas críticas. Diferencie um máximo excecional de um objetivo habitual. Se uma aplicação processa documentos longos, medir apenas mensagens breves não representa o seu risco de memória nem a sua qualidade útil.
Depois reúna os parâmetros da arquitetura e do runtime. Para o modelo, registe camadas, cabeças KV, dimensão da cabeça e o formato dos pesos. Para o runtime, registe o tipo de dados da cache, se reserva cache estática ou dinâmica, se permite offloading, o limite de memória de GPU e qualquer ajuste de lote ou tokens em voo. Em ferramentas de serviço, o orçamento de cache pode configurar-se explicitamente ou derivar-se de uma fração da memória disponível; ambos os casos devem constar no experimento.
Calcule uma banda, não um número único: pesos observados ou estimados, cache KV para a carga objetivo, uma reserva para temporários e uma margem. Se o total exceder a VRAM disponível antes de aplicar a margem, descarte a combinação. Se entrar por muito pouco, classifique-a como candidata de risco e teste-a sob carga máxima. Se entrar com margem, não a dê por aprovada até validar qualidade e latência.
Escolha pelo menos três candidatos que respondam a hipóteses distintas: o modelo desejado a 8 bits, a 6 bits e a 4 bits; ou, quando um candidato não fizer sentido, um modelo menor com uma precisão superior. Mantenha constantes o modelo base, a revisão, o prompt, o contexto máximo, o limite de saída, a semente quando for compatível, os parâmetros de decodificação, o hardware e a versão do runtime. Mudar várias variáveis ao mesmo tempo impede atribuir uma diferença à quantização.
Processo reproduzível em sete passos
-
Defina contexto de entrada, saída reservada, concorrência e latência objetivo.
-
Registe arquitetura, artefato de pesos e configuração de cache.
-
Estime pesos, cache KV e margem para o máximo de tokens vivos.
-
Descarte candidatos que não entrem antes da margem ou que requeiram suposições não verificadas.
-
Execute candidatos comparáveis com parâmetros idênticos.
-
Meça memória, tempo ao primeiro token, geração, erros e qualidade de saída.
-
Conserve a configuração apenas se superar o limiar de qualidade e mantiver margem sob carga máxima.
Prova mínima para detetar uma perda que importe
Uma prova útil não precisa de ser enorme, mas sim representativa. Construa um conjunto pequeno de casos que inclua o trabalho que motiva a implantação: extração estruturada, classificação, síntese de documentos, assistência de código ou respostas com restrições, consoante corresponda. Inclua entradas de comprimento habitual e algumas próximas do limite operacional. As entradas longas são necessárias porque podem revelar tanto erros de memória como perdas de seguimento de instruções ou de recuperação de detalhes.
Para cada caso, defina o que se validará antes de executar o modelo. Algumas tarefas admitem comparação exata: um esquema JSON válido, etiquetas permitidas, campos obrigatórios, uma consulta que deve conter valores concretos ou testes automatizados para código. Outras requerem revisão humana com uma grelha: fidelidade ao documento, cobertura, ausência de invenções, cumprimento de formato e utilidade. Não confunda fluidez com correção.
Fixe um limiar explícito. Por exemplo, uma configuração pode ficar descartada se incumprir mais casos críticos que o candidato de referência, se piorar a validade estrutural acima de um limite decidido pela equipa ou se produzir erros novos em dados sensíveis. O limiar pertence ao risco da aplicação; não pode inferir-se dos bits. Numa tarefa de rascunho criativo pode aceitar-se mais variação do que na extração de dados para um processo posterior.
Repita os testes. Com decodificação estocástica, várias execuções ajudam a separar variação de geração e degradação sistemática. Com decodificação determinística, as repetições continuam a ser úteis para observar estabilidade de desempenho e erros de memória. Informe resultados por tipo de tarefa e comprimento de entrada, não apenas uma média global. Uma quantização que parece equivalente na média pode concentrar as suas falhas nos documentos mais longos ou na tarefa de maior impacto.
Comparação justa
Não compare uma quantização com contexto de 4.000 tokens contra outra com 16.000, nem uma com offloading e outra sem ele, se quiser atribuir diferenças aos pesos. Se se mudar uma condição por necessidade de memória, reporte-a como uma configuração operacional distinta.
Como medir a perda: perplexidade, divergência KL e tarefas próprias
A perplexidade mede a surpresa média do modelo face a um texto de referência; calcula-se com o modelo original e com o quantizado sobre o mesmo texto, e os trabalhos citados reportam-na sobre o WikiText e o C4 [5][6][18]. A documentação do llama.cpp recomenda a perplexidade e a divergência KL entre as distribuições do modelo original e do quantizado como métricas de precisão do quantizado [21]. Jin et al. observam que a perplexidade serve como indicador substituto na maioria dos benchmarks que testaram [12], mas Lee et al. mostram que o seguimento de instruções e a deteção de alucinações ressentem-se mais do que sugerem as métricas de texto plano [11]. Por isso a perplexidade serve para descartar candidatos e a prova mínima com casos próprios continua a ser a decisão final.
A calibração é outra variável. O GPTQ calibra com 128 segmentos aleatórios de 2.048 tokens do C4 [5] e o AWQ, com 16 sequências [6]; o llama.cpp oferece uma matriz de importância (imatrix) para guiar a quantização [21]. Sem verificar: as fontes consultadas não medem como afeta calibrar com texto principalmente em inglês a qualidade em português ou outros idiomas; verifique-o com o seu corpus.
Um aviso prático do llama.cpp: voltar a quantizar um modelo que já estava quantizado, com a opção --allow-requantize, produz uma perda de qualidade grave [21]. Parta sempre dos pesos de 16 bits e anote que artefato concreto descarregou, no registo mínimo de execução.
Três perfis operacionais e as suas prioridades
Num portátil com GPU limitada, a prioridade costuma ser evitar uma configuração que dependa continuamente da RAM do sistema ou do offloading para uma experiência interativa. Comece com um contexto realista, um único pedido e um modelo ou quantização que deixe margem. Se 4 bits for a única forma de carregar o modelo, compare também um modelo menor a 6 ou 8 bits. O segundo pode ser mais estável e mais útil na tarefa concreta, apesar de ter menos parâmetros.
Numa estação de trabalho com uma GPU, há mais margem para escolher entre qualidade, contexto e velocidade, mas o limite continua a ser partilhado por pesos, cache e temporários. É um ambiente apropriado para comparar 4, 6 e 8 bits sob o mesmo corpus e para decidir se se reserva VRAM para contextos longos. Se se prever alternar entre sessões breves e análise documental, meça ambos os perfis: o resultado de uma conversa curta não dimensiona o segundo.
Num servidor com concorrência moderada, a unidade de planeamento já não é o ficheiro de modelo, mas sim a capacidade total de tokens vivos. O gestor de cache e o planeador de pedidos são parte da decisão. Uma configuração que funciona para uma única sessão pode esgotar a memória ao coincidirem prefills longos. Defina limites de admissão, comprimento máximo, reserva de saída e concorrência; depois teste rajadas e misturas de pedidos curtos e longos. As métricas de fila e percentis de latência são mais informativas que o melhor resultado isolado.
Em qualquer um dos três perfis, o offloading é uma opção que deve declarar-se, não uma solução invisível. Pode ampliar a capacidade aparente transferindo parte dos dados, mas também pode alterar a latência e depender do enlace entre CPU e GPU. Decida com medições se essa compensação é aceitável para o caso de uso.
Compatibilidade: o formato deve encaixar com o motor e a GPU
Escolher 4 bits não é uma decisão sobre o modelo, mas sim sobre o trio modelo, formato e motor. O GGUF é um formato binário que guarda os tensores e um conjunto padronizado de metadados, desenhado para o llama.cpp e ferramentas derivadas como LM Studio, GPT4All e Ollama [22]. Os outros formatos vivem em outros motores, com requisitos de hardware próprios. A tabela recolhe o que documentam as suas páginas oficiais consultadas a 17 de outubro de 2026; este campo muda depressa e convém rever a versão vigente.
O que documenta cada motor sobre quantização
| Motor | Formatos documentados | Condições assinaladas |
|---|---|---|
| llama.cpp e GGUF [21][22] | Quantizadores de cerca de 2 a 8 bits por peso nas tabelas do Llama 3.1 8B, e tipos F16 e BF16 | Quantizar a partir de pesos de 16 bits; evitar a requantização |
| Transformers com bitsandbytes [23][24] | 8 bits (LLM.int8()) e 4 bits (NF4 e FP4); a visão geral lista também AWQ, GPTQ, GGUF, AQLM, HQQ e torchao | O LLM.int8() requer GPU NVIDIA Turing ou posterior; NF4 e FP4, Pascal ou posterior |
| vLLM [27] | AWQ, GPTQ, FP8 W8A8, INT8 W8A8, INT4 W4A16, bitsandbytes, GGUF e cache KV quantizada | O AWQ não figura como compatível com Volta; FP8 W8A8 com llm-compressor figura em Ada, Hopper e GPU AMD |
| TensorRT-LLM [28] | FP4, FP8 (por tensor, por blocos e por linhas), cache KV em FP8 e NVFP4, e AWQ e GPTQ em W4A16 e W4A8 | O backend por omissão admite FP4 e FP8 em Blackwell e Hopper |
| ONNX Runtime [3] | 8 bits dinâmica e estática, e 4 bits por blocos em MatMul com RTN, GPTQ ou HQQ | Em GPU requer Tensor Cores de inteiros de 8 bits, como T4 ou A100 |
| PyTorch [29] | API torch.ao.quantization em migração para torchao | A documentação pede migrar para torchao |
Duas consequências para a decisão. A primeira: em hardware sem as instruções adequadas, quantizar pode piorar o desempenho pelo custo de quantizar e desquantizar, como avisa o ONNX Runtime [3]. A segunda: o melhor formato depende do padrão de uso. Kurtic et al. encontram que o W4A16 é o mais rentável com pedidos síncronos e que o W8A8 domina com processamento contínuo assíncrono por lotes [10]. É a mesma distinção que este artigo faz entre o portátil de uma única sessão e o servidor com concorrência.
Registo mínimo de uma execução
Anote: identificador e revisão do modelo base; artefato e método de quantização; versão e backend do runtime; GPU e memória disponível; tipo de cache KV; limite de contexto; reserva de saída; lote, concorrência e limite de tokens em voo; offloading; parâmetros de decodificação; corpus e grelha; memória livre mínima; tempo ao primeiro token; taxa de geração; erros e resultado de qualidade. Sem este registo, uma comparação posterior dificilmente será reproduzível.
Sinais para descartar uma configuração e checklist de adoção
Descarte uma configuração quando produzir erros de memória intermitentes, apesar de uma demonstração breve funcionar. A intermitência costuma indicar que a memória disponível depende da forma dos pedidos, do pico de prefill, da fragmentação ou de outras cargas do processo. Também é motivo de descarte que o runtime reduza silenciosamente o contexto efetivo, que o sistema só seja estável com uma carga inferior à prevista ou que a margem observada desapareça em testes repetidos.
A degradação de qualidade deve analisar-se por padrão. Erros concentrados em extração, cálculos, campos obrigatórios, seguimento de instruções ou documentos extensos têm mais peso que mudanças estilísticas se essas tarefas forem críticas. Uma saída aparentemente razoável mas com valores inventados não deve aprovar-se apenas por uma pontuação média. Revise também a validade do formato quando a saída alimentar software posterior.
Antes de fixar uma opção predefinida, verifique privacidade e operação. Executar inferência no equipamento não garante por si só que nenhum dado saia dele: descargas de modelos, telemetria, registo de prompts, atualização de dependências e ferramentas de observabilidade são aspetos separados. Que dados se conservam e que comunicações realiza cada componente deve verificar-se na configuração e no ambiente de rede escolhido.
O resultado final não tem por que ser «a maior quantização possível». Pode ser 6 bits para um perfil interativo, 4 bits para um perfil de análise com grande contexto, ou 8 bits para uma tarefa onde a perda detetada não seja aceitável. Mantenha as alternativas junto com o seu registo de testes. Se mudarem o runtime, o hardware, o formato de pesos ou a carga de trabalho, volte a medir: a conclusão anterior deixa de ser uma garantia.
Checklist antes de adotar uma quantização
-
Os pesos, a cache KV, temporários e a margem mediram-se ou justificaram-se por separado.
-
O contexto, a saída reservada e a concorrência refletem a carga máxima prevista.
-
Verificou-se se a quantização afeta os pesos, a cache KV ou ambos.
-
As configurações comparadas usam o mesmo modelo base e condições equivalentes.
-
O corpus contém tarefas críticas e entradas longas representativas.
-
Existe um limiar de perda aceitável definido antes de rever resultados.
-
A memória livre mínima, os erros e os percentis de latência registaram-se.
-
A configuração de offloading, telemetria, descargas e registos foi revista.
-
A decisão pode reproduzir-se a partir do registo técnico completo.
Limites da evidência e dados sem verificar
Os números do GPTQ, AWQ, SmoothQuant e LLM.int8() mediram-se em OPT, BLOOM e Llama 2; não se extrapolam para os modelos atuais. Kurtic et al. e Lee et al. usam Llama 3.x e são mais próximos, mas não cobrem todos os modelos nem idiomas.
As melhorias de velocidade do KIVI, KVQuant e AWQ (TinyChat) provêm de núcleos e GPU dos seus autores. No README do llama.cpp não se indica o hardware das medidas de velocidade nem a precisão da coluna de tamanho original, apesar de os seus números (32,1 GB para 8B) coincidirem com cerca de 4 bytes por parâmetro; esta leitura é uma inferência nossa e não figura na fonte.
Os parâmetros de arquitetura da tabela de cache KV procedem de réplicas públicas dos ficheiros config.json no Hugging Face (NousResearch e unsloth), porque os repositórios oficiais da Meta exigem autenticação. A dimensão da cabeça do Llama 3.1 70B (128) deriva de dividir o tamanho oculto pelo número de cabeças de atenção.
A tabela de compatibilidade reflete a documentação consultada a 17 de outubro de 2026, incluindo a versão em desenvolvimento do vLLM. Sem verificar: o efeito do idioma de calibração, o comportamento de cada quantizador com arquiteturas de mistura de especialistas e o desempenho na sua GPU concreta; essas três coisas requerem a sua própria medição.
Referências
-
[1] Gholami, A. et al. (2021). A Survey of Quantization Methods for Efficient Neural Network Inference. arXiv:2103.13630. https://arxiv.org/abs/2103.13630
-
[2] Wu, H. et al. (2020). Integer Quantization for Deep Learning Inference: Principles and Empirical Evaluation. arXiv:2004.09602. https://arxiv.org/abs/2004.09602
-
[3] ONNX Runtime. Quantize ONNX models. https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html
-
[4] Dettmers, T., Lewis, M., Belkada, Y., Zettlemoyer, L. (2022). LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. arXiv:2208.07339. https://arxiv.org/abs/2208.07339
-
[5] Frantar, E., Ashkboos, S., Hoefler, T., Alistarh, D. (2022). GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers. arXiv:2210.17323. https://arxiv.org/abs/2210.17323
-
[6] Lin, J. et al. (2023). AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration. arXiv:2306.00978. https://arxiv.org/abs/2306.00978
-
[7] Xiao, G. et al. (2022). SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models. arXiv:2211.10438. https://arxiv.org/abs/2211.10438
-
[8] Dettmers, T., Pagnoni, A., Holtzman, A., Zettlemoyer, L. (2023). QLoRA: Efficient Finetuning of Quantized LLMs. arXiv:2305.14314. https://arxiv.org/abs/2305.14314
-
[9] Dettmers, T., Zettlemoyer, L. (2022). The case for 4-bit precision: k-bit Inference Scaling Laws. arXiv:2212.09720. https://arxiv.org/abs/2212.09720
-
[10] Kurtic, E. et al. (2024). «Give Me BF16 or Give Me Death»? Accuracy-Performance Trade-Offs in LLM Quantization. arXiv:2411.02355. https://arxiv.org/abs/2411.02355
-
[11] Lee, J. et al. (2024). Exploring the Trade-Offs: Quantization Methods, Task Difficulty, and Model Size in Large Language Models From Edge to Giant. arXiv:2409.11055. https://arxiv.org/abs/2409.11055
-
[12] Jin, R. et al. (2024). A Comprehensive Evaluation of Quantization Strategies for Large Language Models. arXiv:2402.16775. https://arxiv.org/abs/2402.16775
-
[13] Micikevicius, P. et al. (2022). FP8 Formats for Deep Learning. arXiv:2209.05433. https://arxiv.org/abs/2209.05433
-
[14] Rouhani, B. D. et al. (2023). Microscaling Data Formats for Deep Learning. arXiv:2310.10537. https://arxiv.org/abs/2310.10537
-
[15] Ma, S. et al. (2024). The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits. arXiv:2402.17764. https://arxiv.org/abs/2402.17764
-
[16] Egiazarian, V. et al. (2024). Extreme Compression of Large Language Models via Additive Quantization. arXiv:2401.06118. https://arxiv.org/abs/2401.06118
-
[17] Liu, Z. et al. (2024). KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache. arXiv:2402.02750. https://arxiv.org/abs/2402.02750
-
[18] Hooper, C. et al. (2024). KVQuant: Towards 10 Million Context Length LLM Inference with KV Cache Quantization. arXiv:2401.18079. https://arxiv.org/abs/2401.18079
-
[19] Kwon, W. et al. (2023). Efficient Memory Management for Large Language Model Serving with PagedAttention. arXiv:2309.06180. https://arxiv.org/abs/2309.06180
-
[20] Ainslie, J. et al. (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. arXiv:2305.13245. https://arxiv.org/abs/2305.13245
-
[21] ggml-org. llama.cpp, tools/quantize/README.md. https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md
-
[22] Hugging Face. GGUF (documentação do Hub). https://huggingface.co/docs/hub/gguf
-
[23] Hugging Face. Quantization (documentação de Transformers). https://huggingface.co/docs/transformers/main/en/quantization/overview
-
[24] Hugging Face. bitsandbytes (documentação de Transformers). https://huggingface.co/docs/transformers/main/en/quantization/bitsandbytes
-
[25] Hugging Face. KV cache strategies (documentação de Transformers). https://huggingface.co/docs/transformers/en/kv_cache
-
[26] Hugging Face. Optimizing LLMs for speed and memory. https://huggingface.co/docs/transformers/main/en/llm_tutorial_optimization
-
[27] vLLM. Quantization. https://docs.vllm.ai/en/latest/features/quantization/
-
[28] NVIDIA. TensorRT LLM: Quantization. https://nvidia.github.io/TensorRT-LLM/features/quantization.html
-
[29] PyTorch. Quantization (documentação 2.14). https://docs.pytorch.org/docs/2.14/quantization.html
-
[30] Hugging Face. Ficheiros config.json de Llama 2 7B, Llama 3 8B, Llama 3.1 8B e 70B (réplicas NousResearch e unsloth). https://huggingface.co/unsloth/Meta-Llama-3.1-8B/blob/main/config.json
-
[31] Liu, Z. et al. (2023). LLM-QAT: Data-Free Quantization Aware Training for Large Language Models. arXiv:2305.17888. https://arxiv.org/abs/2305.17888
Continue a explorar
Fontes consultadas
Correções e transparência
Se encontrar informação incorreta ou desatualizada, envie-nos a página e a fonte que devemos rever.
Propor uma correção