Visão Geral das Redes Neurais
Redes neurais são modelos computacionais inspirados em sistemas biológicos, capazes de aprender padrões complexos por meio de camadas de transformações não-lineares. Seus principais benefícios incluem:
- Extração Automática de Features: Elimina a necessidade de engenharia manual de características, comum em métodos tradicionais como SIFT.
- Aprendizado End-to-End: Mapeia diretamente dados brutos para saídas desejadas, por exemplo, de imagens para categorias.
- Universalidade: Redes com pelo manos uma camada oculta podem aproximar qualquer função contínua, conforme o teorema de aproximação universal.
Desafios Fundamentais na Aprendizagem Automática
O Abismo Semântico
Tornar máquinas inteligentes enfrenta obstáculos como:
- Problema de Symbol Grounding: Conceitos humanos, como "gato", são abstratos, enquanto máquinas lidam com representações numéricas, como matrizes de pixels.
- Escassez de Dados: Cenários reais são infinitos, mas os dados de treino são limitados, criando desafios como os corner cases em condução autônoma.
- Limitações na Inferência Causal: Redes neurais aprendem correlações, mas não modelam relações causais de forma explícita.
Representação Vetorial de Imagens
Processamento do Dataset MNIST
Para imagens de dígitos manuscritos em escala de cinza de 28x28 pixels, cada exemplo é vetorizado em um array unidimensional de 784 elementos. A seguir, um exemplo de carregamento usando PyTorch:
import torch
from torchvision import datasets, transforms
# Configuração do conjunto de dados
conjunto_treino = datasets.MNIST(
root='dados',
train=True,
download=True,
transform=transforms.Compose([transforms.ToTensor()])
)
# Inspeção das dimensões
imagem, rotulo = conjunto_treino[0]
print(imagem.flatten().shape) # Saída: torch.Size([784])
A Unidade Básica: Neurônio Artificial
Formulação Matemática
Um neurônio computa uma soma ponderada das entradas, adiciona um viés e aplica uma função de ativação. A saída é dada por a = σ(Σ(wi * xi) + b), onde wi são pesos, xi são entradas, b é o viés e σ é uma não-linearidade como ReLU ou Sigmoid.
Implementação em código:
class Neuronio:
def __init__(self, dim_entrada):
self.pesos = torch.randn(dim_entrada)
self.viés = torch.randn(1)
def processar(self, entrada):
soma_ponderada = torch.dot(entrada, self.pesos) + self.viés
saida_ativada = torch.tanh(soma_ponderada) # Usando tangente hiperbólica
return saida_ativada
# Exemplo de uso
neuronio = Neuronio(784)
resultado = neuronio.processar(torch.rand(784))
Arquitetura de Redes Neurais
Modelo de Camada Totalmente Conectada
Uma arquitetura típica para classificação de dígitos pode ser estruturada como:
Camada de Entrada (784 neurônios) → Camada Oculta (512 neurônios) → Camada de Saída (10 neurônios)
O número de parâmetros é calculado como (784 * 512 + 512) + (512 * 10 + 10) = 407,050.
Código em PyTorch:
import torch.nn as nn
class RedeMulticamadas(nn.Module):
def __init__(self):
super().__init__()
self.camada_oculta = nn.Linear(784, 512)
self.camada_saida = nn.Linear(512, 10)
self.ativacao = nn.ReLU()
def forward(self, x):
x = x.view(-1, 784) # Redimensionar para vetor
x = self.ativacao(self.camada_oculta(x))
x = self.camada_saida(x)
return x
modelo = RedeMulticamadas()
print(modelo)
Camadas de Entrada e Saída
Princípios de Design para Entrada
- Normalização: Escalar valores de pixel de [0, 255] para [0, 1] ou usar estatísticas do dataset.
- Processamento em Lotes: Aproveitar a paralelização da GPU, por exemplo, com batch_size=128.
Exemplo de pré-processamento:
transformacoes = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)) # Média e desvio padrão do MNIST
])
Design da Camada de Saída
Para tarefas de classificação, utiliza-se a função de ativação Softmax e a perda de entropia cruzada (CrossEntropyLoss) durante o treinamento.
Camadas Ocultas: O Coração da Aprendizagem Profunda
Papel na Extração de Features
Camadas ocultas permitem a extração hierárquica de características:
- Primeira camada: detecta bordas e texturas simples.
- Camadas intermediárias: combinam padrões para formar formas complexas.
- Camadas finais: reconhecem objetos inteiros.
Matrizes de Pesos e Inicialização
Representação e Estratégias
Os pesos conectam neurônios entre camadas, por exemplo, a matriz W entre a entrada e a camada oculta tem dimensões (784, 512). Uma inicialização adequada, como Xavier, ajuda a manter a variância das ativações estável.
Código para inicialização:
nn.init.xavier_uniform_(modelo.camada_oculta.weight)
Os pesos são atualizados via retropropagação, com a regra w = w - η * ∂L/∂w, onde η é a taxa de aprendizado.
Exemplo Completo: Treinamento no MNIST
Código integrado para carregar dados, definir o modelo e executar o loop de treino:
from torch.utils.data import DataLoader
# Preparação dos dados
loader_treino = DataLoader(
datasets.MNIST('dados', train=True, download=True, transform=transformacoes),
batch_size=128, shuffle=True
)
# Configuração do treinamento
funcao_perda = nn.CrossEntropyLoss()
otimizador = torch.optim.Adam(modelo.parameters(), lr=0.001)
for epoca in range(10):
for lote_dados, lote_rotulos in loader_treino:
otimizador.zero_grad()
saidas = modelo(lote_dados)
perda = funcao_perda(saidas, lote_rotulos)
perda.backward()
otimizador.step()
print(f'Época {epoca+1} - Perda: {perda.item():.4f}')