Ir para o conteúdo

13. Transformers

Transformers

Em 2017, o artigo "Attention Is All You Need"1 eliminou recorrência e convoluções dos modelos de sequência, substituindo-os inteiramente por atenção. O resultado foi o Transformer — a arquitetura que impulsiona GPT, BERT, ViT, Whisper e virtualmente todo modelo de estado da arte hoje.


Visão Geral da Arquitetura

O Transformer original é um modelo encoder-decoder para tradução automática. Hoje existem variantes apenas-encoder (BERT, para classificação/representação) e apenas-decoder (GPT, para geração).


O Bloco Encoder em Detalhe

Cada um dos \(N\) blocos encoder idênticos consiste em:

\[ \text{SubCamada}_1(x) = \text{LayerNorm}(x + \text{MultiHead}(x, x, x)) \]
\[ \text{SubCamada}_2(x) = \text{LayerNorm}(x + \text{FFN}(x)) \]

A FFN (Feed-Forward Network) é aplicada independentemente a cada posição:

\[ \text{FFN}(x) = \max(0,\; xW_1 + b_1)W_2 + b_2 \]

Tipicamente \(d_{\text{model}} = 512\), \(d_{\text{ff}} = 2048\) — um gargalo 4× mais largo que o embedding.

Conexões residuais (inspiradas em ResNets) garantem que gradientes fluam diretamente por muitas camadas. Layer Normalization normaliza ao longo da dimensão de features (não do batch), o que é mais estável para sequências de comprimento variável.


Decoder e Geração Autorregressiva

O decoder gera tokens um de cada vez, condicionado em tudo o que foi gerado até o momento:

\[ p(\text{saída}) = \prod_{t=1}^{T} p(y_t \mid y_{<t},\; \text{saída do encoder}) \]

Para impedir que o token \(t\) veja tokens futuros durante o treinamento, a self-attention mascarada aplica uma máscara causal:

Máscara causal (n=4 tokens):
     pos0  pos1  pos2  pos3
pos0 [ 0   -inf  -inf  -inf ]   (vê apenas a si mesmo)
pos1 [ 0    0   -inf  -inf ]    (vê pos0 e pos1)
pos2 [ 0    0    0   -inf ]
pos3 [ 0    0    0    0   ]     (vê tudo)

BERT vs. GPT — Encoder vs. Decoder

BERT (Apenas Encoder)

  • Bidirecional: vê o contexto esquerdo e direito
  • Pré-treinado com Masked Language Model
  • Excelente para classificação, NER, QA
  • Não é generativo
GPT (Apenas Decoder)

  • Causal: vê apenas o contexto à esquerda
  • Pré-treinado com Predição do Próximo Token
  • Excelente para geração de texto, chat, código
  • A escala leva a capacidades emergentes

Vision Transformer (ViT)

Em 2020, Dosovitskiy et al.3 aplicaram Transformers a imagens dividindo-as em patches de \(16 \times 16\) pixels, linearizando cada patch como um token:

\[ \text{Entrada} \in \mathbb{R}^{H \times W \times C} \xrightarrow{\text{patches}} \mathbb{R}^{N \times (P^2 C)} \xrightarrow{\text{linear}} \mathbb{R}^{N \times d} \]

onde \(N = HW/P^2\) é o número de patches. Um token especial [CLS] é adicionado ao início e sua saída final é usada para classificação.

O ViT superou CNNs no ImageNet com escala de dados suficiente — mostrando que vieses indutivos convolucionais (localidade, equivariância à translação) não são necessários quando dados suficientes estão disponíveis.


Leis de Escala e Modelos de Linguagem de Grande Escala

Kaplan et al. (2020)4 descobriram que a perda de modelos de linguagem segue leis de escala em lei de potência:

\[ L(N, D) = \left(\frac{N_c}{N}\right)^{\alpha_N} + \left(\frac{D_c}{D}\right)^{\alpha_D} + L_\infty \]

onde \(N\) é o número de parâmetros, \(D\) o tamanho dos dados e \(L_\infty\) a perda irredutível.

Isso levou ao paradigma dos LLMs: treinar modelos enormes (bilhões de parâmetros) em trilhões de tokens. A próxima aula explora esse mundo.


Referência Rápida de Implementação

import torch
import torch.nn as nn

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.d_k = d_model // num_heads
        self.W_q = nn.Linear(d_model, d_model)
        self.W_k = nn.Linear(d_model, d_model)
        self.W_v = nn.Linear(d_model, d_model)
        self.W_o = nn.Linear(d_model, d_model)

    def forward(self, q, k, v, mask=None):
        B, n, _ = q.shape
        Q = self.W_q(q).view(B, n, self.num_heads, self.d_k).transpose(1,2)
        K = self.W_k(k).view(B, -1, self.num_heads, self.d_k).transpose(1,2)
        V = self.W_v(v).view(B, -1, self.num_heads, self.d_k).transpose(1,2)

        scores = Q @ K.transpose(-2,-1) / self.d_k**0.5
        if mask is not None:
            scores = scores.masked_fill(mask==0, float('-inf'))
        attn = scores.softmax(dim=-1)
        out = (attn @ V).transpose(1,2).reshape(B, n, -1)
        return self.W_o(out)
class TransformerBlock(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super().__init__()
        self.attn = MultiHeadAttention(d_model, num_heads)
        self.ff = nn.Sequential(
            nn.Linear(d_model, d_ff), nn.GELU(),
            nn.Linear(d_ff, d_model)
        )
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.drop = nn.Dropout(dropout)

    def forward(self, x, mask=None):
        x = self.norm1(x + self.drop(self.attn(x, x, x, mask)))
        x = self.norm2(x + self.drop(self.ff(x)))
        return x



  1. Vaswani, A. et al. (2017). Attention Is All You Need. NeurIPS. 

  2. Devlin, J. et al. (2019). BERT: Pre-training of Deep Bidirectional Transformers

  3. Dosovitskiy, A. et al. (2020). An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale

  4. Kaplan, J. et al. (2020). Scaling Laws for Neural Language Models