Quantização

Quantização é uma família de técnicas que representa tensores de redes neurais (pesos, ativações e, às vezes, gradientes ou caches) usando formatos numéricos de menor precisão do que os originais. A maioria dos modelos modernos é treinada em FP32 ou em precisão mista (FP16/BF16), mas muitos podem ser inferidos em INT8 ou até INT4 com perda mínima de qualidade — frequentemente com grandes ganhos em consumo de memória, largura de banda, latência e custo.

Este artigo foca em tradeoffs entre INT8 e INT4, impactos típicos na acurácia e considerações de implantação em CPUs, GPUs e aceleradores.

Por que a quantização importa em sistemas reais

Em inferência de produção, o desempenho frequentemente é limitado menos por computação bruta e mais por movimentação de memória:

  • Carregar pesos do modelo a partir de HBM/DRAM
  • Mover ativações entre kernels
  • Ler/escrever caches KV para modelos autorregressivos

Reduzir a precisão diminui os bytes movidos e armazenados, o que melhora diretamente a taxa de processamento (throughput) e/ou a latência — especialmente em workloads limitados por memória. Veja Memória e Largura de Banda para o enquadramento mais amplo.

Assim, a quantização é tanto uma otimização de sistemas quanto uma aproximação numérica.

Ideia central: mapear valores reais para inteiros

A quantização mais prática para INT8/INT4 é a quantização afim uniforme (uniform affine quantization): representar um valor real (x) com um inteiro (q) usando uma escala (s) e um ponto zero (z).

Quantização afim (comum em INT8)

Dado um tensor de valores reais (x), defina:

  • escala (s > 0)
  • ponto zero (z) (inteiro)

Quantizar: [ q = \mathrm{clamp}\left(\left\lfloor \frac{x}{s} \right\rceil + z,\ q_{\min}, q_{\max}\right) ]

Desquantizar: [ \hat{x} = s \cdot (q - z) ]

Para INT8, tipicamente (q \in [-128, 127]) (simétrico) ou (q \in [0, 255]) (assimétrico).

Simétrica vs assimétrica

  • Simétrica: (z = 0). Simples e rápida; comum para pesos em aceleradores.
  • Assimétrica: (z \neq 0). Melhor para ativações que não são centradas em zero (por exemplo, pós-ReLU), comum em CPUs e alguns runtimes.

Erro de quantização (intuição)

A quantização uniforme introduz erro de arredondamento. Para um tamanho de passo (s), o ruído de quantização por valor é aproximadamente limitado por (\pm s/2) (ignorando o clipping). Um (s) menor reduz o erro de arredondamento, mas aumenta o risco de clipping de outliers porque o intervalo representável encolhe.

Isso estabelece a tensão central: escolher escalas que equilibrem erro de arredondamento vs clipping.

Granularidade: quantização por tensor, por canal e por grupos

Como você escolhe (s) (e, opcionalmente, (z)) tem um grande efeito na acurácia.

Quantização por tensor (per-tensor quantization)

Uma escala para o tensor inteiro.

  • Prós: mais simples, metadados mais leves, amplamente suportada
  • Contras: sensível a outliers; a acurácia frequentemente é pior, especialmente para pesos

Quantização por canal (per-channel quantization)

Uma escala diferente por canal de saída (por exemplo, por linha de uma matriz de pesos em camadas lineares).

  • Prós: normalmente uma grande melhora de acurácia para pesos
  • Contras: mais metadados; o suporte do kernel precisa lidar com escalas por canal

Pesos INT8 por canal são comuns em inferência de produção.

Quantização por grupos (block-wise) (group-wise quantization) (comum em INT4)

Uma escala diferente para grupos de elementos (por exemplo, grupos de 32/64/128 pesos).

  • Prós: acurácia muito melhor do que por tensor para INT4, com menos overhead do que por elemento
  • Contras: overhead de desquantização; complexidade do kernel; o tamanho do grupo é um parâmetro de ajuste

Muitos métodos populares de LLM em INT4 são efetivamente W4 com escalas por grupo.

O que exatamente é quantizado?

A quantização pode mirar diferentes tensores, e a escolha afeta fortemente tanto a acurácia quanto a velocidade.

Quantização apenas de pesos (weight-only quantization) (comum para INT4)

  • Pesos armazenados em INT4 (ou variantes de 4 bits)
  • Ativações permanecem em FP16/BF16
  • A computação frequentemente roda em FP16/BF16 com desquantização dos pesos on-the-fly

Isso é frequentemente escrito como W4A16.

Por que é popular:

  • A maior parte da memória do modelo está nos pesos
  • Ativações podem ser mais difíceis de quantizar de forma robusta frente a entradas dinâmicas
  • Muitas GPUs têm suporte nativo limitado a INT4 de ponta a ponta (end-to-end), mas kernels de apenas pesos são amplamente disponíveis

Quantização de pesos + ativações (common for INT8)

  • Pesos INT8, ativações INT8
  • Acumulação tipicamente em INT32 (e depois reescalonada)

Isso pode habilitar GEMMs (multiplicações de matrizes) inteiras rápidas em hardware suportado: W8A8.

Quantização do cache KV (KV-cache quantization) (específica de LLM)

A inferência autorregressiva armazena tensores de chave/valor para cada camada e token. O cache KV pode dominar a memória em comprimentos de contexto longos.

  • Quantizar o cache KV (por exemplo, INT8, FP8, às vezes INT4) pode reduzir memória e largura de banda
  • Mas pode introduzir perda de qualidade perceptível dependendo do modelo, do comprimento de contexto e do método

A quantização de KV é um eixo de projeto separado da quantização de pesos e frequentemente requer avaliação cuidadosa.

INT8 vs INT4: tradeoffs em um relance

Consumo de memória

  • Pesos INT8: ~2× menores do que FP16/BF16
  • Pesos INT4: ~4× menores do que FP16/BF16 (frequentemente um pouco menos na prática devido a overhead de escala/metadados)

Para modelos grandes, isso pode decidir se um modelo cabe em uma única GPU ou se requer sharding.

Largura de banda e throughput

A quantização reduz os bytes movimentados da memória. Se a inferência é limitada por memória (comum em decodificadores transformer), INT4 apenas de pesos pode melhorar materialmente o throughput.

No entanto, os ganhos reais de velocidade dependem de:

  • eficiência do kernel (empacotamento/desempacotamento de valores de 4 bits)
  • overhead de desquantização
  • quanto do tempo de execução é GEMM vs outras operações (layernorm, softmax, roteamento etc.)

Suporte de kernel e hardware

  • INT8 tem suporte amplo e maduro (instruções vetoriais de CPU, tensor cores de GPU, aceleradores de inferência).
  • INT4 tem suporte mais variável. Muitas implantações dependem de kernels especializados que fazem quantização apenas de pesos com eficiência.

Para contexto em nível de sistema, veja Introdução a Hardware e Compiladores e Runtimes.

Acurácia e robustez

  • INT8 frequentemente é próximo de “seguro” para muitas arquiteturas com boa calibração/QAT.
  • INT4 é mais sensível:
    • outliers em pesos/ativações
    • diferenças de sensibilidade entre camadas
    • matrizes de projeção de atenção e MLP podem se comportar de forma diferente
    • modelos pequenos podem degradar mais do que modelos grandes em alguns regimes (não é universal)

Na prática:

  • INT8 comumente resulta em perda negligenciável para modelos de classificação/CV e perda modesta para muitas tarefas de LLM com configuração adequada.
  • INT4 pode ser excelente para LLMs com métodos modernos de PTQ, mas exige alinhamento entre método e kernel para bons resultados.

Impactos na acurácia: o que causa degradação?

A quantização muda a função computada pela rede. As maiores quedas de qualidade geralmente vêm de alguns problemas recorrentes.

Outliers

Transformers frequentemente contêm canais/pesos “outliers” com magnitude incomumente alta. Com uma única escala, outliers forçam um (s) grande, o que aumenta o erro de arredondamento para a maioria dos valores.

Mitigações incluem:

  • quantização por canal ou por grupos
  • estratégias de clipping durante a calibração
  • métodos conscientes de outliers (por exemplo, reescalonar ativações/pesos antes da quantização)

Deriva da distribuição de ativações

Ativações dependem dos dados de entrada. Se os dados de calibração não correspondem ao tráfego de produção, a quantização estática de ativações pode degradar.

Mitigações:

  • conjuntos de dados de calibração representativos
  • quantização dinâmica (escalas calculadas em tempo de execução)
  • QAT para adaptar o modelo ao ruído de quantização

Sensibilidade por camada

Algumas camadas toleram melhor a quantização do que outras. Padrões comuns:

  • camadas de embedding e projeção de saída podem ser sensíveis
  • projeções de atenção podem precisar de granularidade mais fina
  • camadas iniciais às vezes importam mais em modelos de visão; em LLMs, a sensibilidade pode variar por arquitetura

Um padrão comum de implantação é a quantização seletiva (selective quantization):

  • manter algumas camadas em FP16/BF16
  • quantizar o restante para INT8/INT4

Erro cumulativo

O erro de quantização pode se acumular ao longo das camadas, especialmente quando tanto pesos quanto ativações são quantizados. Essa é uma razão pela qual INT4 frequentemente é apenas de pesos.

PTQ vs QAT (e por que isso importa para INT4)

Quantização pós-treinamento (Post-Training Quantization, PTQ)

Quantizar um modelo treinado sem retreinamento completo.

Passos típicos:

  1. Coletar ativações de calibração usando dados representativos
  2. Escolher escalas/pontos zero (por tensor/canal/grupo)
  3. Converter pesos e/ou ativações para menor precisão
  4. Validar acurácia em tarefas-alvo

PTQ é atraente porque é barato e rápido. Muitas abordagens modernas de LLM em INT4 são baseadas em PTQ, mas mais sofisticadas do que “arredondamento ingênuo” (por exemplo, otimizar escalas, minimizar erro por camada, usar quantização por grupos).

Treinamento consciente de quantização (Quantization-Aware Training, QAT)

Treinar (ou fazer fine-tuning) do modelo simulando efeitos de quantização (por exemplo, via nós de fake-quant).

  • Frequentemente produz maior acurácia na mesma largura de bits
  • Mais caro e operacionalmente mais complexo
  • Requer infraestrutura de treinamento e hiperparâmetros cuidadosos

Para muitos ambientes de produção:

  • INT8 para visão e transformers menores frequentemente usa QAT para melhores resultados
  • Implantações de LLM frequentemente usam PTQ por custo, embora QAT possa ajudar para quantização agressiva (especialmente se também quantizar ativações)

Exemplo prático de INT8 (estilo PyTorch eager-mode)

As APIs exatas evoluem, mas o fluxo de trabalho se parece com:

import torch
import torch.nn as nn

# Example model
model = nn.Sequential(
    nn.Linear(1024, 1024),
    nn.ReLU(),
    nn.Linear(1024, 10),
).eval()

# Dynamic quantization: weights quantized to int8, activations quantized on the fly
# Good for CPU inference on transformer-like models with many Linear layers.
qmodel = torch.ao.quantization.quantize_dynamic(
    model,
    {nn.Linear},
    dtype=torch.qint8
)

x = torch.randn(16, 1024)
with torch.no_grad():
    y = qmodel(x)
print(y.shape)

Notas:

  • Quantização dinâmica (dynamic quantization) frequentemente é eficaz em CPU para modelos com muitas camadas lineares.
  • Para GPUs, INT8 geralmente depende de runtimes/kernels especializados em vez do PyTorch em eager-mode.

Exemplo prático de INT4 (conceito de LLM apenas de pesos)

Muitas implantações de LLM em GPU usam W4A16 (ou similar), em que os pesos têm 4 bits e as ativações permanecem FP16/BF16. Um fluxo de trabalho típico é:

  1. Carregar o modelo em FP16
  2. Aplicar um método de PTQ para produzir pesos empacotados em 4 bits + escalas por grupo
  3. Rodar inferência usando kernels que fundem desquantização + GEMM de forma eficiente

Na prática, você usará uma biblioteca ou runtime que suporte seu hardware-alvo (por exemplo, via um runtime do fornecedor ou kernels otimizados de atenção/GEMM). A principal conclusão é que INT4 raramente é “matemática puramente inteira”; frequentemente é armazenamento comprimido + computação especializada.

Considerações de implantação (o que tende a quebrar no mundo real)

O sucesso da quantização depende de alinhar método, kernel, hardware e workload.

1) Suporte de hardware e ganhos reais de velocidade

Mesmo que INT4 reduza os bytes dos pesos em ~4×, a latência ponta a ponta pode não cair em 4× devido a:

  • overhead de desquantização
  • operações não-GEMM dominando o tempo de execução (softmax, layernorm, sampling)
  • baixa utilização do kernel em tamanhos de batch pequenos
  • layouts de memória e overheads de empacotamento

INT8 frequentemente oferece ganhos mais previsíveis porque o ecossistema é maduro:

  • CPU: aceleração de produto escalar inteiro no estilo AVX2/AVX-512/VNNI
  • GPU: caminhos de tensor cores INT8 em muitas GPUs NVIDIA; suporte similar em outros aceleradores

Os ganhos de INT4 dependem muito de sua plataforma ter:

  • caminhos rápidos de tensor cores de 4 bits ou
  • kernels extremamente otimizados de apenas pesos

2) Integração com compilador/runtime

A quantização não é apenas uma mudança numérica; ela muda o grafo:

  • nós de quantização/desquantização inseridos
  • reescalonamento entre camadas
  • acumuladores inteiros e requantização
  • tratamento de escalas por canal

Compiladores de grafo e runtimes de inferência podem fundir muitos desses passos; sem fusão, a quantização pode ficar mais lenta.

É aqui que ferramentas de Compiladores e Runtimes importam: seleção de kernels, fusão de operadores, transformações de layout e pipelines de calibração frequentemente vivem ali.

3) Requisitos de dados de calibração (especialmente INT8)

A ativação INT8 estática geralmente precisa de calibração.

Checklist:

  • Use entradas representativas (domínio, distribuições de comprimento, idiomas, estatísticas de imagem etc.)
  • Amostras suficientes para capturar faixas de ativação (frequentemente centenas a milhares)
  • Avalie tanto métricas médias quanto comportamento de cauda (categorias raras, contextos longos)

Se a calibração for ruim, INT8 pode ter desempenho inferior ao FP16 mais do que o esperado.

4) Políticas de precisão mista

Muitos sistemas de produção adotam políticas como:

  • manter embeddings/cabeça de saída em FP16
  • quantizar pesos de MLP para INT4
  • usar INT8 para projeções de atenção
  • manter layernorm em FP16

Essa “quantização seletiva” frequentemente entrega a maior parte do benefício de memória com menos risco de acurácia.

5) Tamanho de batch e metas de latência

  • Em batch grande, a computação domina mais e GEMMs quantizadas podem brilhar.
  • Em batch pequeno / baixa latência, overheads de lançamento de kernel e operações não-matmul importam mais; INT4 pode não ajudar tanto, a menos que os kernels sejam muito otimizados.

Meça no formato real do seu serving:

  • tamanho de batch
  • comprimento de sequência (para LLMs)
  • decodificação em streaming vs não streaming
  • padrões de concorrência

6) Numéricos e depuração

Modelos quantizados podem falhar de maneiras sutis:

  • NaNs ocasionais após bugs de desquantização/reescalonamento
  • saturação/clipping levando a logits degradados
  • regressões apenas em certas entradas

Táticas práticas de depuração:

  • comparar saídas por camada entre FP16 e quantizado (similaridade cosseno / erro relativo)
  • rodar avaliação A/B em um conjunto de teste amplo
  • verificar histogramas de ativação por camada durante a calibração
  • começar com INT8 e depois migrar para INT4 quando o pipeline estiver estável

Escolhendo INT8 vs INT4: um framework de decisão

Prefira INT8 quando…

  • você precisa de acurácia previsível
  • você quer suporte amplo de hardware
  • você consegue calibrar ou usar QAT
  • você quer ganhos de velocidade de GEMM inteira W8A8 (especialmente em CPU ou caminhos maduros de GPU)

Casos de uso comuns:

  • modelos de CV em edge/CPU
  • modelos encoder (tipo BERT) em CPU
  • stacks gerais de inferência onde robustez importa mais do que compressão máxima

Prefira INT4 quando…

  • o tamanho do modelo e a largura de banda são o custo dominante
  • você está implantando LLMs onde a memória de pesos é o gargalo
  • você tem kernels/runtime otimizados para INT4 apenas de pesos
  • você tolera alguma perda de acurácia ou consegue validar que suas tarefas são robustas

Casos de uso comuns:

  • colocar LLMs maiores em menos GPUs
  • aumentar throughput para inferência de decodificador limitada por memória
  • reduzir custo em nuvem por token

Interação com inferência de transformer (notas específicas de LLM)

Decodificadores transformer frequentemente são limitados por largura de banda durante a geração: cada novo token exige ler muitas matrizes de pesos. A quantização INT4 apenas de pesos pode ser especialmente eficaz aqui.

Mas a implantação de LLM também inclui:

  • crescimento do cache KV com o comprimento da sequência (frequentemente domina a memória em contextos longos)
  • kernels de atenção que podem não se beneficiar diretamente da quantização de pesos
  • overhead de amostragem/decodificação (top-k/top-p) que não é quantizado

Para entender esses gargalos em nível de sistema, conecte decisões de quantização ao comportamento de Arquitetura Transformer e de Memória e Largura de Banda.

Armadilhas comuns

  • Assumir “4-bit = 2× mais rápido que 8-bit”: memória é apenas uma parte; kernels e fusão no runtime determinam a velocidade.
  • Usar dados de calibração não representativos: pode causar regressões surpreendentes.
  • Ignorar sensibilidade por camada: um pequeno conjunto de camadas pode dominar a perda de qualidade.
  • Desalinhar formato de quantização e kernel: por exemplo, tamanho de grupo ou layout de escalas não suportado com eficiência pelo seu runtime.
  • Não medir ponta a ponta: tokens/s ou latência p95 importa mais do que benchmarks isolados de GEMM.

Resumo

A quantização reduz a precisão para melhorar a eficiência de inferência. Na prática:

  • INT8 é o cavalo de batalha: maduro, amplamente suportado e frequentemente com perda mínima de acurácia com calibração ou QAT.
  • INT4 é uma ferramenta poderosa de compressão — especialmente para inferência de LLM apenas de pesos — mas é mais sensível a outliers, suporte de kernels e detalhes do método de quantização.

Uma boa quantização é um problema de co-projeto entre sistemas e numéricos: os melhores resultados vêm de alinhar a granularidade da quantização (por canal/grupo), a estratégia de calibração/QAT e os kernels do compilador/runtime no seu hardware-alvo.