Neste put up, vamos aprender a intuição por trás de um dos pilares da ascensão da inteligência synthetic. Junto com as modernas GPUs e a enorme quantidade de dados disponíveis, o algoritmo de retropropagação (backpropagation) está no centro das atenções, se destacando como um dos mais importantes para a computação e os negócios. A aprendizagem de máquina, com aplicações em processamento de linguagem, visão computacional e os grandes modelos LLM, tem esse algoritmo como base. Compreendê-lo bem abre as portas para entender como tudo isso que estamos vendo funciona. Ao last deste put up, você vai ter a intuição de como ele funciona e será capaz de implementar uma versão mais simplificada dele, sem otimizações, mas que vai te dar uma base para avançar e entender conceitos mais avançados.
No post anterior, vimos o que é um modelo de regressão linear e percebemos como pode ser trabalhoso ajustar os parâmetros manualmente para que o modelo seja útil de verdade. Agora, vamos entender como podemos automatizar esse processo e por que isso é um passo essencial para que as máquinas comecem a aprender sozinhas.
Vamos retomar o exemplo de prever o preço de venda de imóveis, porém para simplificar teremos apenas duas colunas no nosso dataset (conjunto de treino). O nosso problema é encontrar um modelo que dado um certo tamanho de terreno x, ele preveja o preço de venda y.
Dataset (Conjunto de treinamento)
Antes de prosseguir, vamos definir:
- x: Tamanho do terreno (m²)
- y: Preço de venda (R$) (em mil reais)
Queremos descobrir a relação entre o tamanho do terreno e o preço de venda, ou seja:
Agora, vamos visualizar esses dados em um gráfico para entender melhor como eles se comportam.
Observando o padrão formado pelos dados, ainda não podemos afirmar com certeza qual é o melhor modelo. No entanto, já é possível perceber que uma reta, como a que vimos antes, se ajustaria bem aos dados e seria um excelente ponto de partida.
O aprendizado de máquina, no fim das contas, é sobre fazer previsões. Temos um modelo que já observou alguns dados (como o tamanho de uma casa e seu preço) e agora queremos que ele consiga prever o preço de uma nova casa com base no tamanho dela.
Os valores de y são os preços reais das casas que o modelo já conhece. O que buscamos é que o modelo, usando a fórmula que ele aprendeu, faça uma boa estimativa para o preço de uma casa nova . Esse valor previsto é o ŷ (lê-se “y chapéu” ou hát).
- ŷ: saída que queremos prever
- w: é o equivalente ao “” da função linear, mas em ML é chamado de peso
- b: é o intercepto, mas em ML chamamos de viés.
- x: é x mesmo, que no nosso caso seria um novo tamanho de terreno, ex: 70m²
Em machine studying, os pesos (w) e o viés (b) geralmente são inicializados de forma aleatória. Podemos definir valores iniciais manualmente para eles e observar como o modelo se comporta.
Para w = 10 e b = 10, o modelo será:
Vamos calcular o preço previsto (ŷ) para uma entrada x = 70:
Resultado: 710 (mil reais)
Com esses parâmetros, o modelo superestimou bastante o preço. Existem casas muito maiores custando bem menos. Precisamos ajustar melhor o modelo.
Agora, testando w = 0,01 e b = 1, temos:
Mantendo x = 70:
Resultado: 1,7 (mil reais)
Esse valor ficou muito abaixo do esperado. Com parâmetros tão pequenos, o modelo está subestimando o preço.
Você já percebeu o perrengue que seria ficar ajustando w e b, né? Com sorte, depois de uma centena de tentativas, a gente conseguiria encontrar bons valores, isso para apenas uma variável x. Mas e se existissem x1,x2,…xn? Aí o problema poderia ficar insustentável. Por isso, desde a década de 80, cientistas utilizam um algoritmo chamado backpropagation para fazer esse trabalho sujo por nós.
Vamos retomar o exemplo que estamos seguindo e supor que inicializamos nosso modelo com w = 0.5 e b = 50.
Ao passarmos os valores de entrada que já temos no nosso dataset, podemos observar que os valores previstos pelo modelo (ŷ) estão bem distantes dos valores reais (y) que temos no dataset. Isso significa que, com esses parâmetros iniciais, o modelo apresenta erros grandes ou seja, ele ainda não está ajustado para representar bem a relação entre as variáveis.
No gráfico, podemos visualizar claramente essas distâncias como as linhas verticais entre os pontos reais e a reta: quanto maiores essas linhas, maior o erro (resíduo). Precisaremos ajustar os parâmetros w e b para reduzir esses erros e aproximar a reta dos dados.
Até agora temos um modelo desregulado e um sonho de um dia conseguir prever bem os preços dos imóveis, para isso vamos aprender a “treinar” nosso modelo para que ele aprenda a fazer a tarefa direito.
As primeiras etapas para esse treinamento são:
- Calcular todos os (ŷ) usando como entrada cada valor de nosso conjunto de treino
- Comparar (ŷ) com o (y) que já temos no nosso conjunto de treino e ver qual foi o erro
Uma das formas utilizadas para isso é calcular o erro quadrático entre a saída actual e a saída prevista (y — ŷ)², elevamos ao quadrado pra penalizar erros grandes, melhorando os cálculos.
Esse erro vai ser utilizado para melhorar o valor dos parâmetros w e b na próxima rodada de treino. Mas o erro que de fato usamos é o Erro Quadrado Médio (MSE)
A fórmula é:
- Em poucas palavras, para cada linha de nosso conjunto de treinamento
- Calcule o erro quadrado, onde i = 1, 2, 3, …, n
- Some tudo: (y1 — ŷ1)²+(y2 — ŷ2)²++(yn — ŷn)²,
- divida tudo por n, que é o complete de exemplos
Apenas pra entender o que compõe a components do MSE
- Função de Perda L: avalia o erro para um único exemplo, ou seja, para um par de entrada . Então o L seria:
- Função de Custo J: é a média das perdas de todos os exemplos do dataset, ou seja, o erro complete do modelo para o conjunto de dados inteiro. Vamos chamar MSE de J a partir de agora pra facilitar:
Até aqui:
- Definimos nosso conjunto de treino
- Chutamos um modelo que pode se ajustar bem a nossos dados
- Inicializamos w e b com valores aleatórios
- Fizemos o passo pra frente que é a média entre os quadrados dos erros de y e ŷ
Antes de prosseguir, precisamos entender que, para treinar nosso modelo, sempre precisaremos passar por todo o conjunto de dados. Cada passada completa do dataset pelo modelo é chamada de época. A cada época, calculamos o custo J, e usamos esse valor para descobrir os novos valores que w e b vão assumir. Você pode escolher um número fixo de épocas para o treino ou definir um valor mínimo de erro como critério para parar o treinamento.
Agora vem a grande pergunta: como vamos atualizar nossos parâmetros?
Por exemplo, digamos que o J calculado foi 4. Okay, mas… isso não nos diz para onde ajustar w e b. Como saber se aumentamos ou diminuímos?
No nosso exemplo, inicializamos:
Para simplificar, vamos focar apenas no w e imaginar b = 0.
A função J(w,b) representa o erro do modelo ŷ = w x + b. Dá para imaginar o J como um controle com dois botões (um para w, outro para b) que você pode girar; dependendo de como girar, o erro do modelo ŷ aumenta ou diminui.
A forma da função J nesse caso é uma parábola. Inicializamos w = 0.5, mas poderia ter sido qualquer valor aleatório.
Olhando o gráfico, observamos:
- w=0.5 (ponto verde que inicializamos)
- A função J tem um mínimo
- w está à esquerda desse mínimo
Quando w atinge o mínimo, isso significa que o erro J é zero ou o menor possível. Hmm… então o objetivo é chegar nesse ponto mínimo! E agora? Devemos somar ou subtrair algo em w para nos aproximarmos desse mínimo?
- Só de olhar a imagem, sabemos que somar um pequeno valor a w o aproximaria do mínimo (pois ele está à esquerda).
- Se estivesse à direita, precisaríamos subtrair um valor para chegar ao mínimo.
- Eu, vendo o gráfico, consigo perceber isso. Mas como a máquina sabe?
A resposta também está na figura:
- há uma reta tangente passando pelo ponto w, com uma certa inclinação.
- Essa inclinação é justamente a derivada. (Vixi… começou a complicar de novo!)
- Mas calma! Não é matemática de construir foguete, conseguimos vencer.
O gradiente descente (GD) é um algorítimo utilizado para encontrar o mínimo de funções, para o nosso caso, que temos apenas uma variável x ele é uma parábola 2D, nosso objetivo é descer a cada iteração e chegar ao fundo. Se ouvessem duas variáveis, o gráfico do gradiente seria parecido com uma cadeia montanhosa com vales e picos, mas objetivo seria o mesmo, descer até o mínimo.
Quando a função tem n variaveis x, sequer é possivel imaginar o gráfico pois ele está em dimensões acima do 3D, mesmo assim o GD consegue lidar bem com isso e encontrar o mínino, a cada iteração ele “olha para os lados” e vê por onde é melhor continuar a descida, igual um alpinista experiente. Mas vamos focar no nosso 2D com uma variável apenas pra não complicar muito.
Já aprendemos bastante até aqui, mas ainda falta responder uma pergunta importante. “Como saber para onde ajustar os valores de w e b?” A resposta está no cálculo da derivada de J em relação a w e a b. Com essa informação, o algoritmo finalmente sabe em que direção deve seguir para minimizar J e reduzir o erro.
Para simplificar, vamos calcular a derivada considerando apenas um único exemplo de treino. No last, somamos os resultados de todos os exemplos e dividimos por n, seguindo o que já fizemos antes. Também multiplicamos J por ½ para deixar a derivação e os cálculos mais fáceis de explicar.
Não precisa se preocupar demais com os detalhes matemáticos agora. Se não se sentir totalmente confortável, pode avançar para a próxima parte. Esta explicação é mais para quem quer entender o que realmente acontece por trás do algoritmo. Se alguma parte não ficar clara, vale dar uma olhada rápida no conceito de derivada. Aqui estamos usando regras de derivação como a regra da potência e a regra da cadeia. Se tiver curiosidade, vale pesquisar mais sobre elas, mas garanto que são tranquilas de entender. Agora vamos seguir com os passos da derivação:
Temos:
Derivamos J em relação ao erro e = ŷ — y:
Derivamos e em relação a ŷ:
Derivamos ŷ em relação a w:
Aplicamos a regra da cadeia:
Resumindo:
Desafio, sabendo da regra pra w, qual seria a derivada de J em relação a b? Mandem nos comentários.
Agora vamos introduzir α que é um valor que você escolhe para controlar a velocidade com que o algoritmo aprende. Ela determina o tamanho do passo que o modelo dá a cada atualização dos parâmetros.
Se α for muito grande, o modelo pode dar passos largos demais e acabar passando direto pelo ponto mínimo, oscilando ou até mesmo divergindo (em vez de diminuir o erro, ele aumenta).
Se α for muito pequeno, os passos serão tão curtos que o modelo vai demorar muito para aprender, exigindo um número maior de épocas para chegar perto do mínimo.
costumo começar com α = 0,01 e vou ajustando conforme observo o comportamento do erro, se oscila muito, diminuo, se o aprendizado está lento, aumento um pouco. Juntando tudo temos:
- Gradiente em relação a w:
- Gradiente em relação a b:
- Substituindo temos a notação last de nosso GD:
A cada passo o w e o b recebem a atualização de seu valor multiplicado pelos valores das suas respectivas derivadas em relação a J. Be aware que multiplicamos pelo pra evitar que o passo seja muito grande. Então repetimos até convergir, e com sorte após determinadas épocas encontramos um modelo que se ajusta bem aos nosso dados, como na figura abaixo:
- Inicializa os parâmetros (pesos w e viés b) com valores aleatórios.
- Faz a previsão (ŷ = wx + b) para os dados de treino.
- Calcula o erro entre a previsão e o valor actual (ex.: erro quadrático médio — MSE).
- Computa o gradiente (derivadas de J em relação a w e b) para descobrir em que direção ajustar os parâmetros.
- Atualiza os parâmetros na direção oposta ao gradiente, usando a taxa de aprendizado α:
- Repete os passos 2–5 por várias épocas até o erro ser minimizado.
Agora que você pegou a intuição de como o algoritmo funciona, podemos partir para a prática e escrever um exemplo simples usando Python e NumPy, deixamos professional próximo put up. Na prática, frameworks como TensorFlow e PyTorch já fazem todo esse trabalho pesado por trás dos panos. Mas quando você entende o que realmente está acontecendo, fica muito mais fácil depurar, otimizar seu modelo, ajustar os dados e até experimentar novas ideias. Por isso pelo menos uma vez na vida vale a pena implementar do zero com sua linguagem de preferência pra fixar bem os conceitos por trás desse algorítmo.
Entender o algoritmo de backpropagation vai muito além de simplesmente decorar fórmulas. É como entrar nos bastidores e ver como a máquina realmente aprende, ajustando seus parâmetros aos poucos para transformar dados crus em previsões mais precisas. Por trás de redes neurais, deep studying e todas as aplicações modernas de IA, esse mecanismo atua ali, de forma quase invisível, aprimorando modelos que hoje em dia reconhecem imagens, traduzem textos e até geram conteúdo.
Dominar o backpropagation não só nos ajuda a criar modelos mais poderosos e robustos, mas também traz aquela sensação de “uau”, quando a gente entende um dos pilares mais elegantes e fundamentais da inteligência synthetic. É como enxergar o motor de uma revolução tecnológica e, melhor ainda, fazer parte dela.
Até a próxima e bora gerar!
By Thiago Pablicio