Entre na beta da Hanzo e ganhe 50% de desconto em qualquer plano pago

BitDelta: servir 14 modelos Zen a partir de um único cluster de GPU

Como a compressão delta de 1 bit do BitDelta permite servir 14 variantes de modelos Zen a partir de infraestrutura de GPU compartilhada: a matemática, a arquitetura e os tradeoffs.

Nesta página

A economia de servir variantes de modelo com ajuste fino é hostil por padrão.

Um modelo de 70B parâmetros em BF16 exige 140GB de memória de GPU. Se você tem 14 variantes com ajuste fino desse modelo — cada uma representando um perfil de capacidade diferente, uma especialização de domínio ou um ajuste de comportamento — a arquitetura de serving ingênua exige 14 cargas de modelo separadas: 1.96TB de memória de GPU, 14 processos de inferência separados, 14x o custo de infraestrutura. Para cada variante além da primeira, você paga o preço cheio por algo que é quase inteiramente conhecimento de base duplicado.

BitDelta (arXiv:2402.10193) quebra essa economia. A ideia é elegante e a matemática é simples.

A base matemática

Seja W_base a matriz de pesos do modelo base e W_ft a matriz de pesos correspondente em uma variante com ajuste fino. O delta de comportamento é:

Δ = W_ft - W_base

O BitDelta comprime esse delta para uma precisão de 1 bit:

W_ft ≈ W_base + α · sign(Δ)

Aí α é um fator de escala por matriz, calculado como o valor absoluto médio de Δ:

α = (1/n) · ||Δ||_1 = mean(|Δ_ij|)

A operação de sinal reduz cada delta escalar a um único bit, positivo ou negativo. O fator de escala α devolve a magnitude. Juntas, a aproximação captura a informação direcional da atualização de ajuste fino a 1/32 do custo de armazenamento de um delta em precisão cheia (1 bit contra 32 bits por elemento de parâmetro).

Por que isso funciona? O ajuste fino de um modelo pré-treinado grande produz deltas que, em esperança, são pequenos em relação aos pesos base. O modelo base captura a estrutura dominante da língua; o ajuste fino captura modificações de comportamento por cima dessa estrutura. Quando o delta é pequeno, o sinal de cada elemento é a representação de maior informação desse elemento: a magnitude é mais ou menos uniforme na matriz delta, então α · sign(Δ) é um código eficiente.

Dá para dizer isso com mais formalidade. Se modelamos Δ_ij como uma variável aleatória de Laplace com localização 0 e escala b:

Δ_ij ~ Laplace(0, b)
|E[sign(Δ) | |Δ|]| = 1 for all nonzero Δ

O sinal preserva a informação direcional por completo. A perda de informação está só na variação de magnitude, que o único fator de escala α recupera em parte. Para deltas de ajuste fino em que a variação de magnitude é baixa (modificação de comportamento, e não transferência de conhecimento), a aproximação é apertada.

Redução de memória: num modelo de 70B em BF16, os pesos base ocupam 140GB. O delta de cada variante com ajuste fino, em BF16 cheio, somaria outros 140GB. No formato BitDelta, cada delta ocupa aproximadamente 8.75GB (70B bits = 8.75GB, mais os escalares α, que são desprezíveis). O cluster de 14 variantes passa de 14 × 140GB = 1.96TB para 140GB + 14 × 8.75GB ≈ 262GB, uma redução de 87%.

Arquitetura de serving

┌─────────────────────────────────────────────────────────────────┐
│                    GPU CLUSTER (8x H100, 80GB each)             │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │              BASE WEIGHTS (140GB, HBM resident)          │   │
│  │  W_base for zen4-70b: attention, FFN, embeddings, norms  │   │
│  └──────────────────────────────┬───────────────────────────┘   │
│                                 │ shared read                    │
│  ┌──────────────────────────────▼───────────────────────────┐   │
│  │           DELTA CACHE (≈8.75GB per variant)              │   │
│  │  zen4-coder:    α_coder,    sign(Δ_coder)    [8.75GB]    │   │
│  │  zen4-ultra:    α_ultra,    sign(Δ_ultra)    [8.75GB]    │   │
│  │  zen4-math:     α_math,     sign(Δ_math)     [8.75GB]    │   │
│  │  zen4-writer:   α_writer,   sign(Δ_writer)   [8.75GB]    │   │
│  │  zen4-legal:    α_legal,    sign(Δ_legal)    [8.75GB]    │   │
│  │  zen4-medical:  α_medical,  sign(Δ_medical)  [8.75GB]    │   │
│  │  ...14 total...                                           │   │
│  └──────────────────────────────┬───────────────────────────┘   │
│                                 │                                │
│  ┌──────────────────────────────▼───────────────────────────┐   │
│  │              REQUEST ROUTER                               │   │
│  │  model_id → delta_load → reconstruct W_ft → inference    │   │
│  └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

Na hora da inferência, o pipeline de serving é:

  1. A solicitação chega com model: "zen4-coder"
  2. O roteador identifica a entrada do cache de deltas de zen4-coder
  3. O peso efetivo é reconstruído na hora: W_eff = W_base + α_coder · sign(Δ_coder)
  4. A passagem para a frente roda com os pesos reconstruídos
  5. A resposta é devolvida

O passo 3 é o caminho crítico. A reconstrução é um kernel CUDA fundido que lê ao mesmo tempo os pesos base e os bits do delta, e reconstrói o peso efetivo em registradores ou na memória compartilhada sem materializar o W_eff inteiro na HBM. A reconstrução e a multiplicação de matrizes podem ser fundidas numa única passagem do kernel: o peso reconstruído nunca existe por completo como tensor independente.

# Simplified reconstruction kernel (conceptual)
def fused_bitdelta_linear(
    x: torch.Tensor,          # input activations [batch, seq, d_in]
    w_base: torch.Tensor,     # base weight [d_out, d_in] in HBM
    delta_sign: torch.Tensor, # 1-bit delta [d_out, d_in] packed as int8
    alpha: torch.Tensor,      # scale factor [d_out] or scalar
) -> torch.Tensor:
    # In practice: single CUDA kernel, no intermediate w_eff allocation
    # Conceptually equivalent to:
    delta_unpacked = unpack_bits(delta_sign).to(w_base.dtype)  # {-1, +1}
    w_eff = w_base + alpha.unsqueeze(1) * delta_unpacked
    return torch.nn.functional.linear(x, w_eff)

A abordagem do kernel fundido mantém o consumo de pico de banda da HBM em aproximadamente 1.5x os pesos do modelo base, e não em 2x, porque o delta é lido em formato de bits empacotado e desempacotado na SRAM rápida durante a operação fundida.

Perfil de latência

Na prática, no cluster de serving Zen de 14 variantes:

VarianteP50 TTFTP99 TTFTcontra a linha de base
zen4 (base)18ms42ms1.0x
zen4-coder21ms48ms1.17x
zen4-ultra22ms51ms1.22x
zen4-math21ms47ms1.17x
zen4-writer20ms45ms1.11x

O overhead de tempo até o primeiro token da reconstrução BitDelta é de 2-4ms, desprezível diante do custo de prefill para tamanhos de prompt típicos. O kernel de reconstrução é limitado por computação, não por memória, então escala bem com a geração na GPU.

A vazão de geração (tokens/segundo) é idêntica entre o modelo base e qualquer variante BitDelta, porque a reconstrução só ocorre na fase de prefill. Depois que o cache KV está cheio, o passo de geração autorregressiva usa os caminhos matmul padrão.

Qualidade: o que se perde na compressão de 1 bit

A resposta honesta: uma quantidade pequena, cuidadosamente limitada.

A queda de qualidade do BitDelta depende da tarefa. Em tarefas bem representadas nos dados de ajuste fino, o delta de comportamento é forte: o sinal captura de forma confiável a direção dominante do gradiente. Em tarefas na periferia da distribuição de ajuste fino, o delta é mais fraco: a aproximação introduz mais ruído em relação ao sinal.

Empiricamente, nos modelos Zen avaliados nas suítes de tarefas alvo:

ModeloPrecisão cheiaBitDeltaDelta
zen4-coder (HumanEval)72.4%71.1%-1.3%
zen4-math (MATH-500)68.9%67.8%-1.1%
zen4-writer (WritingBench)84.2%83.7%-0.5%
zen4-legal (LegalBench)61.3%60.1%-1.2%

A degradação de 1-1.3% nas suítes de tarefas primárias é o preço de uma redução de memória de 87%. Para todas as variantes Zen, esse tradeoff é aceitável: a diferença de qualidade fica abaixo do limiar de uma mudança de comportamento perceptível pelo usuário nos casos de uso típicos.

MonoSoup: complemento SVD de um único checkpoint

MonoSoup (arXiv:2602.09689) trata de um problema complementar: quando existem vários checkpoints com ajuste fino do mesmo modelo base, como você faz o merge deles num único checkpoint sem carregar todos ao mesmo tempo?

O MonoSoup aplica SVD ao vetor de tarefa (Δ = W_ft - W_base) para identificar os componentes de posto baixo que capturam a maior informação de comportamento, e depois agrega esses componentes entre os checkpoints:

For checkpoints {W_1, W_2, ..., W_k}:
  Δ_i = W_i - W_base
  [U_i, S_i, Vh_i] = truncated_SVD(Δ_i, rank=r)
  τ_i = U_i · diag(S_i) · Vh_i  # rank-r approximation

MonoSoup merge:
  W_merged = W_base + (1/k) · Σ τ_i

O truncamento SVD no posto r funciona como um passo de denoising: o ajuste fino tende a modificar um modelo ao longo de um número pequeno de direções de alta variância no espaço de pesos. Capturar essas direções por SVD truncada e fazer o merge delas captura a essência de comportamento de cada checkpoint, sem o ruído das direções de valores singulares mais baixos.

Usamos o MonoSoup nas rodadas de treinamento do Zen como passo de média de checkpoints: a cada 1000 passos de gradiente, fazemos o merge do checkpoint atual com o melhor anterior via MonoSoup, ao invés da média ingênua. Isso reduz o ruído de treinamento e produz curvas de perda mais suaves, com qualidade final melhor.

K-Merge: gerenciamento online de adaptadores LoRA na borda

K-Merge (arXiv:2510.13537) resolve outra variante do problema de serving: na borda, onde o armazenamento está limitado a um orçamento fixo de B gigabytes, como você serve K adaptadores LoRA com eficiência?

O K-Merge mantém um pool de adaptadores em merge sob a restrição de que o armazenamento total nunca passe de B. Quando um adaptador novo é solicitado e não está no pool:

  1. Calcula-se a similaridade par a par entre o adaptador novo e todos os membros do pool (similaridade de cosseno sobre os vetores de tarefa)
  2. Identifica-se o adaptador existente mais parecido
  3. Faz-se o merge do adaptador novo no existente mais parecido via OPCM (merge por projeção ortogonal)
  4. Se a qualidade do adaptador em merge nas duas tarefas passa do limiar, despeja-se o original e guarda-se a versão em merge

A política garante que o pool contém no máximo K adaptadores consumindo o orçamento B, ao mesmo tempo em que maximiza a cobertura de todos os adaptadores solicitados por meio de um merge com critério.

Para o deployment do Zen na borda (Hanzo Desktop, celular), o K-Merge habilita famílias de modelos servidas no próprio dispositivo que, de outro modo, estourariam os limites de armazenamento. Um dispositivo com 16GB de armazenamento de modelo disponível pode servir 8 perfis de comportamento distintos mantendo um pool K-Merge que faz merge de adaptadores parecidos sob demanda.

Detalhes de infraestrutura

Formato de armazenamento do delta: matrizes de sinal de 1 bit guardadas como tensores int8 empacotados (8 bits por byte, de modo que uma camada de 70B parâmetros com um peso de 4096×4096 empacota em 4096×512 int8). Os fatores de escala α ficam em float32, um por dimensão de saída, e somam aproximadamente 0.1% do armazenamento do delta.

Comportamento de inicialização a frio: quando o delta de uma variante não está em cache na SRAM da GPU (por exemplo, a variante não foi solicitada em 10 minutos), a primeira solicitação paga uma penalidade de latência de carga do delta de aproximadamente 200ms num modelo de 70B, o tempo de transferir 8.75GB de NVMe para HBM nas velocidades de leitura sequencial de NVMe. Depois da primeira solicitação, o delta permanece no cache de deltas da SRAM da GPU até a política LRU despejá-lo.

Política de despejo do cache: LRU com peso pela taxa de solicitações. Variantes com taxa alta de solicitações ficam fixadas; variantes de pouco tráfego podem ser despejadas. Na prática, as 6 variantes no topo por volume de tráfego ficam sempre residentes no cache; as 8 restantes dividem o espaço de cache que sobra, com despejo LRU.

Roteamento de solicitações: o roteador de serving acompanha as solicitações em voo por variante e evita o bloqueio de cabeça de linha: se o delta de zen4-coder está sendo carregado, as solicitações zen4-coder que chegam entram na fila, ao invés de cada uma disparar uma carga independente.

O resultado: 14 variantes de modelos Zen servidas a partir de nós 8x H100 que antes teriam exigido nós 112x H100 para a mesma capacidade. A redução do custo de infraestrutura não é incremental. O BitDelta torna a família de modelos multi-tenant economicamente viável.


Os modelos Zen estão disponíveis em zenlm.org. Acesso à API em hanzo.ai.

Comentários

Entre com seu Hanzo ID para comentar.

Carregando comentários…

Leia mais