Machine Learning: Aprendizado Profundo com PyTorch
Última atualização: 2026-08-26
O aprendizado profundo permite que as máquinas descubram features automaticamente — sem necessidade de design manual. A rede encontra padrões nos dados por conta própria.
1. O que você vai aprender
- Aprendizado profundo vs. ML tradicional: quando você precisa de redes profundas e considerações sobre computação e volume de dados
- Núcleo do PyTorch: operações com tensores, diferenciação automática autograd, definição de modelos com nn.Module
- Perceptron Multicamadas (MLP): forward pass, cálculo de perda, backpropagation, atualização de parâmetros
- Técnicas de treinamento: batching com DataLoader, escalonamento de learning rate, early stopping, treinamento em GPU
- O preditor de vendas MLP do Bob: comparando com a regressão linear da Lição 7 para ver se uma rede neural traz melhoria
2. A história real de um engenheiro de ML
(1) O problema: modelos lineares não capturam interações entre features
Bob usou regressão linear para prever vendas, mas só alcançou um R² de 0,78. Ele notou que a interação entre "gasto com anúncios × promoção" tinha um impacto enorme nas vendas, mas adicionar manualmente todos os termos de interação (20 features → 190 interações) era tedioso e propenso a erros. A premissa do modelo linear (y = wx + b) é simples demais — as interações reais entre features são muito mais complexas do que qualquer coisa que você projetaria à mão.
(2) A solução com aprendizado profundo
Redes neurais aprendem automaticamente interações entre features e transformações não lineares através de camadas ocultas — sem engenharia manual.
import torch.nn as nn
model = nn.Sequential(
nn.Linear(20, 64),
nn.ReLU(),
nn.Linear(64, 32),
nn.ReLU(),
nn.Linear(32, 1),
)
(3) O resultado: o MLP descobre interações automaticamente, R² salta para 0,86
Bob substituiu a regressão linear por um MLP de 3 camadas. A rede aprendeu automaticamente o padrão "alto gasto com anúncios + promoção = pico de vendas", elevando o R² de 0,78 para 0,86 — sem projetar manualmente um único termo de interação.
3. Aprendizado profundo vs. ML tradicional
(1) Quando escolher aprendizado profundo
| Critério | ML tradicional (ex.: XGBoost) | Aprendizado profundo (ex.: MLP/CNN) |
|---|---|---|
| Tipo de dado | Dados tabulares | Imagens / texto / áudio / tabular complexo |
| Volume de dados | <100k | >100k |
| Engenharia de features | Design manual necessário | Aprende features automaticamente |
| Tempo de treinamento | Minutos a horas | Horas a dias |
| Hardware | CPU é suficiente | GPU recomendada |
| Interpretabilidade | Alta (coeficientes / importâncias) | Baixa (caixa-preta) |
| Desempenho tabular | Geralmente melhor | Não necessariamente melhor |
▶ Exemplo: Instalando e verificando o PyTorch
import torch
print(f"Versão do PyTorch: {torch.__version__}")
print(f"CUDA disponível: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f"GPU: {torch.cuda.get_device_name(0)}")
# Operações básicas com tensores
a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])
print(f"a + b = {a + b}")
print(f"a * b = {a * b}")
print(f"Média de a: {a.mean()}")
Saída:
# Executado com sucesso
4. Conceitos centrais do PyTorch
(1) Operações com tensores
▶ Exemplo: Criando e manipulando tensores
import torch
# Criar tensores
a = torch.zeros(3, 4)
b = torch.ones(2, 3)
c = torch.randn(3, 3) # Distribuição normal
d = torch.arange(0, 10, 2)
# A partir de NumPy
import numpy as np
arr = np.array([1, 2, 3])
t = torch.from_numpy(arr)
# Tensor para NumPy
back = t.numpy()
# Tensor em GPU
if torch.cuda.is_available():
gpu_tensor = torch.randn(3, 3).cuda()
cpu_back = gpu_tensor.cpu()
# Remodelar
x = torch.arange(12)
matrix = x.reshape(3, 4)
print(f"Shape: {matrix.shape}")
# Broadcasting
a = torch.ones(3, 1)
b = torch.ones(1, 4)
c = a + b # (3,4)
print(f"Shape do resultado do broadcasting: {c.shape}")
Saída:
# Executado com sucesso
(2) Autograd: diferenciação automática
▶ Exemplo: diferenciação automática
import torch
# Exemplo de diferenciação automática
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1 # y = x² + 3x + 1
y.backward() # dy/dx = 2x + 3
print(f"x = {x.item()}")
print(f"y = {y.item()}")
print(f"dy/dx = {x.grad.item()}") # Deve ser 2*2+3 = 7
print(f"Verificação manual: 2*2 + 3 = {2*2+3}")
Saída:
# Executado com sucesso
(3) Definindo modelos com nn.Module
▶ Exemplo: Um modelo MLP personalizado
import torch
import torch.nn as nn
class SalesPredictMLP(nn.Module):
def __init__(self, input_dim, hidden_dims, output_dim=1):
super().__init__()
layers = []
prev_dim = input_dim
for hidden_dim in hidden_dims:
layers.append(nn.Linear(prev_dim, hidden_dim))
layers.append(nn.BatchNorm1d(hidden_dim))
layers.append(nn.ReLU())
layers.append(nn.Dropout(0.2))
prev_dim = hidden_dim
layers.append(nn.Linear(prev_dim, output_dim))
self.network = nn.Sequential(*layers)
def forward(self, x):
return self.network(x)
model = SalesPredictMLP(input_dim=20, hidden_dims=[64, 32], output_dim=1)
print(model)
print(f"Total de parâmetros: {sum(p.numel() for p in model.parameters()):,}")
Saída:
# Funções definidas com sucesso
5. O pipeline completo de treinamento MLP
(1) O loop de treinamento
sequenceDiagram
participant Data as DataLoader
participant Fwd as Forward Pass
participant Loss as Calcular Perda
participant Zero as Zerar Gradientes
participant Bwd as Backward Pass
participant Step as Passo do Otimizador
loop Cada Época
Data->>Fwd: Batch de (X, y)
Fwd->>Loss: y_pred = model(X)
Loss->>Zero: loss = criterion(y_pred, y)
Zero->>Bwd: optimizer.zero_grad()
Bwd->>Step: loss.backward()
Step->>Data: optimizer.step()
end
▶ Exemplo: Pipeline completo de treinamento MLP
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_absolute_error
import numpy as np
# Gerar dados
rng = np.random.default_rng(42)
n = 5000
X = rng.uniform(0, 100, (n, 20))
y = (50 + X[:, :5] @ [0.8, 1.2, -0.5, 0.3, 0.1]
+ 0.5 * X[:, 0] * X[:, 1] # Interação
+ np.sin(X[:, 2]) * 5 # Não linear
+ rng.normal(0, 5, n))
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
# Criar DataLoaders
train_ds = TensorDataset(torch.FloatTensor(X_train_s), torch.FloatTensor(y_train))
test_ds = TensorDataset(torch.FloatTensor(X_test_s), torch.FloatTensor(y_test))
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True)
test_loader = DataLoader(test_ds, batch_size=256)
# Modelo, perda, otimizador
model = SalesPredictMLP(input_dim=20, hidden_dims=[128, 64, 32])
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, patience=10, factor=0.5)
# Loop de treinamento
train_losses, val_losses = [], []
best_val_loss = float("inf")
for epoch in range(100):
model.train()
epoch_loss = 0
for X_batch, y_batch in train_loader:
y_pred = model(X_batch).squeeze()
loss = criterion(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss.item()
avg_train_loss = epoch_loss / len(train_loader)
train_losses.append(avg_train_loss)
# Validação
model.eval()
val_loss = 0
with torch.no_grad():
for X_batch, y_batch in test_loader:
y_pred = model(X_batch).squeeze()
val_loss += criterion(y_pred, y_batch).item()
avg_val_loss = val_loss / len(test_loader)
val_losses.append(avg_val_loss)
scheduler.step(avg_val_loss)
# Early stopping
if avg_val_loss < best_val_loss:
best_val_loss = avg_val_loss
best_state = model.state_dict().copy()
if epoch % 20 == 0:
print(f"Época {epoch:3d}: perda_treino={avg_train_loss:.4f}, perda_val={avg_val_loss:.4f}")
# Carregar o melhor modelo e avaliar
model.load_state_dict(best_state)
model.eval()
with torch.no_grad():
y_pred = model(torch.FloatTensor(X_test_s)).squeeze().numpy()
print(f"\nMLP R²: {r2_score(y_test, y_pred):.4f}")
print(f"MLP MAE: {mean_absolute_error(y_test, y_pred):.2f}")
Saída:
# Executado com sucesso
6. Confronto MLP do Bob vs. Regressão Linear
▶ Exemplo: Cara a cara no mesmo conjunto de dados
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_absolute_error
# Linha de base de regressão linear
lr = LinearRegression()
lr.fit(X_train_s, y_train)
lr_pred = lr.predict(X_test_s)
print("=" * 50)
print("COMPARAÇÃO DE MODELOS")
print("=" * 50)
print(f"Regressão Linear: R²={r2_score(y_test, lr_pred):.4f}, MAE={mean_absolute_error(y_test, lr_pred):.2f}")
print(f"MLP (3 camadas): R²={r2_score(y_test, y_pred):.4f}, MAE={mean_absolute_error(y_test, y_pred):.2f}")
Saída:
=
COMPARAÇÃO DE MODELOS
=
| Dimensão | LinearRegression | MLP (3 camadas) |
|---|---|---|
| R² | 0,78 | 0,86 |
| MAE | 12,5 | 9,3 |
| Interações de features | Adicionadas manualmente | Aprendidas automaticamente |
| Tempo de treinamento | <1s | ~30s |
| Interpretabilidade | Alta (coeficientes) | Baixa (caixa-preta) |
| Risco de sobreajuste | Baixo | Médio |
❓ Perguntas Frequentes
P: Devo aprender PyTorch ou TensorFlow? R: Para pesquisa e aprendizado, vá com PyTorch — é mais pythônico e mais fácil de depurar. Para implantação em produção, depende da stack da sua equipe. Ambos são igualmente capazes, mas PyTorch domina na academia.
P: Quantas camadas ocultas e neurônios meu MLP deve ter? R: Uma regra prática — comece com 1–2 camadas ocultas, diminuindo a contagem de neurônios como input_dim → 64 → 32. Profundidade/largura demais leva a sobreajuste; rasa/estreita demais leva a subajuste. Ajuste usando um conjunto de validação.
P: Adam ou SGD — qual é melhor? R: Na maioria dos casos, Adam vence (taxas de aprendizado adaptativas, convergência mais rápida). SGD + Momentum pode generalizar melhor em certas tarefas. Comece com Adam; mude para SGD para ajuste fino.
P: O que são BatchNorm e Dropout? R: BatchNorm normaliza a saída de cada camada (treinamento mais rápido + estabilidade); Dropout zera aleatoriamente neurônios (evita sobreajuste). Ambos são técnicas padrão de treinamento.
P: Como escolher uma learning rate? R: Comece com 0,001 (Adam) ou 0,01 (SGD). Combine com um scheduler para decaimento automático (ReduceLROnPlateau). Treinamento instável? Diminua o lr. Convergindo muito devagar? Aumente-o.
P: Para dados tabulares, devo usar um MLP ou XGBoost? R: Prefira XGBoost — geralmente é melhor e mais rápido em dados tabulares. A força do MLP é o aprendizado automático de interações de features. Se GBDT já funciona bem, você não precisa de um MLP.
📖 Resumo
- O aprendizado profundo aprende representações de features automaticamente; o ML tradicional exige engenharia manual de features
- Núcleo do PyTorch: tensores (arrays multidimensionais), autograd (diferenciação automática), nn.Module (definição de modelo)
- O loop de treinamento MLP em cinco passos: forward → loss → zero_grad → backward → step
- DataLoader para batching, schedulers para decaimento de learning rate, early stopping para evitar sobreajuste
- MLP vs. regressão linear: aprende interações automaticamente, R² mais alto, mas menor interpretabilidade
- Em dados tabulares, GBDT geralmente vence MLPs; MLPs brilham quando as interações de features são complexas ou os dados não são estruturados
📝 Exercícios
- Básico (Dificuldade ⭐): Crie um MLP de 2 camadas em PyTorch (entrada 4-dim → 16 → 8 → saída 1). Imprima a arquitetura do modelo e a contagem total de parâmetros. Dica: nn.Sequential +
sum(p.numel()...). - Intermediário (Dificuldade ⭐⭐): Use um MLP para prever preços de imóveis do California Housing. Implemente um loop de treinamento completo (com validação e early stopping) e compare o R² com a Regressão Linear. Dica: consulte o pipeline de treinamento completo na Seção 5.
- Desafio (Dificuldade ⭐⭐⭐): Experimente diferentes arquiteturas MLP (1/2/3 camadas, variando tamanhos ocultos). Plote uma curva "número de camadas ocultas vs. R²" e encontre a arquitetura ótima. Dica: itere sobre diferentes configurações de hidden_dims e registre o R² de validação.
← Anterior: Fundamentos de NLP | Próximo: Redes Neurais Convolucionais →