5 pontos por GN⁺ 2025-11-03 | 1 comentários | Compartilhar no WhatsApp
  • Retropropagação (backpropagation) é a base do treinamento de redes neurais, mas, se você não entender seu funcionamento interno, podem surgir erros inesperados em uma estrutura de “abstração com vazamento (leaky abstraction)”
  • Funções de ativação sigmoid e tanh podem fazer o treinamento parar por causa do desvanecimento do gradiente (vanishing gradient) se a inicialização dos pesos estiver errada
  • ReLU pode causar o fenômeno de ReLU morta (dead ReLU), em que neurônios ficam permanentemente desativados quando a entrada é menor ou igual a 0
  • Em RNNs, multiplicações repetidas de matrizes podem causar explosão do gradiente (exploding gradient), e para evitar isso é necessário usar gradient clipping ou LSTM
  • Se você não entender como a retropropagação funciona, mesmo que o framework cuide disso automaticamente, sua capacidade de depuração e de melhorar o modelo cai bastante

Por que é necessário entender a retropropagação

  • No curso CS231n de Stanford, as tarefas são projetadas para que os alunos implementem manualmente a propagação direta e a retropropagação
    • Alguns alunos reclamam que isso é desnecessário, já que frameworks como TensorFlow calculam a retropropagação automaticamente
  • Porém, a retropropagação é uma abstração com vazamento, não uma abstração completa, e sem conhecer seu funcionamento interno fica difícil identificar a causa de falhas no treinamento
  • A postura de simplesmente pensar “o framework resolve isso sozinho” acaba levando à queda na capacidade de projetar modelos e depurar problemas

Desvanecimento do gradiente em sigmoid

  • Funções não lineares como sigmoid e tanh entram em estado de saturação (saturation) quando os valores de entrada são grandes, fazendo a saída se aproximar de 0 ou 1
    • Nessa situação, o gradiente local z(1-z)* se torna 0, e a propagação do gradiente é bloqueada durante a retropropagação
  • O gradiente máximo da sigmoid é 0,25, então a cada passagem o sinal encolhe para 1/4 ou menos
  • Como resultado, a velocidade de aprendizado das camadas inferiores fica significativamente mais lenta do que a das camadas superiores
  • Por isso, ao usar camadas sigmoid, é preciso ter atenção especial à inicialização dos pesos e ao pré-processamento dos dados

O problema da ReLU morta

  • ReLU é uma função que transforma a saída em 0 quando a entrada é menor ou igual a 0
    • Se a saída do neurônio for 0 na propagação direta, então na retropropagação o gradiente também será 0, e esse neurônio pode ficar permanentemente desativado
  • Durante o treinamento, grandes atualizações de peso ou uma taxa de aprendizado alta podem fixar o neurônio em um “estado morto”
  • Há casos em que, após o treinamento, uma parte considerável de todos os neurônios continua produzindo 0
  • Portanto, ao usar ReLU, o ajuste da taxa de aprendizado e a estratégia de inicialização são importantes

Explosão do gradiente em RNNs

  • Em RNNs simples, a mesma matriz de estado oculto (Whh) é multiplicada repetidamente a cada passo de tempo
    • Na retropropagação, o gradiente converge para 0 ou cresce sem limite dependendo do tamanho do autovalor (eigenvalue) dessa matriz
  • Se |b| < 1, ocorre desvanecimento do gradiente; se |b| > 1, ocorre explosão do gradiente
  • Para evitar isso, é comum aplicar gradient clipping ou usar a arquitetura LSTM

Exemplo incorreto de clipping em código de DQN

  • Em uma implementação de DQN baseada em TensorFlow, foi encontrado um código que aplicava tf.clip_by_value diretamente ao delta (erro de Q)
    • Dessa forma, quando o delta saía da faixa definida, o gradiente se tornava 0 e o treinamento parava
  • Como a intenção era fazer clipping do gradiente, o correto seria usar Huber loss
    • No código de exemplo, a perda de Huber é implementada condicionalmente combinando tf.square e tf.abs
  • Esse problema foi relatado como issue no GitHub e corrigido imediatamente

Conclusão

  • Retropropagação não é apenas uma ferramenta automatizada, mas um sistema de atribuição de crédito (credit assignment) que produz consequências complexas
  • Sem entender seu funcionamento interno, você acaba enfrentando instabilidade do modelo, falhas de treinamento e limites na depuração
  • Retropropagação não é matematicamente tão difícil, e pode ser aprendida de forma intuitiva com o curso e as tarefas de CS231n
  • Entender retropropagação melhora bastante a capacidade de projetar redes neurais e resolver problemas
  • Em vez de depender apenas da diferenciação automática do framework, é importante compreender o fluxo real da retropropagação

1 comentários

 
GN⁺ 2025-11-03
Comentários no Hacker News
  • Parece que backpropagation está ganhando uma má reputação injustamente aqui
    Na verdade, esta discussão é mais sobre o quão imperfeitas são a gradient e as variantes de gradient descent como abstrações do processo de otimização do que sobre backpropagation em si
    Backpropagation é apenas um algoritmo para calcular a derivada de uma função composta, e problemas como o desaparecimento do gradiente ao empilhar várias sigmoides não são problemas do backpropagation, mas sim características da própria função
    O motivo de fazer as pessoas implementarem o backward pass manualmente é fazê-las calcular a derivada por conta própria e sentir na prática como os termos exponenciais atuam

    • Entendo o que você quer dizer, mas acho que, neste caso, essa “correção menor” não é muito útil
      O ponto principal é que, em certas situações, não dá para abstrair os detalhes do backpropagation, inclusive o cálculo do gradiente
      Isso vale especialmente ao usar gradient descent, e pode ser menos problemático em outros algoritmos de otimização global
      Como, na prática de hoje, o único jeito de calcular gradients em deep learning é via backpropagation, esse vazamento de abstração é algo real
    • Não acho que seja uma correção menor, mas uma contestação legítima a um enquadramento conceitual errado
      Respeito a contribuição do Karpathy, mas os textos e palestras dele muitas vezes embaralham distinções conceituais, o que acaba gerando mal-entendidos
      Pelo nível dele, espera-se uma precisão maior
  • A contribuição do Karpathy para o ensino de deep learning é realmente enorme
    De textos curtos até o artigo clássico sobre RNNs, além das aulas no YouTube e projetos no GitHub, tudo é excelente
    O nanochat, publicado recentemente, também é um ótimo exemplo: um exemplo completo, pequeno e claro ajuda muito quem está aprendendo

    • Recomendei os textos do Karpathy para colegas profundamente envolvidos com LLMs, mas, surpreendentemente, eles não se interessaram
      Depois percebi que eles estavam mais interessados em confiar e usar LLMs do que em entendê-los
      Na prática, pareciam mais fascinados por discussões especulativas do tipo “uma sociedade em que máquinas inteligentes fazem tudo” do que pelo funcionamento dos LLMs
    • O jeito como o Karpathy trabalha é interessante
      Em vez de simplesmente “perguntar ao ChatGPT”, ele pesquisa por conta própria, lê código e encontra bugs
      Esse tipo de abordagem investigativa é o verdadeiro aprendizado
  • No mestrado, fiz uma tarefa de implementar backpropagation manualmente com base em um artigo
    Era para escrever o forward e o backward pass usando apenas operações matemáticas, e foi a melhor experiência de aprendizado daquele ano
    É o tipo de tarefa que a gente dificilmente faria por conta própria, mas que ajuda demais quando se é obrigado a fazer

    • A diferença de entendimento entre só ler o artigo e implementar em código é enorme
    • Implementei isso no ensino médio em Java, e a parte mais difícil foi escrever a multiplicação de matrizes à mão
      Criei uma UI para visualizar como pesos e vieses mudavam durante o treinamento
    • Fiquei curioso para saber se esse artigo está disponível publicamente
  • Eu tinha curiosidade sobre a relação entre backpropagation e optimizer
    O SGD simplesmente anda na direção do gradiente, mas optimizers mais sofisticados, como Adam, não usam o gradiente de forma direta: aplicam normalização, momentum, clipping etc.
    Nesse caso, será que calcular o gradiente com exatidão é realmente necessário, ou basta saber a direção aproximada?

    • Na prática, o gradiente é aproximado com minibatches pequenos, então cada passo é ruidoso, mas esse ruído pode até ajudar no desempenho
      Há trabalhos relacionados, como artigos sobre o ruído no SGD e estudos de visualização
      Mas seguir uma direção “mais ou menos certa” é arriscado — a curvatura da função de loss (Hessian) muda abruptamente
    • “Será que não basta saber a direção, em vez do gradiente exato?” é uma pergunta antiga
      Na prática, ideias assim levaram ao stochastic gradient descent e, mais adiante, a abordagens como Direct Feedback Alignment
      Também é interessante o texto do Ben Recht sobre a relação entre otimização e aprendizado por reforço
    • Backpropagation calcula derivadas exatas no nível da precisão numérica
      O que importa não é tanto o valor da função de loss, mas a forma do gradiente e da curvatura
      Optimizers como Adam ajustam o gradiente estimando, por aproximação de primeira ordem, a sensibilidade de escala de cada parâmetro
      Otimização de ordem mais alta é inviável com funções não lineares como ReLU
    • Ajustar o gradiente não é um “fudge”
      É uma medida essencial para lidar com o problema de vanishing gradient
      Em espaços de alta dimensão, mesmo pequenos erros podem ter grande impacto, então calcular o gradiente com precisão é muito importante
    • O cálculo em si não é difícil
      O problema começa já em como calcular essa “direção aproximada”
      Mesmo optimizers avançados, como AdamW, ainda dependem fortemente do gradiente
  • Por volta de 2016, parece que truques como gradient clipping eram usados com muito mais frequência
    Por exemplo, no artigo de 2013 do Alex Graves, Sequence Generation with RNNs, ele diz que usou clipping para evitar explosão de gradiente em LSTMs
    Eu mesmo senti a importância do tema a ponto de fazer uma disciplina inteira focada só no autograd do PyTorch

    • Acho que antes havia muitos desses truques porque se experimentava com arquiteturas de rede mais variadas
      Hoje os frameworks já dão suporte a clipping, e a compreensão dos problemas de treinamento melhorou bastante
      Mas o problema em si não desapareceu — ReLU e GELU continuam sendo ativações padrão, e o treinamento de LLMs ainda se parece bastante com uma “arte obscura”
      O Smol Training Playbook da Hugging Face é prova disso
  • No longo prazo, talvez seja vantajoso treinar modelos robustos à diversidade de funções de ativação
    Por exemplo, alternar aleatoriamente entre ReLU, Swish, GELU etc. durante o treinamento poderia produzir um efeito de regularização semelhante ao dropout
    Isso também permitiria, na inferência, trocar por uma função computacionalmente mais barata

  • O título original, “Yes you should understand backprop”, é bem mais claro e melhor

    • Justamente porque backpropagation é uma abstração com vazamentos, é importante construir intuição calculando as coisas à mão
      Quando o código faz coisas demais por você, fica fácil cair na ilusão de que “funciona por mágica”
      Na pós-graduação, ter calculado convolução e backpropagation manualmente foi muito útil para mim
  • A pergunta “se o framework calcula o backward pass automaticamente, por que eu deveria escrever isso?”
    segue a mesma lógica preocupante de “se existe calculadora, por que aprender soma?”

    • Também há um contraponto
      Assim como é útil entender compiladores, algoritmos de ordenação e o funcionamento de transistores, aprender backpropagation também tem valor
      Mas, como o tempo de estudo é limitado, a questão central é se isso é mais útil do que estudar outros tópicos
      Backpropagation vale a pena justamente porque, sem entender seu funcionamento interno, fica difícil perceber modos de falha ocultos
      Eu mesmo já implementei várias vezes, e é uma complexidade ideal para avaliar uma linguagem nova
  • Quando comecei a aprender deep learning, backpropagation parecia quase mágica
    Mas, ao implementar por conta própria, vi que era apenas uma sequência de cálculos simples, e isso me deu muito mais confiança para depurar problemas e descobrir por que a loss parava de cair
    Para quem está aprendendo deep learning, eu recomendo implementar isso manualmente ao menos uma vez

  • Também existe a posição contrária
    Acho que estudantes não precisam necessariamente implementar backpropagation em NumPy
    O problema de vazamento do BackProp pode ser resolvido por pesquisadores com novos optimizers, e o desenvolvedor só precisa achar bons hiperparâmetros

    • Mas o objetivo da universidade é formar pesquisadores
    • Dizer que “basta escolher um bom optimizer” é um mal-entendido
      Parte do problema surge no design do modelo ou no loop de treinamento
      Por exemplo, gradient clipping não é o padrão na maioria dos frameworks
      Este texto é voltado a leitores com perfil de pesquisador ou interesse acadêmico
    • O problema não é o optimizer, mas as propriedades de derivação das funções de ativação
      Se você não entender como o gradiente se comporta em funções como sigmoid ou ReLU, não vai conseguir lidar com problemas de explosão ou desaparecimento
    • Em uma disciplina de CS em Stanford, é natural implementar os princípios básicos diretamente
      Para criar novas arquiteturas de modelo, é preciso entender como backpropagation funciona; caso contrário, o treinamento pode falhar ou perder desempenho
    • O problema é continuar sem saber aquilo que você não sabe
      Só ao atravessar a abstração é que você consegue descobrir as verdadeiras áreas desconhecidas (unknown unknowns)