AI: Redes Neurais
Última atualização: 2026-08-26
"Aprendizado profundo" pode parecer misterioso e complexo, mas seu bloco básico de construção — o neurônio — é simplesmente uma soma ponderada seguida de uma transformação não linear. Começando com o perceptron, este capítulo o guia passo a passo através de redes multicamadas, funções de ativação, propagação para frente e retropropagação, e mostra como implementar um MLP do zero usando Python puro que pode resolver o problema do XOR.
1. O Que Você Vai Aprender
- Perceptron: A unidade de rede neural mais simples
- Estrutura e Intuição de Redes Neurais Multicamadas (MLPs)
- O Papel e a Seleção de Funções de Ativação (Sigmoid / ReLU / Tanh)
- A Intuição Central por Trás da Propagação Direta e Retropropagação
- Deep learning = uma pilha de redes neurais multicamadas
2. História: O "Cérebro" por Trás de 10 Linhas de Código
(1) Ponto de Dor: O que exatamente é Deep Learning?
Bob tinha ouvido que "deep learning" podia reconhecer rostos, traduzir idiomas e escrever artigos, mas ele não tinha a menor ideia do que era uma rede neural. Ele tentou pesquisar na Wikipedia, mas a tela cheia de derivadas parciais e da regra da cadeia fez ele fechar a página imediatamente. "Alguém não pode simplesmente me explicar nos termos mais simples o que um 'neurônio' realmente faz?"
(2) Os 10 linhas de código da Alice
Alice sorriu enquanto abria o Python e escrevia um perceptron em 10 linhas de código — ele recebe dois números como entrada e produz 0 ou 1 como saída:
# Um único perceptron: porta AND
def perceptron(x1, x2, w1=1, w2=1, b=-1.5):
return 1 if w1 * x1 + w2 * x2 + b > 0 else 0
for x1, x2 in [(0,0),(0,1),(1,0),(1,1)]:
print(f"AND({x1},{x2}) = {perceptron(x1, x2)}")
AND(0,0) = 0
AND(0,1) = 0
AND(1,0) = 0
AND(1,1) = 1
Bob percebeu: "Isso não é apenas votação ponderada? Ambas as entradas são definidas como 1, e se a soma ponderada exceder o limiar, a saída é 1 — então uma rede neural é apenas uma série dessas unidades de decisão simples conectadas juntas!"
(3) Benefícios: De unidades simples a redes poderosas
Alice assentiu. "É isso mesmo. Um único perceptron só pode realizar classificação linear, mas ao empilhar dezenas ou centenas de perceptrons em uma rede multicamadas, podemos aproximar funções extremamente complexas — e é aí que o deep learning começa."
3. Perceptron—A Rede Neural Mais Simples
(1) Definição Matemática de um Perceptron
O perceptron, proposto pelo psicólogo Rosenblatt em 1958, é um dos modelos mais antigos de um neurônio artificial. Ele recebe múltiplas entradas $x_1, x_2, \ldots, x_n$, cada uma com um peso $w_i$, mais um viés $b$, e finalmente produz a saída 0 ou 1 através de uma função degrau:
$$y = \text{step}\left(\sum_{i=1}^{n} w_i x_i + b\right)$$
Em particular, $\text{step}(z) = 1$ se $z > 0$, e $0$ caso contrário.
Intuição: Imagine que você está decidindo se vai sair para correr—temperatura, qualidade do ar, se você tem tempo e seu humor—cada fator tem um "peso", e se o total exceder seu "limite de preguiça", você sai.
(2) O Significado Geométrico do Perceptron
Um perceptron desenha uma linha reta $w_1 x_1 + w_2 x_2 + b = 0$ no espaço bidimensional, produzindo a saída 1 de um lado da linha e 0 do outro. Ele pode resolver perfeitamente problemas linearmente separáveis, como AND e OR, mas não consegue resolver o problema do XOR—porque as duas classes de pontos em um problema XOR não podem ser separadas por uma única linha reta.
▶ Exemplo: Uma Implementação Pura em Python de uma Porta AND com Perceptron (Dificuldade: ⭐)
# Porta AND com Perceptron e treinamento
def step(z):
return 1 if z > 0 else 0
def train_perceptron(X, y, lr=0.1, epochs=10):
n_features = len(X[0])
weights = [0.0] * n_features
bias = 0.0
for epoch in range(epochs):
total_error = 0
for xi, yi in zip(X, y):
z = sum(w * x for w, x in zip(weights, xi)) + bias
pred = step(z)
error = yi - pred
total_error += abs(error)
for j in range(n_features):
weights[j] += lr * error * xi[j]
bias += lr * error
if total_error == 0:
break
return weights, bias
X = [[0,0],[0,1],[1,0],[1,1]]
y_and = [0, 0, 0, 1]
w, b = train_perceptron(X, y_and)
print(f"Weights: {w}, Bias: {b}")
for xi in X:
print(f"AND{xi} = {step(sum(ww*xx for ww,xx in zip(w,xi)) + b)}")
Weights: [0.2, 0.1], Bias: -0.2
AND[0, 0] = 0
AND[0, 1] = 0
AND[1, 0] = 0
AND[1, 1] = 1
4. Arquitetura do Perceptron Multicamadas (MLP)
(1) Por que são necessárias múltiplas camadas?
Um perceptron de camada única só pode traçar linhas retas e não consegue resolver o problema XOR. No entanto, se as saídas de dois perceptrons forem alimentadas em um terceiro perceptron, torna-se possível combinar duas linhas retas para formar uma fronteira não-linear. Esta é a ideia central por trás do perceptron multicamadas (MLP).
(2) A Estrutura de Três Camadas de um MLP
| Camada | Função | Exemplo |
|---|---|---|
| Camada de entrada | Recebe os recursos brutos | 2 nós (x₁, x₂) |
| Camada oculta | Extrai recursos intermediários | 4 nós (recursos aprendidos) |
| Camada de saída | Fornece a previsão final | 1 nó (0 ou 1) |
Cada nó em cada camada está conectado a todos os nós da camada anterior (totalmente conectado), e a força dessas conexões é representada por pesos.
(3) O Processo de Propagação Direta em Redes Neurais
graph LR
X1["x₁"] --> H1["h₁ (sigmoid)"]
X1 --> H2["h₂ (sigmoid)"]
X2["x₂"] --> H1
X2 --> H2
H1 --> Y["y (saída)"]
H2 --> Y
style X1 fill:#e1f5fe
style X2 fill:#e1f5fe
style H1 fill:#fff9c4
style H2 fill:#fff9c4
style Y fill:#c8e6c9
Intuição: A camada de entrada é como os olhos, as camadas ocultas são como as regiões de processamento do cérebro, e a camada de saída é como a boca falando a resposta. Cada camada realiza uma "soma ponderada + transformação não-linear."
(4) Comparação: Perceptron vs. MLP vs. Redes Neurais Profundas
| Dimensão | Perceptron | MLP | Rede Neural Profunda |
|---|---|---|---|
| Número de camadas | 1 camada (sem camadas ocultas) | 2–3 camadas | ≥ 4 camadas (incluindo múltiplas camadas ocultas) |
| Capacidades | Classificação Linear | Classificação/Regressão Não-linear | Extração Automática de Recursos Complexos |
| Treinamento | Regras do Perceptron | Retropropagação | Retropropagação + Técnicas de Otimização |
| Aplicações Típicas | Portas lógicas simples | Classificação/regressão em pequena escala | Reconhecimento de imagens, PLN, geração |
| Número de parâmetros | Dezenas | Centenas a milhares | Dezenas de milhões a bilhões |
| Modelos Representativos | Perceptron de Rosenblatt | MLP de 2 camadas | CNN, Transformer |
5. Funções de Ativação—Adicionando Não-linearidade à Rede
(1) Por que as Funções de Ativação São Necessárias?
Sem funções de ativação, independentemente do número de camadas, uma rede é essencialmente uma transformação linear—a composição de múltiplas transformações lineares permanece linear. As funções de ativação introduzem não-linearidade, permitindo que a rede ajuste curvas e limites complexos.
Analogia: Transformações lineares são como "esticar e girar"; não importa quantas vezes você as estique ou gire, o resultado ainda é uma linha reta ou um plano. Funções de ativação são como "dobrar"—dobre um pedaço de papel uma vez, e o espaço muda.
(2) Comparação de Funções de Ativação Comuns
| Função | Fórmula | Intervalo de Saída | Vantagens | Desvantagens |
|---|---|---|---|---|
| Sigmoid | $\sigma(z)=\frac{1}{1+e^{-z}}$ | (0, 1) | A saída pode ser tratada como uma probabilidade | Gradientes que desaparecem, centro não-zero |
| Tanh | $\tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}$ | (-1, 1) | Centrado em zero | O gradiente desaparece |
| ReLU | $\max(0, z)$ | [0, +∞) | Computação rápida, mitiga o problema de gradiente desaparecendo | Morte do neurônio (gradiente é 0 na região negativa) |
| Leaky ReLU | $\max(0.01z, z)$ | (-∞, +∞) | Aborda a morte do neurônio | Requer ajuste de hiperparâmetros |
▶ Exemplo: Comparando os Valores de Saída de Diferentes Funções de Ativação (Dificuldade: ⭐)
# Compare activation functions
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def tanh(z):
return np.tanh(z)
def relu(z):
return np.maximum(0, z)
def leaky_relu(z, alpha=0.01):
return np.where(z > 0, z, alpha * z)
z_values = np.array([-5, -2, -1, 0, 1, 2, 5])
print(f"{'z':>5} | {'Sigmoid':>8} | {'Tanh':>8} | {'ReLU':>8} | {'LeakyReLU':>10}")
print("-" * 55)
for z in z_values:
s = sigmoid(z)
t = tanh(z)
r = float(relu(np.array([z]))[0])
lr = float(leaky_relu(np.array([z]))[0])
print(f"{z:5.1f} | {s:8.4f} | {t:8.4f} | {r:8.4f} | {lr:10.4f}")
z | Sigmoid | Tanh | ReLU | LeakyReLU
-------------------------------------------------------
-5.0 | 0.0067 | -0.9999 | 0.0000 | -0.0500
-2.0 | 0.1192 | -0.9640 | 0.0000 | -0.0200
-1.0 | 0.2689 | -0.7616 | 0.0000 | -0.0100
0.0 | 0.5000 | 0.0000 | 0.0000 | 0.0000
1.0 | 0.7311 | 0.7616 | 1.0000 | 1.0000
2.0 | 0.8808 | 0.9640 | 2.0000 | 2.0000
5.0 | 0.9933 | 0.9999 | 5.0000 | 5.0000
6. Propagação para Frente e Retropropagação
(1) Propagação para frente: Da entrada à saída
A propagação para frente é o processo pelo qual os dados viajam da camada de entrada, passando por cada camada, até a camada de saída:
- Calcular a entrada da camada oculta: $z^{(1)} = W^{(1)} \cdot x + b^{(1)}$
- Aplicar a função de ativação: $h = \sigma(z^{(1)})$
- Calcular a entrada da camada de saída: $z^{(2)} = W^{(2)} \cdot h + b^{(2)}$
- Previsão de saída: $\hat{y} = \sigma(z^{(2)})$
(2) Função de Perda: Mede a discrepância entre a previsão e o valor real
O Erro Quadrático Médio (MSE) é comumente usado como função de perda:
A perda é o quadrado da diferença entre o valor real e o valor previsto.
Quanto menor a perda, mais precisa é a previsão. O objetivo do treinamento é encontrar os pesos que minimizem a perda.
(3) Retropropagação: Do erro à atualização dos pesos
A ideia central da retropropagação: Partindo do erro na camada de saída, calcular a "contribuição" de cada peso para o erro, propagando-o para trás através das camadas da rede, e então ajustar os pesos.
Analogia intuitiva: Uma fábrica tem um problema com seus produtos—
- Primeiro, verificar quantos erros ocorreram na etapa final (camada de saída)
- Perguntar novamente quantos erros foram causados pelo processo anterior (camada oculta)
- Rastrear camada por camada até chegar às matérias-primas (camada de entrada)
- Cada processo deve ajustar seus procedimentos operacionais com base nos erros para os quais contribuiu
Matematicamente, esta é a Regra da Cadeia: $\frac{\partial L}{\partial w} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial w}$
(4) Tabela de Conceitos-chave na Retropropagação
| Conceito | Significado | Intuição |
|---|---|---|
| Gradiente | A derivada parcial da perda em relação aos pesos | Em qual direção e em qual medida os pesos devem ser ajustados |
| Taxa de Aprendizado | Tamanho do passo por iteração | Muito grande → ultrapassa o ótimo; muito pequeno → o treinamento é muito lento |
| Regra da Cadeia | Passagem do gradiente camada por camada | Rastreando "quem é o responsável" camada por camada da saída para a entrada |
| Atualização de Peso | $w \leftarrow w - \eta \cdot \frac{\partial L}{\partial w}$ | Dá um passo na direção oposta ao gradiente |
| Gradientes Desvanecentes | Os gradientes diminuem a cada camada e se aproximam de 0 | As primeiras camadas aprendem quase nada |
| Explosão de Gradientes | Os gradientes amplificam-se camada por camada | As atualizações de peso são muito agressivas, fazendo o modelo se tornar instável |
(5) Intuição para Superfícies de Perda
Imagine que você está de pé em uma montanha e seu objetivo é chegar ao fundo do vale (perda mínima). A cada passo, você segue pela encosta mais íngreme—isso é descida de gradiente. A taxa de aprendizado determina o tamanho dos seus passos:
- Passo muito longo: Você pode acabar atravessando o fundo do vale e aterrissando na encosta oposta
- Passo muito curto: Você tem andado por horas e ainda está apenas a meio caminho da montanha.
- Passo ótimo: Chegar ao fundo do vale de forma estável e rápida
7. Experimente Rapidamente um MLP com sklearn
▶ Exemplo: Classificação usando o MLPClassifier do sklearn (Dificuldade: ⭐)
# MLPClassifier no problema XOR
from sklearn.neural_network import MLPClassifier
import numpy as np
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])
mlp = MLPClassifier(hidden_layer_sizes=(4,), activation='relu',
max_iter=2000, random_state=42)
mlp.fit(X, y)
print("Previsões do XOR:")
for xi in X:
print(f" {xi} -> {mlp.predict([xi])[0]}")
print(f"Acurácia: {mlp.score(X, y):.2f}")
Previsões do XOR:
[0 0] -> 0
[0 1] -> 1
[1 0] -> 1
[1 1] -> 0
Acurácia: 1.00
MLPClassifier do sklearn trata automaticamente de todos os detalhes, incluindo propagação para frente, retropropagação e inicialização de pesos. Uma vez que você entenda os princípios subjacentes, usar a biblioteca é a abordagem mais eficiente.
▶ Exemplo: Visualizando Mudanças nas Fronteiras de Decisão (Dificuldade: ⭐⭐)
# Visualizar fronteira de decisão do MLP no XOR
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPClassifier
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
hidden_configs = [(2,), (4,), (8,)]
for ax, config in zip(axes, hidden_configs):
mlp = MLPClassifier(hidden_layer_sizes=config, activation='relu',
max_iter=3000, random_state=42)
mlp.fit(X, y)
xx, yy = np.meshgrid(np.linspace(-0.5, 1.5, 100),
np.linspace(-0.5, 1.5, 100))
Z = mlp.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
ax.scatter(X[:,0], X[:,1], c=y, cmap='coolwarm', edgecolors='k', s=100)
ax.set_title(f"Oculta: {config}, Acurácia: {mlp.score(X,y):.2f}")
ax.set_xlabel("x1")
ax.set_ylabel("x2")
plt.tight_layout()
plt.savefig("mlp_decision_boundary.png", dpi=100)
plt.show()
Saída: (Execute o exemplo para ver a saída real, ou consulte a nota de saída esperada nos comentários do código acima.)
▶ Exemplo: Como o Número de Nós em uma Camada Oculta Afeta o Desempenho (Dificuldade: ⭐⭐)
# Efeito do tamanho da camada oculta na perda de treinamento
import numpy as np
from sklearn.neural_network import MLPClassifier
import matplotlib.pyplot as plt
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])
fig, ax = plt.subplots(figsize=(8, 5))
for n_hidden in [2, 4, 8, 16]:
mlp = MLPClassifier(hidden_layer_sizes=(n_hidden,), activation='relu',
max_iter=3000, random_state=42, solver='lbfgs')
mlp.fit(X, y)
ax.plot(range(len(mlp.loss_curve_)), mlp.loss_curve_,
label=f"Oculta={n_hidden}")
ax.set_xlabel("Iteração")
ax.set_ylabel("Perda")
ax.set_title("Perda de Treinamento vs Tamanho da Camada Oculta")
ax.legend()
ax.set_yscale('log')
plt.savefig("hidden_size_loss.png", dpi=100)
plt.show()
Saída: (Execute o exemplo para ver a saída real, ou consulte a nota de saída esperada nos comentários do código acima.)
8. Deep Learning = Redes Neurais de Múltiplas Camadas
(1) Comparação entre ML Tradicional e Deep Learning
| Dimensão | Aprendizado de Máquina Tradicional | Deep Learning |
|---|---|---|
| Engenharia de Features | Requer design e seleção manual de features | Aprende automaticamente as features a partir dos dados |
| Requisitos de Volume de Dados | Uma pequena quantidade de dados é suficiente | Geralmente requer uma grande quantidade de dados |
| Recursos Computacionais | Apenas CPU | Geralmente requer GPU |
| Interpretabilidade do Modelo | Alta (árvores de decisão, modelos lineares) | Baixa (caixa preta) |
| Teto de Desempenho | Limitado pela qualidade das features | Escala com os dados e o tamanho do modelo |
| Algoritmos Representativos | SVM, Random Forest, XGBoost | CNN, RNN, Transformer |
| Cenários Aplicáveis | Dados estruturados, pequenas amostras | Dados não estruturados, como imagens, texto e fala |
(2) O que significa "profundo"?
"Profundidade" refere-se ao número de camadas ocultas. Há um consenso geral de que:
- Rede rasa: 1 camada oculta (MLP)
- Redes Neurais Profundas: 2 ou mais camadas ocultas
- Redes ultra-profundas: dezenas ou até centenas de camadas (a ResNet-152 tem 152 camadas)
Quanto mais camadas, mais abstratas são as features que a rede pode extrair—a primeira camada pode aprender bordas, a segunda pode aprender texturas e a terceira pode aprender componentes; conforme avançamos na rede, as features tornam-se cada vez mais "semânticas".
(3) Mais profundidade não é necessariamente melhor
Uma rede mais profunda não significa necessariamente um melhor desempenho. Camadas demais podem levar a:
- Desaparecimento/explosão do gradiente: O sinal atenua ou amplifica conforme passa por camadas sucessivas
- Overfitting: Parâmetros demais, fazendo com que o modelo memorize os dados de treinamento em vez de aprender os padrões subjacentes
- Custo de Treinamento: Mais parâmetros = tempo de treinamento mais longo + mais poder computacional
A profundidade do modelo deve ser escolhida com base na complexidade da tarefa e na quantidade de dados—assim como construir um arranha-céu: nem sempre mais alto é melhor; é preciso considerar a fundação e o orçamento.
9. Exercício Prático Abrangente: Implementando um Classificador MLP do Zero
▶ Exemplo: Implementando uma MLP do Zero para Resolver o Problema XOR (Dificuldade: ⭐⭐⭐)
Abaixo, implementaremos uma MLP completa usando Python puro (sem quaisquer frameworks), incluindo propagação para frente, retropropagação e o loop de treinamento, com o objetivo de resolver o problema XOR.
# MLP from scratch: solve XOR with pure Python + NumPy
import numpy as np
# --- Activation functions and their derivatives ---
def sigmoid(z):
return 1 / (1 + np.exp(-np.clip(z, -500, 500)))
def sigmoid_deriv(a):
return a * (1 - a)
# --- MLP class ---
class MLP:
def __init__(self, layer_sizes):
# layer_sizes e.g. [2, 4, 1] => 2-input, 4-hidden, 1-output
self.weights = []
self.biases = []
for i in range(len(layer_sizes) - 1):
w = np.random.randn(layer_sizes[i], layer_sizes[i+1]) * 0.5
b = np.zeros((1, layer_sizes[i+1]))
self.weights.append(w)
self.biases.append(b)
def forward(self, X):
self.activations = [X]
for i in range(len(self.weights)):
z = self.activations[-1] @ self.weights[i] + self.biases[i]
a = sigmoid(z)
self.activations.append(a)
return self.activations[-1]
def backward(self, y, lr=0.5):
m = y.shape[0]
deltas = [None] * len(self.weights)
# Output layer delta
output = self.activations[-1]
deltas[-1] = (output - y) * sigmoid_deriv(output)
# Hidden layer deltas (backprop)
for i in range(len(self.weights) - 2, -1, -1):
deltas[i] = (deltas[i+1] @ self.weights[i+1].T) * sigmoid_deriv(self.activations[i+1])
# Update weights and biases
for i in range(len(self.weights)):
self.weights[i] -= lr * (self.activations[i].T @ deltas[i]) / m
self.biases[i] -= lr * np.mean(deltas[i], axis=0, keepdims=True)
def train(self, X, y, epochs=5000, lr=0.5):
self.losses = []
for epoch in range(epochs):
pred = self.forward(X)
loss = np.mean((y - pred) ** 2) / 2
self.losses.append(loss)
self.backward(y, lr)
if epoch % 1000 == 0:
print(f"Epoch {epoch:4d} | Loss: {loss:.6f}")
def predict(self, X):
return (self.forward(X) > 0.5).astype(int)
# --- Train on XOR ---
np.random.seed(42)
X = np.array([[0,0],[0,1],[1,0],[1,1]], dtype=float)
y = np.array([[0],[1],[1],[0]], dtype=float)
mlp = MLP([2, 4, 1])
mlp.train(X, y, epochs=5000, lr=1.0)
print("\nFinal Predictions:")
preds = mlp.predict(X)
for i in range(4):
print(f" XOR{X[i].astype(int).tolist()} = {preds[i,0]} (target: {y[i,0].astype(int)})")
Epoch 0 | Loss: 0.132415
Epoch 1000 | Loss: 0.004726
Epoch 2000 | Loss: 0.002517
Epoch 3000 | Loss: 0.001751
Epoch 4000 | Loss: 0.001330
Final Predictions:
XOR[0, 0] = 0 (target: 0)
XOR[0, 1] = 1 (target: 1)
XOR[1, 0] = 1 (target: 1)
XOR[1, 1] = 0 (target: 0)
❓ Perguntas Frequentes
📖 Resumo
- Perceptron: Soma ponderada + função degrau, o classificador linear mais simples
- MLP: Multi-Layer Perceptron; camadas ocultas fornecem capacidades não lineares
- Função de Ativação: Introduz não linearidade; ReLU é a escolha padrão moderna
- Propagação Direta (Forward): Os dados são processados camada por camada, da camada de entrada até a camada de saída
- Retropropagação (Backpropagation): O erro é propagado para trás, camada por camada, a partir da camada de saída, para atualizar os pesos
- Aprendizado Profundo (Deep Learning): Redes Neurais Multicamada: Aprendizado Automático de Recursos Multi-Nível
- Descida do Gradiente (Gradient Descent): Descer ao longo da inclinação mais íngreme da superfície de perda para encontrar os pesos ótimos
Conceito-Chave: Um neurônio = soma ponderada + transformação não linear. Uma rede neural = muitos neurônios empilhados juntos. Deep learning = uma rede neural com muitas camadas. Do perceptron com 10 linhas de código ao GPT, todos seguem essencialmente o mesmo paradigma.
📝 Exercícios
- Básico (⭐): Implemente um perceptron para uma porta OR. A tabela verdade da porta OR é: (0,0) → 0, (0,1) → 1, (1,0) → 1, (1,1) → 1. Modifique os rótulos no código de treinamento do perceptron deste capítulo, treine o modelo e verifique os resultados.
# Starter code: OR gate perceptron
X = [[0,0],[0,1],[1,0],[1,1]]
y_or = [0, 1, 1, 1]
# TODO: train and test
- Avançado (⭐⭐): Use o MLPClassifier do sklearn para realizar a classificação no conjunto de dados Iris, ajuste os parâmetros e registre a acurácia para diferentes configurações:
# Starter code: MLP on Iris
from sklearn.datasets import load_iris
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.3, random_state=42)
# TODO: try different hidden_layer_sizes, activation, learning_rate
# Record accuracy for each config
Tente pelo menos 3 configurações diferentes de hidden_layer_sizes (como (10,), (50,50), (100,)) e 2 funções de ativação, e anote qual configuração funciona melhor.
- Desafio (⭐⭐⭐): Com base na implementação pura em Python do MLP deste capítulo, plote a curva de perda para o MLP implementado manualmente:
# After training MLP from Section 9, plot the loss curve
import matplotlib.pyplot as plt
# mlp.losses is already recorded during training
plt.figure(figsize=(8, 5))
plt.plot(range(len(mlp.losses)), mlp.losses, linewidth=1)
plt.xlabel("Epoch")
plt.ylabel("Loss (MSE)")
plt.title("MLP Training Loss Curve (XOR)")
plt.yscale('log')
plt.grid(True, alpha=0.3)
plt.savefig("mlp_loss_curve.png", dpi=100)
plt.show()
Extensões Avançadas: ① Compare as curvas de perda para diferentes taxas de aprendizado (0.1, 0.5, 1.0, 2.0) no mesmo gráfico; ② Experimente com diferentes tamanhos de camada oculta (2, 4, 8) para ver como afetam a taxa de convergência; ③ Observe se a perda oscila ou até diverge quando a taxa de aprendizado é muito alta.