Bruno Ferreira

Deep Learning

Notes: These are personal class notes, not necessarily cohesive, not necessarily structured, and β€” most importantly β€” not necessarily accurate! Please check the sources, and feel free to point out any errors via email!


Referencia: Bishop, D2L e MIT conforme a lista de aulas.

01. Introduction

Deep learning aprende representacoes em camadas:

π‘₯β†’1π‘“β„Ž1β†’2π‘“β„Ž2→⋯→𝑦̂

A ideia central e ajustar parametros πœƒ para minimizar uma perda:

πœƒβˆ—=argminπœƒ1π‘βˆ‘π‘–=1𝑁ℒ︀(π‘“πœƒ(π‘₯𝑖),𝑦𝑖)

Notas rapidas:

  • Dados viram tensores.
  • O modelo produz predicoes.
  • A loss mede o erro.
  • O otimizador altera os pesos para reduzir a loss.
  • Generalizar significa ir bem em dados nao vistos, nao apenas memorizar treino.
import torch

x = torch.randn(32, 10)      # batch com 32 exemplos e 10 atributos
y = torch.randint(0, 3, (32,))

print(x.shape, y.shape)
02. Basics on Neural Networks

Um neuronio combina entrada, pesos e vies:

𝑧=𝑀𝑇π‘₯+𝑏

Depois aplica uma ativacao nao linear:

β„Ž=πœ‘(𝑧)

Sem nao linearidade, varias camadas viram apenas uma transformacao linear.

MLP:

β„Ž1=πœ‘(π‘Š1π‘₯+𝑏1),𝑦̂=π‘Š2β„Ž1+𝑏2

Para classificacao multiclasse, usamos softmax:

𝑝(𝑦=π‘˜|π‘₯)=π‘’π‘§π‘˜βˆ‘π‘—π‘’π‘§π‘—

Cross-entropy:

β„’οΈ€=βˆ’log𝑝(𝑦=𝑐|π‘₯)
import torch
from torch import nn

model = nn.Sequential(
    nn.Linear(10, 64),
    nn.ReLU(),
    nn.Linear(64, 3)
)

x = torch.randn(32, 10)
y = torch.randint(0, 3, (32,))

logits = model(x)
loss = nn.CrossEntropyLoss()(logits, y)

loss.backward()
print(loss.item())
03. Convolutional Neural Networks + CNN Architectures

CNNs exploram estrutura espacial. Em vez de conectar tudo com tudo, filtros pequenos percorrem a imagem.

Convolucao 2D simplificada:

π‘Œπ‘–,𝑗,π‘˜=π‘π‘˜+βˆ‘π‘βˆ‘π‘’βˆ‘π‘£π‘Šπ‘’,𝑣,𝑐,π‘˜π‘‹π‘–+𝑒,𝑗+𝑣,𝑐

Intuicao:

  • Filtros aprendem detectores locais: bordas, texturas, partes.
  • O mesmo filtro e reutilizado em varias posicoes: compartilhamento de pesos.
  • Isso reduz parametros e cria equivariancia a translacao.

Tamanho da saida em uma dimensao:

𝑂=⌊𝐼+2π‘ƒβˆ’πΎπ‘†βŒ‹+1

onde 𝐼 e entrada, 𝑃 padding, 𝐾 kernel e 𝑆 stride.

import torch
from torch import nn

cnn = nn.Sequential(
    nn.Conv2d(3, 16, kernel_size=3, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(16, 32, kernel_size=3, padding=1),
    nn.ReLU(),
    nn.AdaptiveAvgPool2d((1, 1)),
    nn.Flatten(),
    nn.Linear(32, 10)
)

x = torch.randn(8, 3, 64, 64)
logits = cnn(x)
print(logits.shape)  # torch.Size([8, 10])

Arquiteturas importantes:

  • LeNet: CNN classica pequena para digitos.
  • AlexNet: ReLU, GPU, dropout; marco em ImageNet.
  • VGG: muitos filtros 3Γ—3, arquitetura simples e profunda.
  • Inception/GoogLeNet: varios tamanhos de filtro em paralelo.
  • ResNet: conexoes residuais facilitam redes muito profundas.

Bloco residual:

𝑦=𝐹(π‘₯)+π‘₯
import torch
from torch import nn

class ResidualBlock(nn.Module):
    def __init__(self, channels):
        super().__init__()
        self.net = nn.Sequential(
            nn.Conv2d(channels, channels, 3, padding=1),
            nn.BatchNorm2d(channels),
            nn.ReLU(),
            nn.Conv2d(channels, channels, 3, padding=1),
            nn.BatchNorm2d(channels),
        )
        self.act = nn.ReLU()

    def forward(self, x):
        return self.act(self.net(x) + x)
04. Training

Treinar e repetir:

  1. forward: calcular 𝑦̂=π‘“πœƒ(π‘₯)
  2. loss: medir β„’οΈ€(𝑦̂,𝑦)
  3. backward: calcular gradientes βˆ‡πœƒβ„’οΈ€
  4. update: alterar pesos

Atualizacao basica por gradiente descendente:

πœƒπ‘‘+1=πœƒπ‘‘βˆ’πœ‚βˆ‡πœƒβ„’οΈ€(πœƒπ‘‘)

Mini-batch SGD aproxima o gradiente usando poucos exemplos:

𝑔𝑑=1π΅βˆ‘π‘–βˆˆβ„¬οΈ€βˆ‡πœƒβ„’οΈ€π‘–(πœƒπ‘‘)

Pontos praticos:

  • Normalizar entradas ajuda a estabilizar treino.
  • Inicializacao ruim pode matar gradientes.
  • BatchNorm reduz sensibilidade a escala interna.
  • Learning rate costuma importar mais que pequenos detalhes do modelo.
import torch
from torch import nn

model = nn.Sequential(nn.Linear(10, 64), nn.ReLU(), nn.Linear(64, 3))
opt = torch.optim.SGD(model.parameters(), lr=0.1)
loss_fn = nn.CrossEntropyLoss()

for step in range(100):
    x = torch.randn(32, 10)
    y = torch.randint(0, 3, (32,))

    opt.zero_grad()
    logits = model(x)
    loss = loss_fn(logits, y)
    loss.backward()
    opt.step()
05. Optimization & Generalization

Otimizacao pergunta: como reduzir a loss de treino?

Generalizacao pergunta: o modelo aprendeu padroes ou decorou?

Momentum suaviza atualizacoes:

𝑣𝑑+1=𝛽𝑣𝑑+βˆ‡πœƒβ„’οΈ€(πœƒπ‘‘)πœƒπ‘‘+1=πœƒπ‘‘βˆ’πœ‚π‘£π‘‘+1

Adam combina media de gradientes e de gradientes ao quadrado:

π‘šπ‘‘=𝛽1π‘šπ‘‘βˆ’1+(1βˆ’π›½1)𝑔𝑑𝑣𝑑=𝛽2π‘£π‘‘βˆ’1+(1βˆ’π›½2)𝑔𝑑2

Regularizacao L2:

β„’οΈ€total=β„’οΈ€data+πœ†β€–πœƒβ€–22

Dropout:

β„ŽΜƒ=π‘šβ‹…β„Ž,π‘šπ‘–βˆΌBernoulli(𝑝)
import torch
from torch import nn

model = nn.Sequential(
    nn.Linear(100, 256),
    nn.ReLU(),
    nn.Dropout(p=0.5),
    nn.Linear(256, 10)
)

opt = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=50)

Checklist mental:

  • loss de treino alta: underfitting, modelo fraco, LR ruim ou poucos epochs.
  • treino bom e validacao ruim: overfitting.
  • treino instavel: LR alto, dados mal escalados, batch pequeno ou inicializacao ruim.
  • validacao melhorando: continue.
  • validacao parou de melhorar: early stopping ou reduzir LR.
06. Semantic Segmentation

Segmentacao semantica classifica cada pixel.

Entrada:

π‘‹βˆˆβ„πΆΓ—π»Γ—π‘Š

Saida:

π‘ŒΜ‚βˆˆβ„πΎΓ—π»Γ—π‘Š

onde 𝐾 e o numero de classes. Cada pixel recebe uma distribuicao sobre classes.

Loss por pixel:

β„’οΈ€=βˆ’1π»π‘Šβˆ‘π‘–,𝑗log𝑝(𝑦𝑖,𝑗|π‘₯)

IoU para uma classe:

IoU=TPTP+FP+FN

Dice:

Dice=2|𝐴∩𝐡||𝐴|+|𝐡|

Arquiteturas comuns:

  • FCN: troca camadas densas por convolucoes e gera mapa denso.
  • U-Net: encoder comprime contexto, decoder recupera resolucao, skips preservam detalhe.
  • DeepLab: convolucoes dilatadas aumentam campo receptivo sem perder tanta resolucao.
import torch
from torch import nn

class TinyUNet(nn.Module):
    def __init__(self, classes):
        super().__init__()
        self.enc = nn.Sequential(
            nn.Conv2d(3, 16, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(16, 16, 3, padding=1),
            nn.ReLU(),
        )
        self.pool = nn.MaxPool2d(2)
        self.dec = nn.Sequential(
            nn.ConvTranspose2d(16, 16, 2, stride=2),
            nn.ReLU(),
            nn.Conv2d(16, classes, 1)
        )

    def forward(self, x):
        h = self.enc(x)
        z = self.pool(h)
        return self.dec(z)

model = TinyUNet(classes=4)
x = torch.randn(2, 3, 128, 128)
y = torch.randint(0, 4, (2, 128, 128))

logits = model(x)
loss = nn.CrossEntropyLoss()(logits, y)
print(logits.shape, loss.item())

Resumo final

Deep learning e a composicao de funcoes parametrizadas. Redes densas aprendem relacoes globais; CNNs exploram localidade e compartilhamento de pesos; treinamento ajusta parametros via gradientes; otimizacao busca reduzir a loss; generalizacao exige controlar overfitting; segmentacao leva classificacao do nivel da imagem para o nivel do pixel.