← Blog

Redes neurais: como o neurônio artificial aprende

2023-05-20Neural NetworksDeep LearningPython

O neurônio como unidade de cálculo

Uma rede neural artificial é uma pilha de neurônios conectados. Cada neurônio recebe números de entrada, multiplica por pesos, soma tudo com um viés (bias) e passa o resultado por uma função de ativação. A saída vai para o próximo neurônio.

Esse modelo vem de uma simplificação do neurônio biológico: dendritos recebem sinais, o corpo celular integra, o axônio transmite. Na versão matemática, não há química. Só aritmética repetida em escala.

O que torna redes neurais úteis não é um neurônio isolado. É a combinação de milhares delas em camadas, com pesos ajustáveis, capazes de aproximar funções complexas. Mas o aprendizado acontece neurônio por neurônio. Entender um é entender a base de tudo.

Forward pass: da entrada à saída

O forward pass é o caminho dos dados da entrada até a predição. Para um único neurônio, o cálculo tem três etapas.

Primeiro, cada entrada xᵢ é multiplicada pelo peso wᵢ correspondente. Segundo, as contribuições são somadas com o bias b, gerando o valor pré-ativação z. Terceiro, z passa pela função de ativação f, produzindo a saída a.

A fórmula:

z = w₁x₁ + w₂x₂ + ... + wₙxₙ + b
a = f(z)

Duas ativações aparecem com frequência:

  • ReLU (max(0, z)): zera valores negativos. Rápida e estável em redes profundas.
  • Sigmoid (1 / (1 + e⁻ᶻ)): comprime a saída entre 0 e 1. Comum em classificação binária na saída.
Forward pass de um neurônio

Entradas (x)

x10.60
x20.40
x30.80

Pesos (w)

w10.8contribuição: 0.48
w2-0.5contribuição: -0.20
w30.3contribuição: 0.24
Bias (b)0.1

Soma ponderada (z)

z = (0.8×0.60) + (-0.5×0.40) + (0.3×0.80) + 0.1

z = 0.620

Função de ativação

Saída (a)

0.6200

a = max(0, z)

Mova os sliders de entrada e troque entre ReLU e Sigmoid. Repare como a soma ponderada muda e como a ativação transforma z em a. Em uma rede real, essa conta se repete para cada neurônio de cada camada.

Camadas: empilhando neurônios

Um neurônio sozinho resolve problemas linearmente separáveis. Para padrões mais complexos, empilhe camadas.

Uma rede típica tem três tipos de camada:

  1. Entrada: recebe os features brutos (pixels, idade, renda, embedding)
  2. Oculta: combina e recombina padrões intermediários
  3. Saída: produz a predição final (classe, probabilidade, valor contínuo)

A saída de cada camada vira entrada da próxima. Se a camada oculta tem 128 neurônios e a entrada tem 10 features, existem 10×128 = 1.280 conexões com pesos próprios, mais 128 biases.

Em notação matricial, uma camada inteira vira uma multiplicação de matriz seguida de ativação:

import numpy as np

def relu(z):
    return np.maximum(0, z)

# X: batch de 32 exemplos, cada um com 10 features
X = np.random.randn(32, 10)
W = np.random.randn(10, 128) * 0.01  # pesos iniciais pequenos
b = np.zeros(128)

# forward pass da camada
z = X @ W + b   # (32, 128)
a = relu(z)     # ativação elemento a elemento

O @ é produto matricial. Cada linha de X é um exemplo; cada coluna de W é um neurônio. Esse formato é o que frameworks como PyTorch e TensorFlow executam em GPU, milhões de vezes por segundo.

O que significa "aprender"

Pesos aleatórios produzem predições ruins. Aprender é ajustar w e b para que a saída se aproxime do valor correto.

O ciclo de treino tem quatro passos:

  1. Forward pass: calcular a predição com os pesos atuais
  2. Calcular perda: medir o erro entre predição e alvo
  3. Backward pass: calcular o gradiente de cada peso (quanto cada um contribuiu para o erro)
  4. Atualizar pesos: mover cada peso na direção que reduz a perda

A perda (loss) é um número escalar que resume o erro. Para regressão, MSE é comum: L = ½(y - ŷ)². Para classificação binária, cross-entropy. O objetivo do treino é minimizar L.

Um passo de gradiente descendente

O gradiente ∂L/∂w indica a direção em que o peso mais aumenta a perda. Para reduzir o erro, ande na direção oposta:

w_novo = w_antigo - η · ∂L/∂w

η (eta) é a taxa de aprendizado. Muito alto: o treino oscila e diverge. Muito baixo: converge devagar.

Um passo de aprendizado

Cada passo ajusta os pesos na direção que reduz a perda. Observe ŷ se aproximar de y.

Entradas fixas

x1 = 1.0

x2 = 0.5

Pesos atuais

w1 = 0.100

w2 = 0.100

b = 0.000

Alvo (y) / Predição (ŷ)

y = 1

ŷ = 0.5374

Perda (L) = 0.1070

Taxa de aprendizado (η)0.50

Gradiente ∂L/∂w

∂L/∂w1 = -0.1150∂L/∂w2 = -0.0575∂L/∂b = -0.1150
Passos: 0

Clique em "Aplicar 1 passo" várias vezes. Os pesos sobem, a predição ŷ se aproxima do alvo y = 1, e a perda cai. Ajuste a taxa de aprendizado e veja como passos grandes podem overshootar.

A regra da cadeia conecta a perda ao peso:

∂L/∂w = ∂L/∂ŷ · ∂ŷ/∂z · ∂z/∂w

Cada fator mede sensibilidade de uma etapa à anterior. Em redes profundas, esse cálculo se propaga camada por camada de trás para frente. É o backpropagation.

Backpropagation em redes profundas

Com uma camada, o gradiente é direto. Com dez camadas, a regra da cadeia se aplica repetidamente: o gradiente da camada l depende do gradiente da camada l+1.

PyTorch calcula isso automaticamente com loss.backward():

import torch
import torch.nn as nn

model = nn.Sequential(
    nn.Linear(10, 128),
    nn.ReLU(),
    nn.Linear(128, 1),
    nn.Sigmoid(),
)

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
criterion = nn.BCELoss()

X_batch = torch.randn(32, 10)
y_batch = torch.randint(0, 2, (32, 1)).float()

# 1. forward
y_pred = model(X_batch)

# 2. perda
loss = criterion(y_pred, y_batch)

# 3. backward — calcula gradientes de todos os pesos
loss.backward()

# 4. update — aplica w = w - lr * gradiente
optimizer.step()
optimizer.zero_grad()  # zera gradientes para o próximo batch

Você não precisa derivar manualmente cada peso. O autograd do PyTorch rastreia as operações do forward pass e aplica a regra da cadeia no backward. O que importa entender é o mecanismo: perda → gradiente → atualização.

Além do básico: o que muda na prática

Alguns detalhes separam o conceito do treino que funciona em produção.

Inicialização dos pesos. Pesos zerados fazem todos os neurônios aprenderem a mesma coisa (problema de simetria). Inicialização pequena e aleatória (Xavier, He) quebra a simetria.

Funções de ativação por camada. Camadas ocultas usam ReLU ou GELU. A saída usa sigmoid (binário), softmax (multiclasse) ou linear (regressão).

Otimizadores. SGD puro é raro em redes grandes. Adam e AdamW ajustam a taxa de aprendizado por parâmetro e convergem mais rápido na maioria dos casos.

Vanishing gradient. Em redes muito profundas com sigmoid em todas as camadas, gradientes podem encolher até zerar nas camadas iniciais. ReLU, skip connections (ResNet) e normalização (BatchNorm) mitigam isso.

Overfitting. Redes grandes memorizam o treino. Regularização (dropout, weight decay), early stopping e mais dados são as respostas usuais.

Conclusão

Um neurônio artificial soma entradas ponderadas, aplica ativação e repete isso em camadas. Aprender é medir o erro (perda), calcular quanto cada peso contribuiu (gradiente) e ajustar na direção oposta (gradiente descendente). Backpropagation automatiza esse cálculo em redes profundas.

Se você entende forward pass, perda e um passo de atualização, o resto do deep learning (CNNs, transformers, fine-tuning) são variações de arquitetura sobre o mesmo mecanismo. O próximo passo natural é treinar uma rede pequena em scikit-learn ou PyTorch com um dataset real e observar a curva de perda cair epoch a epoch.

Referencias

  1. Deep Learning — Goodfellow, Bengio, Courville (livro)
  2. 3Blue1Brown — Neural networks (série em vídeo)
  3. PyTorch — Autograd mechanics
  4. PyTorch — nn.Module tutorial
  5. CS231n — Neural networks and backpropagation
  6. Scikit-learn — MLPClassifier