Avançado

Gradient checkpointing e a matemática da memória

O activation checkpointing troca compute por memória — e com hidden size 5120 e uma FFN de 17.408 de largura, as ativações de uma única sequência de 8.192 tokens fazem tudo que o sharding conquistou parecer pequeno.

Atualizada em

01 · Conceito

Conceito

Suponha que o sharding já tenha reduzido o estado persistente por rank sob uma receita de optimizer explicitamente escolhida. No papel o job cabe. Então uma sequência de 8.192 tokens com batch size um morre no primeiro backward pass. Nada na aritmética do sharding estava necessariamente errado; outra categoria — ativações salvas — dominou o pico.

A backpropagation precisa de valores do forward pass. O autograd portanto retém ativações, cópias normalizadas, saídas de projeção e intermediários de attention até que suas funções de backward rodem, e num transformer profundo esses valores salvos rotineiramente excedem os pesos. O activation checkpointing — normalmente chamado de gradient checkpointing, embora nada nele envolva um arquivo em disco — reduz isso esquecendo e recomputando deliberadamente.

Acerte o tamanho primeiro, porque a intuição que as pessoas carregam está muito errada. A estimativa tentadora conta o residual stream: batch vezes sequência vezes hidden vezes camadas vezes bytes do dtype.

1×8192×5120×64×2 B=5.37×109 B5 GiB.1\times 8192\times 5120\times 64\times 2\ \text{B}=5.37\times 10^{9}\ \text{B}\approx 5\ \text{GiB}.

Cinco gibibytes. Confortável. Agora conte o que o autograd de fato guarda numa camada. Tome um modelo uniformemente de full attention com as dimensões deste — a conta real do híbrido exige custear as camadas DeltaNet à parte, o que é um exercício próprio; o que vem a seguir é o método e a ordem de grandeza. A entrada residual e sua cópia normalizada têm 5120 de largura cada. Numa camada de full attention as projeções não são quadradas: a projeção de query vai de 5120 para 12288, porque carrega a porta de saída junto com os 24 heads de dimensão 256 (lição 4.2); keys e values vão de 5120 para 1024 cada (4 KV heads), a saída da attention é 6144, e a projeção de saída retorna a 5120. Isso dá aproximadamente 35.800 valores por token. O bloco feed-forward é ainda maior: uma entrada normalizada de 5120 de largura, depois os tensores gate, up e do produto com gate, de largura 17.408 cada, depois uma saída de down-projection de 5120 — cerca de 62.500 valores. Somando:

35,800+62,50098,300 valores por token por camada,35{,}800+62{,}500\approx 98{,}300\ \text{valores por token por camada}, 98,300×2 B196,600 B192 KiB por token por camada.98{,}300\times 2\ \text{B}\approx 196{,}600\ \text{B}\approx 192\ \text{KiB por token por camada}.

Isso é 19 vezes o residual stream sozinho, e os três tensores de 17.408 de largura da FFN são a maior parte da diferença. Escale:

8192 tokens×64 camadas×192 KiB96 GiB.8192\ \text{tokens}\times 64\ \text{camadas}\times 192\ \text{KiB}\approx 96\ \text{GiB}.

Noventa e seis gibibytes de ativações salvas para uma sequência com batch size um — mais de dez vezes o estado persistente que seu sharding tanto se esforçou para reduzir, e mais do que um cartão de 80 GB comporta. Esse é o número do modelo uniformemente de attention que postulamos, e é com ele que se raciocina. Para o Qwen3.8-27B especificamente é uma aproximação de sinal desconhecido: a lição 4.16 mostrou que só 16 das suas 64 camadas são de full attention, e as outras 48 são Gated DeltaNet, cujas ativações de treino são uma contabilidade à parte — um estado de inferência de tamanho fixo não implica armazenamento barato no backward, porque o mixer recorrente ainda materializa intermediários por token que o autograd precisa guardar. A metade da FFN, que é a maior parte do total, é idêntica nas 64 camadas de qualquer forma. A estimativa só do residual errou por um fator de aproximadamente dezenove, e erra na direção que faz um job ser agendado e depois morto.

Agora aplique o checkpointing, e deixe a arquitetura escolher a fronteira. Divida a rede em segmentos; durante o forward pass guarde apenas o tensor de fronteira de cada segmento em vez de tudo dentro dele; durante o backward, rode de novo o forward daquele segmento a partir da fronteira salva, reconstrua seus intermediários, compute os gradientes e libere-os. As 64 camadas deste modelo formam 16 super-blocos repetidos de três camadas de Gated DeltaNet mais uma camada de full attention, o que é uma segmentação natural e perfeitamente balanceada. Cada fronteira é um hidden state:

8192×5120×2 B=83,886,080 B=80 MiB,8192\times 5120\times 2\ \text{B}=83{,}886{,}080\ \text{B}=80\ \text{MiB}, 80 MiB×16 fronteiras=1.25 GiB.80\ \text{MiB}\times 16\ \text{fronteiras}=1.25\ \text{GiB}.

Durante o backward, um super-bloco é rematerializado por vez, custando o equivalente aos intermediários de quatro camadas — ainda precificados à taxa do modelo uniformemente de attention, mas essa taxa é apenas um ponto de comparação hipotético para o híbrido, não um teto conhecido nem um pico medido:

4×8192×192 KiB6.0 GiB.4\times 8192\times 192\ \text{KiB}\approx 6.0\ \text{GiB}.

Dentro daquele modelo didático de full attention, a memória de pico de ativações salvas vira aproximadamente 1.25+6.07.31.25+6.0\approx 7.3 GiB em vez de 96 GiB. A razão de cerca de treze vezes pertence apenas a essa contagem por formatos; ela não se transfere ao híbrido real até medir seu grafo de autograd de DeltaNet, kernels fundidos, allocator e política de recomputação.

O tradeoff é direto: menor pico de memória de ativações por computação extra. Não é simplesmente dobrar o tempo de treino, porque backward, comunicação e trabalho não checkpointado continuam de qualquer forma e a recomputação pode se sobrepor de modo diferente. Meça tempo de passo e throughput realizado, não o aumento teórico de FLOPs.

Um orçamento de memória deve manter as categorias separadas:

Mpeak=Mparams+Mgrads+Moptimizer+Mactivations+Mtemporary+Mallocator.M_{peak}=M_{params}+M_{grads}+M_{optimizer}+M_{activations}+M_{temporary}+M_{allocator}.

O checkpointing ataca MactivationsM_{activations} e nada mais. Ele não sharda parâmetros nem muda estado persistente do optimizer. Os dtypes e componentes exatos desse estado dependem da receita, e a lição 9.3 assume o orçamento de memória do Qwen. Um job dominado por estado persistente precisa de ZeRO ou FSDP; um dominado por ativações de sequência longa precisa de checkpointing.

O comprimento de sequência é onde este modelo fica interessante. A estimativa acima usou 8.192 tokens; o contexto nativo é 262.144. As ativações nas partes position-wise da rede crescem linearmente com TT, então a mesma aritmética no contexto completo é trinta e duas vezes maior e absolutamente inviável sem checkpointing e o sequence parallelism da lição 5.8 juntos. Attention comum acrescentaria um termo quadrático em TT por cima, que é precisamente por que kernels de attention eficientes em memória, que nunca materializam a matriz completa de probabilidades, são obrigatórios nesses comprimentos — e por que só 16 das 64 camadas enfrentam esse termo, com as outras 48 varrendo posições com um estado recorrente de tamanho fixo.

Duas armadilhas de correção merecem ser nomeadas. Operações estocásticas precisam reproduzir sua aleatoriedade: se o dropout sorteou uma máscara no forward original e outra durante a recomputação, o backward derivaria uma função diferente, então utilitários de checkpoint dos frameworks preservam e restauram o estado do RNG a um custo pequeno. E módulos com estado, efeitos colaterais, caches mutáveis e chamadas externas dependentes de dados podem tornar a recomputação silenciosamente incorreta em vez de ruidosamente quebrada.

A composição acrescenta o resto da complexidade. O pipeline parallelism faz a recomputação competir com outros microbatches pelos mesmos dispositivos. O FSDP pode precisar de um novo all-gather dos pesos durante a recomputação, a menos que os parâmetros tenham permanecido materializados, trocando comunicação por memória. Compiladores fundem e reordenam regiões de formas que mudam o que um segmento sequer é. A unidade efetiva é o escalonamento distribuído inteiro, nunca uma função isolada.

O modelo mental duradouro é uma troca tempo–espaço: guarde estado de fronteira suficiente para reconstruir o grafo do forward, descarte o miolo volumoso e pague compute para recriá-lo pouco antes de seus gradientes serem necessários. A decisão decorre de uma decomposição medida de para onde a memória de fato foi — que, num modelo com um bloco feed-forward de 17.408 de largura, quase nunca é onde uma primeira estimativa a coloca.

02 · Analogia

Analogia

Um caminhante numa trilha longa pode fotografar cada curva ou apenas os grandes entroncamentos. Fotografar tudo facilita o caminho de volta, mas lota o celular. Guardar só os entroncamentos economiza armazenamento; na volta, o caminhante precisa refazer cada trecho para reconstruir as curvas intermediárias. O checkpointing guarda entroncamentos de ativação selecionados e recomputa o caminho quando os gradientes viajam para trás.

03 · Explique de volta

Explique de volta

Estime ativações salvas para um modelo explicitamente hipotético de full attention com as dimensões do Qwen3.8-27B, mostre como fronteiras de checkpoint em super-blocos mudam essa estimativa e explique por que a razão resultante não pode ser transferida ao híbrido real sem medição.

Mínimo: 80 caracteres e 15 palavras. Seu texto fica somente neste navegador.

Aguardando sua explicação.

Comparar com uma resposta-modelo

Para um modelo deliberadamente uniforme de full attention com essas dimensões, uma contagem por formatos dá cerca de 98.300 valores retidos por token por camada, aproximadamente 192 KiB em bf16, ou cerca de 96 GiB em 8.192 tokens e 64 camadas. Esse é um modelo didático, não o pico medido do Qwen3.8-27B: suas 48 camadas DeltaNet retêm intermediários diferentes de convolução, projeção, gate, beta e estado recorrente. Sob a mesma taxa hipotética por camada, guardar 16 estados de fronteira custa cerca de 1,25 GiB e rematerializar quatro camadas cerca de 6,0 GiB. Esses números demonstram o método de checkpointing; nem 7,3 GiB como teto nem a razão de 13 vezes foram estabelecidos para o híbrido real. Seu pico precisa ser medido com o grafo de autograd e os kernels reais.

04 · Teste seu entendimento

Teste seu entendimento

01Estimar a memória de ativações como batch x sequência x hidden x camadas x 2 bytes dá cerca de 5 GiB para uma sequência de 8.192 tokens. Por que o número para um modelo uniformemente de full attention deste tamanho fica mais perto de 96 GiB?
Resposta e explicação

Essa fórmula conta só o residual stream; o autograd também retém as cópias normalizadas, as projeções de attention e três tensores de FFN com 17.408 de largura por camada, cerca de 19 vezes mais valores — Os intermediários da FFN dominam: três tensores de largura 17.408 contra um residual stream de 5120 de largura, e é por isso que uma estimativa só do residual subestima por mais de uma ordem de magnitude.

02Sob a receita ilustrativa de optimizer discutida nas lições 5.7 e 9.3, o activation checkpointing reduz estado persistente do optimizer?
Resposta e explicação

Não — checkpointing muda as ativações salvas, não a política persistente de parâmetros, gradientes ou estado do optimizer usada pelo job — As categorias são independentes: o sharding trata do estado persistente, o checkpointing trata das ativações, e um job pode ser limitado por qualquer um dos dois.

03Por que o estado do gerador de números aleatórios precisa ser tratado durante a recomputação?
Resposta e explicação

Dropout e outras operações estocásticas precisam reproduzir máscaras compatíveis — Um forward estocástico diferente durante o backward não reproduziria a computação cujos gradientes são exigidos.

Conclua o teach-back e acerte o quiz para finalizar a aula.

◎ · Marcador de evidência

Fontes

  1. Tianqi Chen, Bing Xu, Chiyuan Zhang e Carlos Guestrin (2016). Training Deep Nets with Sublinear Memory Cost.
  2. Qwen Team (2026). Qwen3.8-27B Model Card.