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, loss

10.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.

TipTeste de causalidade

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.

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

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.

ImportantConfiguração é parte do checkpoint

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.