RoPE, RMSNorm e SwiGLU escritas ao lado das peças de 2019 que elas substituem — e o duelo com mesmo corpus, mesma verba e mesma semente.
Uma peça nova é apresentada nas condições em que foi desenvolvida: outra escala, outros dados, outro orçamento. Um ganho medido lá não se transfere automaticamente para cá. Por isso este capítulo refaz a comparação nas nossas condições — mesmos dados, mesma semente, mesmo número de passos —, sabendo que a comparação justa é trabalhosa e sai cara.
As armadilhas são três, e todas foram evitadas aqui de propósito:
| Armadilha | O que acontece | Nossa trava |
|---|---|---|
| Implementações diferentes | a peça nova vem de um repositório otimizado, a antiga de um esboço | as duas no mesmo arquivo, mesmo laço de treino, escolhidas por Config(moderno=…) |
| Contagem de parâmetros diferente | a nova ganha porque é maior | 17,1 M (fiel) contra 16,9 M (moderna) no tamanho pequeno |
| Orçamento ou dados diferentes | a nova viu mais | mesmo treino.bin, mesmos 700 passos, mesma semente 1337 |
Uma comparação só vale se a única coisa diferente for aquilo que está sendo testado. Por isso o arquivo de dados do duelo foi congelado em model/data/duelo_200mb/ e o script de empacotamento agora recusa sobrescrever sem uma confirmação explícita — um corpus trocado no meio de uma série invalidaria tudo em silêncio.
A entrevista (apostila 10) responde a uma pergunta que este duelo levanta: a JEPA não é uma alternativa ao Transformer — é uma macro-arquitetura, e o Transformer é o encoder dentro dela. As duas variantes deste arquivo, clássica e moderna, são o mesmo módulo com peças diferentes; o preditor mundo e o custo no latente ficam por cima, iguais para as duas. O duelo abaixo escolhe o encoder; não escolhe o paradigma.
wpe aprendida → RoPEO GPT-2 tem uma tabela wpe com exatamente 1024 linhas — uma por posição possível. É um vetor aprendido, somado ao embedding do token. Funciona, e tem duas consequências duras.
A primeira é a parede. Medimos ela diretamente:
contexto 512 → 29,6 ms/token contexto 1000 → 34,6 ms/token contexto 1024 → PAREDE: posição 1024 não existe na wpe (ela tem 1024 linhas: 0 a 1023)
Não é um limite de configuração. É uma matriz que acaba. Para estender o contexto, é preciso criar linhas novas — que nunca foram treinadas.
O RoPE (Su et al., 2021) resolve por construção: em vez de somar um vetor de posição, ele gira os vetores de consulta e chave por um ângulo proporcional à posição. Como o produto escalar entre dois vetores girados depende só da diferença dos ângulos, a atenção passa a enxergar distância relativa — e não há tabela para acabar.
A wpe é dar a cada pessoa da fila um crachá com o número dela. Se a fila passa de 1024, acabaram os crachás.
O RoPE é girar cada pessoa por um ângulo proporcional à posição. Duas pessoas separadas por três lugares têm sempre a mesma diferença de ângulo, esteja a dupla no começo ou no fim da fila — e a fila pode ser tão longa quanto se queira.
Custo em parâmetros: a wpe do mini tem 1024 × 256 = 262 144 pesos. O RoPE tem zero — é uma rotação calculada, não aprendida.
LayerNorm faz duas coisas: centra (subtrai a média) e escala (divide pelo desvio). O RMSNorm (Zhang & Sennrich, 2019) observou que a centragem quase não contribui, e removeu:
Some a subtração da média e some o parâmetro de deslocamento
O bloco do GPT-2 expande para 4
class SwiGLU(nn.Module): def forward(self, x): return self.w_down(F.silu(self.w_portao(x)) * self.w_cima(x))
São três matrizes em vez de duas, então a dimensão interna encolhe (de 4
Llama removeu o termo
Tamanho mini (7,4 M), 700 passos, 5,7 M de tokens, mesma semente. Só a arquitetura muda.
| Arquitetura | Validação | PPL | no passo |
|---|---|---|---|
| clássica (GPT-2 fiel) | 5,5391 | 254,45 | 649 |
| moderna | 5,2616 | 192,79 | 549 |
−24% de perplexidade, e com 100 passos a menos para chegar ao melhor ponto. Nas quatro medidas de bits/caractere:
| Arquitetura | Português | Inglês | Código | Média |
|---|---|---|---|---|
| clássica | 2,439 | 2,066 | 2,522 | 2,342 |
| moderna | 2,406 | 2,038 | 2,405 | 2,283 |
Uma rodada por arquitetura, uma semente, um tamanho, 5,7 M de tokens — 0,09 época do corpus. Isso mostra que a arquitetura moderna é melhor nesta escala, com este orçamento. Não mostra qual peça contribuiu: RoPE, RMSNorm, SwiGLU e os vieses mudaram todos juntos.
Isolar cada uma exigiria quatro rodadas extras. Está na lista, não no resultado.
Durante o duelo, a validação do modelo clássico subiu entre os passos 649 e 699 (5,539 → 5,577). A leitura imediata foi "começou a sobreajustar". Estava errada.
Com 5,7 M de tokens num corpus de 60,3 M, o modelo viu 0,09 de uma época — nenhum token foi visto duas vezes. Sobreajuste é impossível por construção. O que subiu foi ruído de avaliação somado ao fechamento do cosseno da taxa de aprendizado.
| Peça | Clássica | Moderna |
|---|---|---|
| Posição | wpe: 1024×384 = 393 216 | RoPE: 0 |
| Normalização | LayerNorm: ganho + viés | RMSNorm: só ganho |
| MLP | 2 matrizes, GELU | 3 matrizes, SwiGLU (dim ⅔) |
| Lineares | com viés | sem viés |
| Total | 17,1 M | 16,9 M |
A moderna é ligeiramente menor e ainda ganha. É o argumento mais forte da tabela: não é mais capacidade, é melhor uso da mesma.
Tudo em model/architecture.py, com cada peça moderna escrita imediatamente ao lado da clássica que substitui:
def normalizador(cfg, d): return RMSNorm(d) if cfg.moderno else nn.LayerNorm(d) # no Transformer: if cfg.moderno: self.wpe = None # RoPE entra na atenção rope = RoPE(cfg.d_k, cfg.n_ctx, cfg.rope_base) else: self.wpe = nn.Embedding(cfg.n_ctx, cfg.d_model) rope = None
Ler esse arquivo de cima a baixo é ver a diferença entre 2019 e hoje em ~400 linhas. Era esse o objetivo — mais do que ter as duas opções, ter as duas legíveis lado a lado.
A arquitetura resolvida, a apostila 03 olha para dentro da tabela de embedding —
e encontra um problema que nenhuma dessas peças modernas conserta.