flowchart LR
X[Estado do token] --> R[Roteador]
R --> E1[Especialista 1]
R --> E2[Especialista 2]
R -. não selecionado .-> E3[Especialista 3]
E1 --> S[Soma ponderada]
E2 --> S
10 Montando o Modelo Completo (Estilo GPT)
Integração de todos os componentes desenvolvidos anteriormente para criar uma arquitetura Decoder-Only funcional pronta para gerar texto.
Este capítulo integra um Transformer somente decodificador, paradigma usado por modelos da família GPT e Llama. Diferentemente do Transformer codificador-decodificador original, essa organização é voltada à modelagem autoregressiva: prever cada próximo token apenas com o contexto anterior.
10.1 1. Arquitetura em Alto Nível
A arquitetura contém embeddings aprendidos, uma pilha de blocos decodificadores e uma projeção final que mapeia as representações internas de volta ao espaço do vocabulário.
10.1.1 Diagrama da Arquitetura
O diagrama mostra o caminho dos IDs de tokens até os logits. Observe a normalização anterior às subcamadas (pre-norm), comum em implementações modernas por favorecer a estabilidade de redes profundas.
graph TD
subgraph Inputs
I[Input Token IDs] --> TE[Token Embeddings]
P[Position IDs] --> PE[Positional Embeddings]
end
TE & PE --> Sum((+))
Sum --> Drop1[Dropout]
subgraph "Transformer Block (Repeated N times)"
Drop1 --> LN1[Layer Norm 1]
LN1 --> MSA[Masked Multi-Head Attention]
MSA --> Res1((+))
Drop1 --> Res1
Res1 --> LN2[Layer Norm 2]
LN2 --> MLP[Feed-Forward Network<br/>(GELU Activation)]
MLP --> Res2((+))
Res1 --> Res2
end
Res2 --> LNF[Final Layer Norm]
LNF --> Head[Linear Head<br/>(Project to Vocab Size)]
Head --> Logits[Logits]
Logits --> Soft[Softmax]
Soft --> Prob[Next Token Probabilities]
style Inputs fill:#f9f,stroke:#333,stroke-width:2px
style MSA fill:#bbf,stroke:#333,stroke-width:2px
style MLP fill:#bbf,stroke:#333,stroke-width:2px
style Head fill:#bfb,stroke:#333,stroke-width:2px
10.2 2. Integração dos Componentes
To assemble the model, we integrate the components defined in previous chapters. The architecture is defined by hyperparameters: \(d_{model}\) (embedding dimension), \(N_{layers}\) (depth), \(N_{heads}\) (attention heads), and \(V\) (vocabulary size).
10.2.1 A. Camada de Embeddings
É a entrada do modelo: converte índices discretos em vetores densos e incorpora informação de posição. * Token Embeddings: A lookup table of size \((V, d_{model})\). * Positional Embeddings: A lookup table of size \((ContextLen, d_{model})\). In GPT, these are typically learned parameters rather than fixed sinusoidal functions. * Combination: The two embeddings are summed element-wise.
10.2.2 B. Bloco Decodificador
A maior parte da computação acontece aqui. Cada bloco contém normalização, autoatenção com máscara causal, uma rede feed-forward e duas conexões residuais. A máscara garante que a posição \(t\) consulte apenas posições até \(t\), preservando o objetivo autoregressivo.
10.2.3 C. Cabeça de Saída
After passing through \(N\) blocks: 1. Normalização final: estabiliza os estados ocultos finais. 2. Projeção linear: mapeia \(d_{model}\) para \(V\), o tamanho do vocabulário. 3. Compartilhamento de pesos: opcionalmente, a projeção usa a mesma matriz dos embeddings de entrada, reduzindo parâmetros.
10.3 3. Lógica da Implementação
Below is a structural representation of the complete model using a PyTorch-like class structure. This demonstrates how the components interact in code.
import torch
import torch.nn as nn
from torch.nn import functional as F
class GPT(nn.Module):
def __init__(self, config):
super().__init__()
self.config = config
# 1. Embeddings
self.token_embedding = nn.Embedding(config.vocab_size, config.n_embd)
self.position_embedding = nn.Embedding(config.block_size, config.n_embd)
self.drop = nn.Dropout(config.dropout)
# 2. Stack of Decoder Blocks
# Assuming 'Block' is a class defined in previous chapters
self.blocks = nn.ModuleList([
Block(config) for _ in range(config.n_layer)
])
# 3. Final Normalization
self.ln_f = nn.LayerNorm(config.n_embd)
# 4. Output Head
self.head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
# Weight tying: embedding weights == output head weights
self.token_embedding.weight = self.head.weight
self.apply(self._init_weights)
def _init_weights(self, module):
""" Initialize weights (typically normal distribution with small std) """
if isinstance(module, nn.Linear):
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
if module.bias is not None:
torch.nn.init.zeros_(module.bias)
elif isinstance(module, nn.Embedding):
torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
def forward(self, idx, targets=None):
# idx shape: (Batch, Seq_Len)
B, T = idx.shape
# Create position indices: [0, 1, 2, ..., T-1]
pos = torch.arange(0, T, dtype=torch.long, device=idx.device)
# Forward pass through embeddings
tok_emb = self.token_embedding(idx) # (B, T, n_embd)
pos_emb = self.position_embedding(pos) # (T, n_embd)
x = self.drop(tok_emb + pos_emb)
# Forward pass through transformer blocks
for block in self.blocks:
x = block(x)
# Final Norm
x = self.ln_f(x)
# Project to vocabulary
logits = self.head(x) # (B, T, vocab_size)
loss = None
if targets is not None:
# Flatten for CrossEntropyLoss
# logits: (B*T, vocab_size), targets: (B*T)
loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1))
return logits, loss10.4 4. Forward de Treino versus Geração
It is crucial to distinguish how this model operates during training versus generation.
10.4.1 Treinamento em paralelo
No treinamento, a sequência correta inteira está disponível. Para entrada [A, B, C], o alvo é [B, C, D]. A máscara causal permite calcular simultaneamente as previsões de B dado A, de C dado AB e de D dado ABC, sem revelar o futuro. A entropia cruzada é calculada em todas as posições válidas.
10.4.2 Inferência sequencial
Na geração, o futuro é desconhecido. Partimos de [A], selecionamos B a partir dos logits, anexamos o token e repetimos com [A,B]. O ciclo termina ao gerar <EOS> ou atingir o limite configurado. Um cache de chaves e valores evita recalcular toda a atenção do prefixo em cada passo.
10.5 5. Resumo da Integração
O modelo no estilo GPT combina embeddings, blocos causais e uma cabeça de vocabulário em um caminho inteiramente diferenciável. Durante o treino, aprende regularidades úteis para prever o próximo token; durante a inferência, aplica essa mesma função repetidamente para produzir uma continuação.
Altere o último token de uma sequência e execute o modelo em modo de avaliação. Os logits das posições anteriores devem permanecer iguais, salvo pequenas diferenças numéricas. Se mudarem, a máscara está permitindo vazamento do futuro.
10.6 Modelos densos e mistura de especialistas
No Transformer denso, todos os tokens atravessam a mesma FFN de cada bloco. Uma camada de mistura de especialistas (Mixture of Experts, MoE) contém várias FFNs e um roteador que escolhe um pequeno subconjunto para cada token. Assim, o número total de parâmetros pode crescer sem que todos sejam ativados em cada passagem.
O roteador produz escores e seleciona, por exemplo, os dois especialistas mais relevantes. A saída combina esses resultados com pesos normalizados. O modelo continua diferenciável nas rotas selecionadas, mas o treinamento precisa de termos auxiliares para evitar que quase todos os tokens sejam enviados aos mesmos especialistas.
MoE separa parâmetros disponíveis de parâmetros ativos por token. Isso pode aumentar capacidade mantendo custo de cálculo controlado, porém adiciona comunicação entre dispositivos, balanceamento de carga e limites de capacidade por especialista. Um modelo com mais parâmetros totais não é automaticamente mais lento ou melhor; a comparação deve considerar parâmetros ativos, memória e arquitetura.
10.7 Contabilidade do caminho completo
Para IDs com forma \((B,T)\), os embeddings produzem \((B,T,d)\). A pilha conserva essa forma, a normalização final também, e a cabeça de linguagem gera \((B,T,V)\). No treino, todas as posições contribuem em paralelo. Na geração, normalmente só os logits da última posição decidem o novo token.
Número de camadas, dimensão, cabeças, grupos KV, vocabulário, tipo de posição e normalização devem acompanhar os pesos. Matrizes com formas compatíveis ainda podem representar arquiteturas semanticamente incompatíveis.