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:

  1. Treinar um modelo denso (ou começar de um checkpoint pré-treinado)
  2. Podar parâmetros até uma esparsidade/estrutura alvo
  3. 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 é:

  1. Poda estruturada (redução da arquitetura)
  2. 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)

  1. Defina o objetivo
    • Menor pegada de memória? Menor latência? Menor energia? Caber no dispositivo?
  2. 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
  3. Selecione um alvo de poda
    • Para Transformers: largura do MLP e cabeças de atenção são comuns
  4. Escolha uma agenda
    • Comece com poda iterativa pós-treinamento + ajuste fino
  5. Ajuste finamente e valide
    • Acompanhe acurácia e latência (não apenas esparsidade)
  6. Exporte e faça benchmark na pilha real de serving
    • Confirme que os kernels exploram a nova estrutura
  7. 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.