Capítulo 2.2 — Quantização e compressão de modelos: de 4-bit ao deployment real¶
🎯 Objetivo¶
Cobrir quantização com nível suficiente para decidir entre laboratório/local e produção enterprise. O foco não é treinar quantização, mas entender o que muda em memória, throughput, qualidade, kernel e hardware quando um modelo deixa FP16 e cai para INT8/INT4.
🧠 Conceito principal¶
Quantização troca precisão numérica por redução de tamanho e custo de operação. Em LLMs, as três operações dominantes são matmul, attention e normalização. Quantizar reduz o tamanho dos pesos e, eventualmente, das ativações; o ganho real depende de o kernel suportar a operação no novo formato.
🧮 Formatos e onde aparecem¶
| Formato | Bits | Uso típico | Hardware necessário |
|---|---|---|---|
| FP32 | 32 | Treino padrão, baselines, comparação | CPU/GPU geral |
| TF32 | 19 (acumulação) | Treino com Tensor Cores (NVIDIA Ampere+) | A100, H100 |
| FP16 | 16 | Treino com mixed precision, inferência GPU | V100+ |
| BF16 | 16 | Treino estável (range ~ FP32), inferência | A100/H100, TPU |
| FP8 (E4M3/E5M2) | 8 | Inferência/treino moderno em GPUs novas | H100/H200, MI300, Trainium |
| INT8 | 8 | Inferência quantizada (W8A8 ou W8A16) | GPU/CPU/edge amplos |
| INT4 / NF4 | 4 | Inferência quantizada e QLoRA | GPU recente, llama.cpp, MLC, GGUF |
| INT3 / INT2 | 3, 2 | Pesquisa, formatos GGUF "agressivos" | Variável; perda comum |
🧠 Inferência vs fine-tuning quantizado¶
São dois problemas diferentes:
- Quantização para inferência - reduzir custo/memória ao servir. Família GPTQ, AWQ, SmoothQuant, formatos GGUF/AWQ/EXL2, kernels do TensorRT-LLM, vLLM, llama.cpp.
- Quantização durante fine-tuning - manter o modelo base em baixa precisão e treinar adapters (LoRA) em precisão maior. O exemplo canônico é QLoRA com NF4 + double quantization + paged optimizers.
A confusão entre os dois costuma aparecer em discussões "QLoRA é melhor que GPTQ?" - são técnicas para problemas diferentes (treino vs serving).
🧪 Métodos principais¶
Post-Training Quantization (PTQ)¶
- GPTQ - quantização camada-a-camada usando aproximação de segunda ordem do erro de reconstrução; tipicamente INT4 ou INT3 só nos pesos. Bom equilíbrio qualidade/custo para serving. (Frantar et al., 2022).
- AWQ (Activation-aware Weight Quantization) - preserva canais "salientes" (alta magnitude de ativação) e quantiza o resto com agressividade. Costuma manter melhor qualidade que round-to-nearest e é amigável a kernels otimizados. (Lin et al., 2023).
- SmoothQuant - redistribui dificuldade de quantização entre pesos e ativações via reescala; permite W8A8.
- GGUF (llama.cpp) - formato de arquivo + variantes de quantização (Q4_K_M, Q5_K_M, Q8_0, IQ-quants etc.) populares em execução local CPU/GPU mista. Bom para laboratório e dev local.
Quantization-Aware Training (QAT)¶
Treina (ou continua treinando) já considerando o ruído de quantização. Custo de engenharia maior, qualidade tipicamente melhor que PTQ em formatos agressivos.
bitsandbytes¶
Biblioteca Python amplamente usada para carregar modelos em 8-bit (LLM.int8) e 4-bit (NF4) com Hugging Face Transformers. Foco em pesquisa, prototipagem e fine-tuning. Não é o caminho típico para serving de baixa latência em produção em larga escala - para isso, prefira kernels do vLLM, TensorRT-LLM ou implementações nativas do framework de serving.
QLoRA¶
Combina (a) modelo base congelado em NF4 (4-bit "NormalFloat", distribuição informacionalmente otimizada para pesos próximos a normal); (b) double quantization dos próprios constantes de quantização; (c) paged optimizers para suportar picos de memória; (d) adapters LoRA em BF16. Resultado: fine-tunar modelos grandes em GPUs únicas com qualidade próxima a full FT em muitos cenários.
⚖️ Trade-offs¶
| Eixo | Realidade |
|---|---|
| VRAM | Quase sempre cai com quantização. INT4 reduz pesos em ~4x vs FP16. |
| Throughput | Nem sempre aumenta. Depende de existir kernel quantizado eficiente para o hardware. Em hardware moderno com FP16 já bem otimizado, INT4 sem kernel adequado pode ser mais lento. |
| Latência (TTFT) | Pode melhorar pelo menor tráfego de pesos via memory bandwidth, principal gargalo do decode. |
| Qualidade | Modelos grandes (>30B) toleram INT4 razoavelmente. Modelos pequenos (≤7B) sofrem mais, especialmente em raciocínio multi-step e tool calling. |
| Compatibilidade | FP8/INT4 exigem hardware e kernels específicos. Mover entre stacks (Transformers, vLLM, TGI, TensorRT-LLM, llama.cpp) frequentemente quebra. |
| Determinismo | Quantização aumenta variação numérica; reproducibilidade fica mais frágil. |
| Tool calling / structured output | Modelos quantizados agressivamente podem degradar mais em seguir schemas e em raciocínio passo-a-passo do que em respostas livres. |
🏗️ Como isso aparece em produção¶
- Laboratório / dev local - INT4 via GGUF + llama.cpp ou via bitsandbytes resolve, com modelos rodando em laptops. Aceita perda; objetivo é experimentação.
- Self-hosting enterprise - geralmente FP16/BF16 ou FP8 em GPUs recentes; AWQ/GPTQ INT4 quando o ganho de densidade compensa a perda mensurável. Sempre com eval set comparativo entre versões.
- API providers - abstrai o leitor. Cliente vê apenas modelo + preço; quantização é responsabilidade do provedor.
- Edge / on-device - INT4/INT3, MLC, llama.cpp, mlx, formatos GGUF; qualidade é negociada explicitamente.
🚨 Modos de falha¶
- Adotar quantização agressiva por causa de "ganho de VRAM" sem medir throughput real no hardware-alvo.
- Avaliar quantização apenas com perplexity em benchmarks gerais; ignorar task-specific degradation em tool calling, JSON output e RAG.
- Trocar de framework (ex.: bitsandbytes -> vLLM) sem reavaliar qualidade.
- Quantizar modelo pequeno que já estava no limite.
- Misturar adapters de QLoRA treinados em uma versão de NF4 com base quantizada em outra biblioteca.
🛡️ Controles e mitigações¶
- Manter eval set fixo (golden + adversarial + tool calling + structured output) para comparar FP16 vs INT8 vs INT4.
- Medir, no hardware-alvo: TTFT, TPOT, throughput de tokens/s, p95, custo por sucesso.
- Versionar
model_id + quant_scheme + framework_versionno tracing. - Fixar versões de kernel/biblioteca via lock files.
- Adotar canary com sombra (shadow inference) antes de promover modelo quantizado.
📈 Métricas¶
- Memória de pesos:
params × bits_per_param / 8. - Perplexity (quando aplicável) em corpus de domínio.
- Task success rate por categoria.
- Hallucination rate vs baseline FP16.
- Tool call accuracy e schema validity rate (especialmente para 4-bit).
- Tokens/s (decode) e TTFT (prefill).
- Custo por sucesso, não apenas custo por chamada.
🧪 Distillation (revisão curta)¶
Modelo "student" aprende a imitar "teacher" (rótulos, logits, comportamento em traces reais). Diferentemente de quantização, distillation altera a arquitetura/tamanho, não apenas a precisão numérica.
Padrões úteis:
- Distilar modelo grande para um modelo menor para uma tarefa específica.
- Distilar comportamento (formato, estilo) e deixar conhecimento factual em RAG.
- Combinar distillation + quantização para reduzir custo agregado.
🧪 Fine-tuning (revisão curta)¶
- Full fine-tuning - atualiza todos os pesos. Caro, exige dataset significativo, pipeline de eval, governança.
- LoRA - adapters de baixo posto sobre camadas selecionadas. Mantém o base congelado.
- QLoRA - LoRA sobre base 4-bit (NF4); permite fine-tune de modelos grandes em hardware modesto.
- Adapter / Prefix / IA³ / DoRA - variações com diferentes trade-offs de parâmetros e qualidade.
- Instruction tuning / SFT - treino supervisionado em pares instrução-resposta.
- Preference tuning (DPO, KTO, ORPO etc.) - alinhar comportamento a preferências; mais delicado e fora do escopo deste livro.
⚠️ Quando NÃO usar fine-tuning¶
- Para adicionar conhecimento factual mutável - prefira RAG.
- Para impor regra de negócio - prefira policy-as-code.
- Para um único requisito de formato - prefira structured outputs.
- Para "ensinar a usar tools" sem dataset estruturado e eval - quase sempre não vale a pena; ajuste descrição, exemplos e harness.
📌 Checklist¶
- [ ] Eval set comparativo entre formatos (FP16/INT8/INT4) está pronto?
- [ ] Métricas de tool calling e structured output foram medidas, não só perplexity?
- [ ] Foi medido throughput real no hardware-alvo, e não só VRAM teórica?
- [ ] O framework de serving suporta o formato de forma estável?
- [ ] Existe plano de rollback para versão FP16?
- [ ] Adapters de fine-tuning estão versionados junto com a base?
🧰 Exemplos práticos relacionados (planejados)¶
EX-LLM-07- comparação FP16 vs INT8 vs INT4 (GPTQ/AWQ) com eval set de tool calling e JSON output.EX-LLM-08- QLoRA mínimo com Hugging Face + bitsandbytes em modelo pequeno para estilo, mantendo conhecimento em RAG.
📚 Referências¶
- Dettmers et al. - QLoRA: Efficient Finetuning of Quantized LLMs (NeurIPS 2023): https://arxiv.org/abs/2305.14314
- Frantar et al. - GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers (ICLR 2023): https://arxiv.org/abs/2210.17323
- Lin et al. - AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration: https://arxiv.org/abs/2306.00978
- Xiao et al. - SmoothQuant: https://arxiv.org/abs/2211.10438
- Hu et al. - LoRA: Low-Rank Adaptation of Large Language Models: https://arxiv.org/abs/2106.09685
- bitsandbytes - Hugging Face docs: https://huggingface.co/docs/bitsandbytes/main/en/index
- llama.cpp - projeto e GGUF: https://github.com/ggml-org/llama.cpp
- Sebastian Raschka - LoRA vs full fine-tuning: https://sebastianraschka.com/faq/docs/lora-vs-full-finetuning.html
- OpenAI - Supervised fine-tuning: https://developers.openai.com/api/docs/guides/supervised-fine-tuning