Poda
O que é poda?
Poda é uma família de técnicas de compressão de modelos que remove parâmetros ou subestruturas inteiras de uma rede neural para reduzir:
- Tamanho do modelo (menos pesos armazenados)
- Latência de inferência (menos operações ou melhor localidade de memória)
- Uso de energia (menos computação e largura de banda de memória)
A poda pode remover:
- Pesos individuais (esparsidade de granulação fina)
- Neurônios, canais, filtros, cabeças de atenção, ou camadas inteiras (esparsidade estruturada)
- Blocos estruturados (por exemplo, padrões N:M ou matrizes esparsas por blocos)
A poda é comumente usada em CNNs e MLPs, e cada vez mais em LLMs (large language models) baseados na Arquitetura Transformer, especialmente quando combinada com Quantização e Destilação de Conhecimento.
Por que a poda funciona (intuição e teoria)
Redes profundas modernas frequentemente são superparametrizadas (overparameterized): contêm mais parâmetros do que o estritamente necessário para representar uma boa solução. Muitos parâmetros são:
- Redundantes (múltiplos pesos aprendem funções semelhantes)
- Próximos de zero ou raramente usados
- Contribuem pouco para a perda final
Do ponto de vista de otimização, a poda costuma ser formulada como encontrar um vetor de parâmetros mais esparso (w) que mantenha a perda baixa:
[ \min_{w} \ \mathcal{L}(w) \quad \text{subject to} \quad |w|_0 \le k ]
Aqui (|w|_0) conta parâmetros não nulos (uma restrição de esparsidade). Otimizar diretamente a norma L0 é combinatório e difícil, então métodos práticos usam heurísticas (limiarização por magnitude), relaxamentos (regularização L1/L0) ou aprendem máscaras de poda.
Uma observação empírica relacionada é a ideia do “bilhete de loteria” (lottery ticket): dentro de uma rede grande pode existir uma sub-rede esparsa que treina bem. Embora não seja uma regra universal, isso motiva abordagens iterativas de podar e ajustar finamente.
Alvos de poda e granularidade
Poda não estruturada (poda de pesos)
A poda não estruturada remove pesos escalares individuais com base em algum critério (frequentemente, magnitude). As matrizes de pesos resultantes tornam-se esparsas com um padrão irregular.
- Prós
- Pode alcançar esparsidade muito alta (por exemplo, 80–95%) com perda de acurácia modesta em alguns cenários
- Fácil de implementar conceitualmente
- Contras
- A esparsidade irregular frequentemente não é mais rápida em GPUs típicas, a menos que você use kernels e formatos esparsos especializados
- O armazenamento pode não reduzir muito a menos que você use formatos esparsos comprimidos (que adicionam overhead de índices)
A poda não estruturada é mais útil quando:
- Sua pilha de implantação oferece suporte a inferência esparsa (runtimes especializados, CPUs com bibliotecas esparsas, ou certos kernels de GPU)
- Você se importa mais com pegada de memória do que com velocidade bruta
Poda estruturada (poda de canal / cabeça / neurônio)
A poda estruturada remove componentes inteiros que se alinham com computação eficiente:
CNN: filtros ou canais
MLP: neurônios (removendo linhas/colunas de matrizes de pesos)
Transformers: cabeças de atenção, dimensões intermediárias do MLP, às vezes camadas inteiras
Prós
- Reduz diretamente tamanhos de tensores densos → geralmente gera melhorias reais de latência em hardware padrão
- Mais fácil de implantar (não exige formatos esparsos)
Contras
- Muitas vezes é menos “cirúrgica”; remover um canal/cabeça inteiro pode prejudicar mais a acurácia do que remover alguns pesos
- Requer implementação ciente da arquitetura
A poda estruturada costuma ser preferida para:
- Implantação em mobile/edge
- Inferência em GPU onde GEMMs densas são altamente otimizadas
- Integração mais simples em sistemas de produção
Poda semiestruturada (esparsidade em blocos, N:M)
Padrões semiestruturados restringem a esparsidade para ser amigável ao hardware, por exemplo:
- Esparso por blocos (block-sparse): podar blocos contíguos (por exemplo, blocos 16×16)
- Esparsidade N:M (N:M sparsity): dentro de cada grupo de M pesos, manter N (por exemplo, 2:4)
Isso pode oferecer um meio-termo:
- Mais compressão do que a poda estruturada
- Mais velocidade do que a poda totalmente não estruturada (em hardware com suporte)
Critérios: decidindo o que podar
Os métodos de poda diferem principalmente em como pontuam a “importância”.
Poda baseada em magnitude (a mais comum)
Remove pesos com menor valor absoluto:
- No nível de peso: poda por (|w|)
- No nível de grupo: poda por normas como (|w_{\text{group}}|1) ou (|w{\text{group}}|_2)
Isso funciona surpreendentemente bem porque muitos regimes de treinamento já empurram pesos pouco importantes para valores pequenos (especialmente com decaimento de peso).
Critérios baseados em gradiente e sensibilidade
Estimam o quanto a perda mudaria se um parâmetro fosse removido.
Uma aproximação clássica usa informação de segunda ordem (ideias no estilo Hessiana/Fisher na diagonal). Na prática, muitos métodos usam proxies mais baratos:
- Gradiente × peso
- Gradientes acumulados
- “Poda por movimento” (movement pruning): pesos que consistentemente se movem em direção a zero são podados
Isso pode superar a poda apenas por magnitude em alguns cenários, especialmente em grandes cenários de Ajuste Fino.
Critérios baseados em ativações/estatísticas (frequente para poda estruturada)
Para canais/neurônios/cabeças, usar:
- Magnitude média de ativação
- Variância de saída
- Contribuição para padrões de atenção (para poda de cabeças)
- Escores de importância aprendidos via portas (gates) (máscaras aprendíveis)
Para Transformers, a importância de cabeças pode se basear em:
- Entropia/“concentração” (peakedness) da atenção
- Saliência baseada em gradiente das saídas das cabeças
- Efeito na perda de validação ao ablar uma cabeça (caro, mas informativo)
Quando podar: pós-treinamento vs durante o treinamento
Poda pós-treinamento (treina denso → poda → ajuste fino)
Este é o fluxo de trabalho mais comum:
- Treinar um modelo denso (ou começar de um checkpoint pré-treinado)
- Podar parâmetros até uma esparsidade/estrutura alvo
- Ajustar finamente o modelo podado para recuperar acurácia
Variantes:
- Poda one-shot (one-shot pruning): podar uma vez e depois ajustar finamente
- Poda iterativa (iterative pruning): aumentar gradualmente a esparsidade ao longo de múltiplos ciclos de poda–ajuste fino (frequentemente com melhor acurácia)
Prós
- Simples de integrar com pipelines de treinamento existentes
- Funciona bem quando você pode arcar com algum custo computacional de ajuste fino
Contras
- Requer tempo extra de ajuste fino
- Poda one-shot pode causar quedas acentuadas de acurácia em alta esparsidade
Poda durante o treinamento (poda durante o treinamento)
Aqui a poda faz parte do processo de treinamento:
- Agendas de poda gradual (gradual pruning schedules): começar denso e aumentar lentamente a esparsidade
- Treinamento esparso dinâmico (dynamic sparse training): manter um orçamento fixo de esparsidade enquanto reconecta conexões (poda algumas, reativa outras)
- Máscaras/portas aprendíveis que convergem para estruturas esparsas
Prós
- Pode alcançar maior esparsidade com acurácia semelhante
- Potencial economia durante o treinamento ao usar kernels de treinamento esparso
Contras
- Implementação mais complexa
- Acelerações de treinamento esparso dependem de hardware/software
Para LLMs, a poda durante o treinamento é menos comum do que a poda pós-treinamento (devido ao custo e à complexidade de engenharia), mas aparece em pesquisa e em implantações especializadas.
Exemplos práticos
Exemplo 1: Poda não estruturada por magnitude em PyTorch (conceitual)
O PyTorch fornece utilitários para poda, mas observe que muitos utilitários de poda criam uma máscara enquanto mantêm o parâmetro denso subjacente, a menos que você “torne permanente” e use um runtime compatível com esparsidade.
import torch
import torch.nn as nn
import torch.nn.utils.prune as prune
model = nn.Sequential(
nn.Linear(768, 3072),
nn.ReLU(),
nn.Linear(3072, 768),
)
# Prune 50% of weights in the first linear layer by magnitude
prune.l1_unstructured(model[0], name="weight", amount=0.5)
# The module now has weight_orig and weight_mask; forward uses masked weight
print(model[0].weight.shape)
# Make pruning permanent (removes reparameterization)
prune.remove(model[0], "weight")
Nota de implantação: Se você exportar este modelo como pesos densos com zeros, muitos mecanismos de inferência ainda farão multiplicações de matrizes densas—então você pode não obter ganhos de latência sem kernels esparsos.
Exemplo 2: Poda estruturada de cabeças de atenção (alto nível)
Em uma camada Transformer com num_heads = 32, a poda estruturada de cabeças pode remover, por exemplo, 8 cabeças:
- Dimensão original por cabeça na camada:
d_model = 4096,head_dim = 128 - Se você podar 8 cabeças, você reduz a largura efetiva da atenção
- Opções de implementação:
- Remover fisicamente as fatias correspondentes das matrizes de projeção Q/K/V/O (melhor para velocidade)
- Manter os tensores, mas mascarar cabeças (mais fácil, porém com menos ganho de velocidade)
Em implantações de LLMs, a poda de cabeças pode reduzir computação e largura de banda de memória, e também pode reduzir o tamanho do cache KV (KV cache) se você também reduzir o número de cabeças e mantiver a dimensão por cabeça consistente (dependente da implementação).
Trade-offs de acurácia e como gerenciá-los
A curva esparsidade–acurácia
À medida que você poda de forma mais agressiva, a acurácia normalmente degrada de maneira não linear:
- Poda baixa a moderada: pouca perda (frequentemente redundância “gratuita”)
- Após um limiar: a acurácia cai rapidamente
Onde esse limiar fica depende de:
- Tamanho do modelo e arquitetura
- Complexidade do conjunto de dados/tarefa
- Se a poda é estruturada ou não estruturada
- Se você ajusta finamente após a poda
Técnicas comuns para recuperar acurácia
- Ajuste fino após a poda (quase sempre necessário além de poda leve)
- Poda iterativa/gradual em vez de one-shot
- Destilação: treinar o modelo podado para corresponder a um professor maior (Destilação de Conhecimento)
- Poda ciente de regularização: incorporar penalidades de esparsidade durante o treinamento (L1, group lasso, métodos inspirados em L0)
- Sensibilidade por camada: podar menos em camadas frágeis (frequentemente camadas de embedding, camadas iniciais ou certos blocos de atenção/MLP)
Poda global vs por camada
- Por camada (layer-wise): podar uma fração fixa por camada (simples, mas subótimo)
- Global: ranquear parâmetros/grupos em toda a rede (frequentemente melhor compressão para uma dada acurácia)
Para LLMs, a poda estruturada global pode ser complicada porque diferentes camadas exercem papéis diferentes; muitos pipelines práticos usam orçamentos por camada informados por testes de sensibilidade.
Considerações de implantação (onde a poda dá certo ou decepciona)
1) Latência depende de suporte de hardware
Um ponto prático-chave:
- Poda estruturada (matrizes densas menores) geralmente acelera a inferência em qualquer lugar.
- Esparsidade não estruturada frequentemente não acelera a inferência em GPUs, a menos que:
- Seu runtime use kernels GEMM esparsos com eficiência
- A esparsidade seja alta o suficiente para compensar overhead
- O padrão de esparsidade corresponda a formatos suportados (por exemplo, N:M)
Se seu objetivo é latência de relógio (“wall-clock”) em pilhas padrão de inferência em GPU, poda estruturada ou semiestruturada costuma ser a aposta mais segura.
2) Tamanho do modelo vs tamanho “em disco”
A poda não estruturada reduz a contagem de não zeros, mas modelos serializados ainda podem armazenar tensores densos a menos que você:
- Armazene pesos em um formato esparso (CSR/CSC/COO ou esparso por blocos)
- Ou re-encodifique fisicamente o modelo
A poda estruturada naturalmente reduz as dimensões dos tensores, então o modelo em disco normalmente diminui sem manuseio especial.
3) Suporte de kernel e framework
Antes de se comprometer com uma abordagem de poda, confirme se sua pilha de serving dá suporte a ela:
- PyTorch eager vs TorchScript vs
torch.compile - Exportação ONNX e se ops esparsas são preservadas
- Suporte a esparsidade em TensorRT / XLA / TVM
- Runtimes de LLM (por exemplo, sistemas do tipo vLLM) e se conseguem explorar esparsidade
Frequentemente, equipes podam com sucesso em pesquisa, mas veem ganhos limitados em produção porque o mecanismo de inferência ainda executa kernels densos.
4) Interação com quantização
Poda e quantização podem ser complementares:
- Poda reduz número de parâmetros
- Quantização reduz bits por parâmetro (Quantização)
No entanto, tenha cuidado:
- Quantização pode amplificar o efeito de mudanças de distribuição induzidas pela poda
- Alguns formatos esparsos e kernels int8/4-bit não se combinam bem sem suporte especializado
Uma receita comum em produção é:
- Poda estruturada (redução da arquitetura)
- Ajuste fino ciente de quantização ou quantização pós-treinamento
5) Medindo as métricas certas
Relate mais do que “% de esparsidade”:
- Acurácia / métricas de tarefa (por exemplo, perplexidade para LLMs)
- Latência nos tamanhos de batch e comprimentos de sequência alvo
- Vazão (throughput)
- Memória de pico (incluindo cache KV para serving de LLMs)
- Energia se relevante (frequentemente aproximada via contadores de hardware)
Um modelo que é “90% esparso”, mas executa kernels densos, pode mostrar pouca melhora em latência e energia.
Poda em LLMs: o que é comumente podado?
LLMs introduzem estrutura e restrições especiais:
Cabeças de atenção
- Remover cabeças inteiras (estruturado)
- Pode reduzir computação na atenção e às vezes reduzir a pegada do cache KV (dependendo da implementação)
Dimensões do MLP (feed-forward)
Transformers frequentemente gastam a maior parte dos FLOPs nos blocos MLP. A poda estruturada pode reduzir:
- Dimensão intermediária (por exemplo, de 11008 → 8192)
- Neurônios inteiros do MLP (poda em grupo)
Esta é uma das abordagens estruturadas mais eficazes para obter acelerações reais.
Camadas (poda de profundidade)
Remover camadas pode ser eficaz, mas arriscado:
- A acurácia pode degradar acentuadamente
- Frequentemente requer destilação cuidadosa ou retreinamento
- Algumas arquiteturas são mais robustas à redução de profundidade do que outras
Esparsidade semiestruturada em camadas lineares
Esparsidade N:M é atraente para LLMs porque:
- Mira as grandes GEMMs
- Pode mapear para kernels especializados em aceleradores com suporte
Na prática, os benefícios dependem fortemente do hardware e do runtime exatos.
Um fluxo de trabalho típico de poda (orientado a produção)
- Defina o objetivo
- Menor pegada de memória? Menor latência? Menor energia? Caber no dispositivo?
- Escolha o tipo de poda
- Precisa de acelerações previsíveis → estruturada/semiestruturada
- Precisa de compressão máxima e tem runtime esparso → não estruturada
- Selecione um alvo de poda
- Para Transformers: largura do MLP e cabeças de atenção são comuns
- Escolha uma agenda
- Comece com poda iterativa pós-treinamento + ajuste fino
- Ajuste finamente e valide
- Acompanhe acurácia e latência (não apenas esparsidade)
- Exporte e faça benchmark na pilha real de serving
- Confirme que os kernels exploram a nova estrutura
- Prepare para implantação
- Testes de regressão, checagens de estabilidade numérica, monitoramento de drift
Armadilhas comuns
- Assumir que esparsidade equivale a velocidade: esparsidade não estruturada frequentemente falha em reduzir latência.
- Podar sem ajuste fino: quedas de acurácia podem ser severas, especialmente para poda estruturada.
- Ignorar sensibilidade por camada: algumas camadas são desproporcionalmente importantes.
- Fazer benchmark incorretamente: meça em tamanhos realistas de batch/sequência e com caches aquecidos.
- Mascarar em vez de remover estrutura: máscaras ajudam na experimentação, mas podem não gerar ganhos na implantação.
Como a poda se compara a técnicas relacionadas de otimização de inferência
- Quantização: reduz precisão; geralmente traz ganhos práticos fortes de velocidade/memória com boas ferramentas.
- Destilação de Conhecimento: treina um modelo menor para imitar um maior; frequentemente é melhor quando você pode mudar a arquitetura livremente.
- Poda: remove partes de um modelo existente; melhor quando você quer manter a arquitetura original “em grande parte” intacta ou precisa de reduções direcionadas.
Na prática, muitas implantações de alto desempenho combinam métodos: poda estruturada + quantização, às vezes com destilação para recuperar qualidade.
Resumo
A poda remove pesos ou componentes estruturados para reduzir redes neurais e LLMs. A escolha de projeto mais importante é não estruturada vs estruturada:
- Poda não estruturada pode alcançar alta esparsidade, mas pode não acelerar a inferência sem runtimes cientes de esparsidade.
- Poda estruturada (canais/cabeças/neurônios/camadas) geralmente produz ganhos confiáveis de latência porque reduz tamanhos de tensores densos.
- Abordagens durante o treinamento podem gerar melhores trade-offs de esparsidade–acurácia, mas são mais complexas; poda pós-treinamento + ajuste fino continua sendo o fluxo prático mais comum.
Quando feita com cuidado—com análise de sensibilidade, poda gradual e ajuste fino—a poda é uma ferramenta poderosa no kit de otimização de inferência, especialmente quando combinada com quantização e destilação.