Laboratório GPT-2 · RoBERTa · Parte I — A fundação
05

O bloco Transformer
completo

LayerNorm, GELU, MLP e conexão residual — as quatro peças que permitem empilhar 48 camadas sem o treino explodir.

Apostila 05 de 15 Pré-requisito: apostilas 03 e 04

1 O que falta na atenção

A atenção resolve o transporte de informação: ela move conteúdo de uma posição para outra. Mas repare no que ela é, matematicamente: uma média ponderada de vetores V. Isto é, uma combinação linear.

E aí está o problema. Empilhar operações lineares não adianta nada: a composição de duas transformações lineares é outra transformação linear. Cem camadas de atenção pura teriam exatamente a mesma expressividade de uma camada só. Você não ganharia nada empilhando.

Precisamos de não-linearidade. E precisamos de um lugar onde o modelo possa processar a informação que a atenção trouxe, em vez de só transportá-la. Esse lugar é o MLP.

A divisão de trabalho do Transformer

Atenção = comunicação. Os tokens conversam entre si. É onde a informação viaja entre posições.

MLP = computação. Cada token pensa sozinho, em silêncio. Nenhuma informação atravessa posições aqui — o MLP processa cada token independentemente dos outros.

Um bloco Transformer é uma rodada de conversa seguida de uma rodada de reflexão. Doze blocos = doze rodadas. É uma das descrições mais úteis que existem, e vale memorizar.

2 Peça 1 — o MLP (onde mora o conhecimento)

O MLP é surpreendentemente simples: duas camadas lineares com uma não-linearidade no meio.

MLP(x) = W2 · GELU(W1x + b1) + b2 W1: 768 → 3072 (expande 4×). W2: 3072 → 768 (contrai de volta). O fator 4 é convenção desde o artigo original de 2017 e continua comum.

A forma é característica: expande, processa, contrai. Por que expandir para 3072 antes de voltar? Porque o espaço largo dá room para o modelo separar padrões que estão emaranhados em 768 dimensões — é o mesmo princípio de um kernel em SVM. A camada larga é onde os conceitos ficam linearmente separáveis.

A conta que assusta

O MLP tem duas vezes mais parâmetros que a atenção em cada bloco:

Parâmetros por bloco no GPT-2 small (d=768). O MLP domina.
ComponenteCálculoParâmetros%
Atenção — c_attn (QKV)768 × 23041 769 47225%
Atenção — c_proj (WO)768 × 768589 8248%
MLP — c_fc (expande)768 × 30722 359 29633%
MLP — c_proj (contrai)3072 × 7682 359 29633%
Total por bloco≈ 7 079 000100%
Onde ficam os fatos

Existe uma linha de pesquisa forte (Geva et al., 2021) mostrando que os MLPs funcionam como memórias chave-valor. A primeira camada (c_fc) detecta padrões — "o contexto atual fala sobre a capital de um país europeu" — e a segunda (c_proj) escreve a informação correspondente de volta no fluxo residual: "então empurre a probabilidade de Paris".

Isso tem uma consequência prática: quando alguém diz que "o conhecimento factual de um LLM está nos pesos", os pesos em questão são, majoritariamente, os MLPs, não a atenção. Técnicas de edição de fatos em modelos (ROME, MEMIT) operam exatamente aí.

A não-linearidade: GELU, não ReLU

O GPT-2 usa GELU (Gaussian Error Linear Unit). A diferença para a ReLU clássica importa:

ReLU(x) = max(0, x)      GELU(x) = x · Φ(x) Φ(x) é a função de distribuição acumulada da normal padrão — a probabilidade de uma amostra normal ser menor que x.

Leia a GELU como uma porta probabilística: em vez de cortar bruscamente em zero, ela multiplica a entrada pela probabilidade dela "merecer passar". Valores muito negativos são quase zerados, valores muito positivos passam quase intactos, e no meio há uma transição suave.

Duas vantagens concretas sobre a ReLU:

O GPT-2 usa, na prática, uma aproximação por tangente hiperbólica da GELU — mais barata de calcular e numericamente idêntica para os fins do modelo.

3 Peça 2 — a conexão residual (a mais importante)

Se você só puder guardar uma coisa desta apostila, guarde esta. A conexão residual é o que torna redes profundas treináveis, e ela é ridiculamente simples:

xsaída = xentrada + Camada(xentrada) Some a entrada de volta na saída. Só isso.

Por que isso salva o treino

Numa rede profunda sem residual, o gradiente precisa atravessar todas as camadas por multiplicação sucessiva. Se cada camada multiplica o gradiente por, digamos, 0,9, então depois de 48 camadas sobra 0,948 ≈ 0,006 — o gradiente desapareceu. As primeiras camadas nunca aprendem. Esse é o problema clássico do vanishing gradient, que travou o aprendizado profundo por anos.

Com a residual, a derivada da soma é:

∂xsaída∂xentrada = I + ∂Camada∂x Aquele I (a matriz identidade) é uma estrada expressa. Mesmo que o termo da camada seja minúsculo, o gradiente passa inteiro pelo caminho da identidade. Ele nunca desaparece.

A leitura conceitual: o fluxo residual

Há um jeito melhor de enxergar isso, e ele mudou a forma como as pessoas pensam sobre Transformers. Em vez de "camadas que transformam", pense num quadro-negro compartilhado de 768 dimensões que atravessa o modelo inteiro do começo ao fim.

Esse quadro é chamado de residual stream. É o conceito central da interpretabilidade moderna de Transformers, e explica por que dá para "ler" o que o modelo está pensando nas camadas intermediárias — a informação está literalmente acumulada ali, disponível.

4 Peça 3 — LayerNorm (e a mudança do GPT-1 para o GPT-2)

Conforme os vetores atravessam dezenas de blocos somando contribuições, suas magnitudes tendem a crescer descontroladamente. O LayerNorm renormaliza:

LN(x) = γ ⊙ x − μ√σ² + ε + β μ e σ² são a média e a variância das 768 dimensões daquele token — calculadas por token, não pelo lote. γ e β são vetores aprendidos que devolvem ao modelo a liberdade de reescalar. ε ≈ 10−5 evita divisão por zero.
LayerNorm vs BatchNorm

O BatchNorm normaliza cada dimensão ao longo do lote. Isso não funciona para linguagem: os textos de um lote têm comprimentos diferentes, e o resultado de um exemplo passaria a depender dos outros exemplos que por acaso estavam no mesmo lote — o que quebra na inferência, quando você processa um texto só. O LayerNorm normaliza dentro de cada token, independentemente. É por isso que NLP usa LayerNorm e visão usa BatchNorm.

A mudança que define o GPT-2: Pre-LN

Esta é a alteração arquitetural mais importante entre GPT-1 e GPT-2, e ela é só uma questão de onde colocar o LayerNorm.

Post-LN (GPT-1, BERT, 2018)Pre-LN (GPT-2 em diante)
x = LN(x + Atenção(x))
x = LN(x + MLP(x))
x = x + Atenção(LN(x))
x = x + MLP(LN(x))
O LayerNorm fica dentro do caminho residual. O gradiente é renormalizado a cada camada. O LayerNorm fica no ramo. O caminho residual vira uma estrada totalmente limpa do fim ao começo.
Precisa de warmup cuidadoso na taxa de aprendizado, e fica instável acima de ~20 camadas. Treina de forma estável com 48+ camadas, dispensa warmup agressivo.

Essa mudança sozinha é o que permitiu o GPT-2 XL ter 48 camadas. Com Post-LN, treinar 48 camadas seria uma batalha de estabilidade numérica. Depois dele, a normalização antes da atenção virou a escolha padrão dos Transformers grandes, muitas vezes na forma do RMSNorm.

O GPT-2 também acrescenta um LayerNorm final (chamado ln_f) depois do último bloco, antes de projetar para os logits — necessário justamente porque, com Pre-LN, a saída do último bloco nunca passou por uma normalização.

5 O bloco montado

class Bloco(nn.Module):
    def __init__(self, d=768, h=12):
        super().__init__()
        self.ln_1 = nn.LayerNorm(d)
        self.attn = AtencaoMultiCabeca(d, h, causal=True)
        self.ln_2 = nn.LayerNorm(d)
        self.mlp  = nn.Sequential(
            nn.Linear(d, 4 * d),   # c_fc  : expande 768 -> 3072
            nn.GELU(),
            nn.Linear(4 * d, d),   # c_proj: contrai 3072 -> 768
        )

    def forward(self, x):
        # comunicacao: os tokens conversam
        x = x + self.attn(self.ln_1(x))
        # computacao: cada token pensa sozinho
        x = x + self.mlp(self.ln_2(x))
        return x

Duas linhas no forward. Repare que x = x + … nas duas: o x original nunca é destruído, só acrescido. É o quadro-negro sendo preenchido.

O fluxo dentro de um bloco (5 tokens, GPT-2 small):

x            (5, 768) ---------------------------+  guarda a copia
  ln_1       (5, 768)                            |
  attn       (5, 768)                            |
                                                 v
x = x + attn(ln_1(x))                        (5, 768)  <- soma
  |
  +----------------------------------------------+
  ln_2       (5, 768)                            |
  c_fc       (5, 3072)  <- expande 4x            |
  GELU       (5, 3072)  <- nao-linearidade       |
  c_proj     (5, 768)   <- contrai               |
                                                 v
x = x + mlp(ln_2(x))                         (5, 768)  <- soma

FORMA DE ENTRADA = FORMA DE SAIDA. Por isso da para empilhar
12, 24, 36 ou 48 desses sem mudar mais nada.

6 No laboratório

.venv\Scripts\python.exe lab\05_block.py
  1. Monta um bloco do zero e confirma que a forma entra e sai igual.
  2. Compara Pre-LN e Post-LN numa pilha de 40 camadas, medindo a norma do gradiente que chega na primeira camada. O Post-LN some; o Pre-LN sobrevive. É a demonstração do porquê da mudança.
  3. Mede o crescimento do fluxo residual camada a camada no GPT-2 real. Você vai ver a norma crescer de forma quase monotônica — o quadro-negro sendo preenchido.
  4. Lê o quadro-negro no meio do caminho — a técnica chamada logit lens. Veja a caixa abaixo: este é o experimento mais surpreendente da apostila.
  5. Contabiliza os parâmetros de um bloco real, peça por peça, confirmando que o MLP leva dois terços.
  6. Desenha a GELU contra a ReLU em ASCII, para você ver a diferença na região crítica perto de zero.
O que o logit lens revela — e é inesperado

Se a informação se acumula no fluxo residual, dá para espiar o que o modelo "está pensando" em cada camada: basta aplicar o ln_f e a matriz de saída num estado intermediário. Rodando isso no gpt2 com o prompt "The capital of France is", aparece o seguinte:

Esse último passo é o achado. O gpt2 small sabia a resposta no bloco 10 e a perdeu no caminho — o que bate exatamente com o que ele de fato gera: "The capital of France is the capital of the French Republic…". O gpt2-medium, no mesmo teste, acerta.

A lição: a diferença entre um modelo que "sabe" e um que "responde" não está apenas em ter a informação armazenada. Está em conseguir sustentá-la até a saída. É boa parte do que a escala compra — e você vai medir isso na apostila 06.

Exercício 5 — preveja antes de rodar

  1. Se o fator de expansão do MLP fosse 2 em vez de 4, quantos parâmetros o GPT-2 small teria a menos? (Cada bloco tem 2 × 768 × 4 × 768 parâmetros de MLP; são 12 blocos.)
  2. O MLP processa cada token independentemente. Isso significa que ele poderia ser calculado antes da atenção, em paralelo? O que se perderia?
  3. Por que o GPT-2 precisa do ln_f no final e o GPT-1 não precisa?
  4. Difícil: se a conexão residual é x + f(x), o que acontece se f aprender a devolver sempre zero? Isso é um problema ou uma característica desejável? (Dica: pense no que significa uma camada "desistir" de contribuir.)
Referências

As fontes primárias dos conceitos deste capítulo. Os números do texto foram medidos no laboratório; as ideias vêm daqui.

Os termos técnicos têm aqui o sentido de aprendizado de máquina. O glossário do curso mostra esse sentido ao lado dos outros que as mesmas palavras têm na TI.

  1. He, K., Zhang, X., Ren, S. e Sun, J. (2016). Deep Residual Learning for Image Recognition. CVPR 2016. arXiv:1512.03385
  2. Ba, J. L., Kiros, J. R. e Hinton, G. E. (2016). Layer Normalization. Preprint. arXiv:1607.06450
  3. Ioffe, S. e Szegedy, C. (2015). Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. ICML 2015. arXiv:1502.03167
  4. Hendrycks, D. e Gimpel, K. (2016). Gaussian Error Linear Units (GELUs). Preprint. arXiv:1606.08415
  5. Geva, M., Schuster, R., Berant, J. e Levy, O. (2021). Transformer Feed-Forward Layers Are Key-Value Memories. EMNLP 2021. arXiv:2012.14913

Você agora conhece todas as peças. A apostila 06 as monta no GPT-2 completo
e mostra o que realmente muda quando se vai de 124 milhões para 1,5 bilhão de parâmetros.