LayerNorm, GELU, MLP e conexão residual — as quatro peças que permitem empilhar 48 camadas sem o treino explodir.
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
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.
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.
O MLP é surpreendentemente simples: duas camadas lineares com uma não-linearidade no meio.
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.
O MLP tem duas vezes mais parâmetros que a atenção em cada bloco:
| Componente | Cálculo | Parâmetros | % |
|---|---|---|---|
Atenção — c_attn (QKV) | 768 × 2304 | 1 769 472 | 25% |
Atenção — c_proj ( | 768 × 768 | 589 824 | 8% |
MLP — c_fc (expande) | 768 × 3072 | 2 359 296 | 33% |
MLP — c_proj (contrai) | 3072 × 768 | 2 359 296 | 33% |
| Total por bloco | ≈ 7 079 000 | 100% | |
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í.
O GPT-2 usa GELU (Gaussian Error Linear Unit). A diferença para a ReLU clássica importa:
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.
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:
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 é:
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.
Conforme os vetores atravessam dezenas de blocos somando contribuições, suas magnitudes tendem a crescer descontroladamente. O LayerNorm renormaliza:
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.
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.
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.
.venv\Scripts\python.exe lab\05_block.py
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:
' destro', ' mathemat'). Nenhum contexto foi processado ainda.' now', ' not', ' also'. Ele resolveu a gramática ("depois de is costuma vir um advérbio") antes de qualquer conteúdo. Sintaxe primeiro, semântica depois.' France'=0,62 e ' Paris'=0,18 surgem do nada. O conhecimento factual entra aqui — nos MLPs das camadas intermediárias, exatamente onde a pesquisa de interpretabilidade diz que os fatos moram.' the'. O modelo desistiu do Paris.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.
ln_f no final e o GPT-1 não precisa?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.)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.
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.