TRL documentation

Destilação Assíncrona

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v1.17.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Destilação Assíncrona

Este trainer requer vllm, transformers>=5.2.0 e kernels (o treinamento carrega o FlashAttention do Hub por meio dele). Para treinamento distribuído, apenas o FSDP2 é suportado (o DeepSpeed ZeRO não é).

Atualmente, vllm e transformers possuem restrições de dependência conflitantes. Para contornar isso, instale o vLLM primeiro, em seguida, force a instalação do transformers e, depois, adicione kernels por meio do extra do TRL, que fixa a versão que o transformers aceita:

pip install vllm
pip install 'transformers>=5.2.0' --no-deps
pip install 'trl[kernels]'

Visão geral

experimental.async_distillation.AsyncDistillationTrainer é o equivalente assíncrono do DistillationTrainer, com arquitetura semelhante à do experimental.async_grpo.AsyncGRPOTrainer: um worker de rollout em segundo plano gera as próprias conclusões on-policy do aluno e as pontua em relação a um professor, enquanto o treinamento prossegue de forma concorrente, em vez de alternar entre geração e atualizações de gradiente. Diferentemente do trainer síncrono, o professor nunca é carregado localmente — apenas uma URL de servidor vLLM é necessária, de modo que o professor pode ser executado em hardware totalmente separado do aluno e do trainer, ou até mesmo ser um modelo muito maior do que caberia ao lado do aluno.

compute_loss minimiza uma Divergência de Jensen-Shannon generalizada entre as distribuições de tokens por posição do aluno e do professor (beta=0.0 é KL direta, beta=1.0 é KL reversa, valores intermediários interpolam), o mesmo objetivo usado por DistillationTrainer e ServerDistillationTrainer. O treinamento é sempre on-policy: o aluno gera todas as conclusões nas quais ele é treinado.

Destilação on-policy com múltiplos professores (MOPD)

O MOPD não faz parte do artigo 2306.13649, no qual se baseia o objetivo central deste trainer (que descreve um cenário síncrono com um único professor); trata-se de um método separado, descrito em MOPD: Multi-Teacher On-Policy Distillation for Capability Integration in LLM Post-Training. Lá, o MOPD é a terceira de três etapas — SFT geral, em seguida treinamento de RL independente por domínio de um especialista por domínio, e então o MOPD funde esses especialistas congelados em um único aluno. O AsyncDistillationTrainer implementa apenas essa terceira etapa de fusão: os professores especialistas por domínio devem já existir (por exemplo, treinados separadamente com GRPOTrainer/RLOOTrainer) e estar sendo servidos via HTTP antes de você apontar teacher_server_urls para eles. A própria Etapa 3 do artigo usa KL reversa (beta=1.0), e não o padrão deste trainer, beta=0.0 (KL direta).

teacher_server_urls aceita mais de uma entrada. Com uma única entrada, cada amostra é pontuada por aquele único professor (destilação on-policy simples). Com múltiplas entradas, a coluna teacher_id de cada amostra de treinamento seleciona qual professor a pontua — por exemplo, roteando prompts de matemática para um professor especialista em matemática e prompts de código para um professor especialista em código, cada um servido independentemente. Cada amostra é despachada para o seu professor correspondente, nunca sendo média ou ensemble entre professores. Uma amostra com teacher_id ausente ou não mapeado gera um erro em vez de cair silenciosamente para o professor errado. Veja examples/async_distillation_mopd/async_distillation_mopd.py para um exemplo executável que funde os especialistas em matemática, código e seguimento de instruções liberados com Open-MOPD em seu checkpoint SFT compartilhado, e examples/async_distillation_mopd/launcher.py para executá-lo em 4 GPUs com um único comando do Hugging Face Jobs.

Cada professor deve compartilhar o tokenizer do aluno. As conclusões são enviadas aos professores como ids de tokens brutos, e os ids de candidatos que um professor relata de volta indexam diretamente o vocabulário do próprio aluno em compute_loss. Professores da mesma família de modelos que o aluno (como no exemplo de MOPD, em que um aluno SmolLM3 é fundido a partir de especialistas SmolLM3 treinados a partir do mesmo checkpoint) satisfazem isso; um professor com um vocabulário diferente treina o aluno contra os tokens errados, silenciosamente, a menos que seu vocabulário seja maior que o do aluno.

Checkpoint e retomada: ignore_data_skip tem valor padrão True; o loop de pulo e reprocessamento do Trainer base não se aplica a uma fila de rollout ao vivo. Em vez disso, o índice do primeiro prompt ainda não treinado é salvo em rollout_state.json junto com cada checkpoint e restaurado na retomada, de modo que o worker avança rapidamente até esse prompt sem reprocessar amostras. É a posição treinada, não a do gerador: o worker opera à frente do treinamento pela profundidade da fila de rollout, e essas amostras em buffer são perdidas quando a execução termina, portanto, retomar a partir da posição do gerador pularia prompts que foram gerados, mas nunca treinados. Conjuntos de dados em streaming (IterableDataset) não podem ser reposicionados; seu worker reinicia do prompt 0 na retomada. Com FSDP e bf16=True (o padrão) ou fp16=True, model.safetensors é gravado nesse dtype, mesmo que os pesos mestres sejam fp32, de modo que um checkpoint publicado tem o tamanho do modelo servido, e não o dobro; os pesos mestres fp32 e o estado do otimizador ainda vão para os arquivos de retomada (pytorch_model_fsdp.bin, optimizer.bin), que são os lidos na retomada, portanto, a retomada não é afetada. Sem FSDP, o checkpoint também é o arquivo de retomada, então ele mantém o dtype mestre.

Como difere do DistillationTrainer

Em DistillationTrainer, o modelo professor é carregado localmente: a geração, a passagem direta do professor e a atualização do gradiente ocorrem sequencialmente no mesmo processo. experimental.async_distillation.AsyncDistillationTrainer separa essas responsabilidades da mesma forma que experimental.async_grpo.AsyncGRPOTrainer separa o rollout do GRPO de sua atualização:

  • Trabalhador de rollout (processo em segundo plano) — gera conclusões a partir do servidor vLLM do aluno, envia a sequência completa de volta para o /v1/completions do professor resolvido com prompt_logprobs para pontuação forçada pelo professor (sem novos tokens gerados pelo professor) e insere amostras prontas para treinamento em uma fila.
  • Loop de treinamento (processo principal) — extrai amostras da fila, calcula a perda JSD generalizada e atualiza os pesos do aluno.

Como o professor é pontuado via HTTP em vez de uma passagem local de forward, apenas uma fatia esparsa, top-k, de sua distribuição é transmitida (teacher_top_k), e não o vocabulário completo — consulte a documentação de beta e teacher_top_k em experimental.async_distillation.AsyncDistillationConfig para saber exatamente quais candidatos o protocolo de rede garante um logprob do professor para cada regime de beta.

A cada weight_sync_steps passos de treinamento, os pesos atualizados do aluno são transferidos para seu servidor vLLM via NCCL. Assim como no experimental.async_grpo.AsyncGRPOTrainer, a geração fica à frente do treinamento, portanto as amostras podem refletir uma política ligeiramente desatualizada; max_staleness controla quantas atualizações de pesos uma amostra pode ficar para trás antes de ser descartada.

Início rápido

# train_async_distillation.py
from datasets import load_dataset
from trl.experimental.async_distillation import AsyncDistillationTrainer

dataset = load_dataset("trl-lib/DeepMath-103K", split="train")

trainer = AsyncDistillationTrainer(
    model="Qwen/Qwen2.5-0.5B-Instruct",
    train_dataset=dataset,
)
trainer.train()

O servidor do professor, o servidor vLLM do aluno e o treinador devem ser executados em GPUs separadas. Ambos são instâncias simples de vllm serve, mas precisam de flags diferentes: o professor é apenas pontuado (--logprobs-mode processed_logprobs para que teacher_temperature alcance os logprobs retornados, --max-logprobs -1 para que teacher_top_k possa exceder o limite padrão de 20 do vLLM), enquanto o aluno é apenas gerado e precisa ter a transferência de pesos NCCL habilitada para que o treinador possa enviar os pesos atualizados para ele. A pontuação envia a sequência inteira do aluno e pede ao professor mais um token, portanto o --max-model-len do professor (ou seu padrão) deve ser maior que o do aluno:

# Terminal 1: teacher server on GPU 0 (static, never updated)
CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen2.5-1.5B-Instruct \
    --port 8001 \
    --logprobs-mode processed_logprobs \
    --max-logprobs -1
# Terminal 2: student's vLLM server on GPU 1 (dev mode + NCCL weight transfer are required)
CUDA_VISIBLE_DEVICES=1 VLLM_SERVER_DEV_MODE=1 vllm serve Qwen/Qwen2.5-0.5B-Instruct \
    --port 8000 \
    --weight-transfer-config '{"backend":"nccl"}'
# Terminal 3: training on GPU 2
CUDA_VISIBLE_DEVICES=2 python train_async_distillation.py

LoRA

Passe um peft_config para treinar um adaptador LoRA no aluno em vez do modelo completo:

from peft import LoraConfig

trainer = AsyncDistillationTrainer(
    model="Qwen/Qwen2.5-0.5B-Instruct",
    train_dataset=dataset,
    peft_config=LoraConfig(r=32, lora_alpha=64, target_modules="all-linear"),
)

Cada sincronização de pesos precisa enviar o aluno atualizado para o servidor. Por padrão, o trainer mescla o adaptador no modelo base e envia os pesos completos mesclados, o que corresponde a cerca de 15 GB para um modelo de 7B em cada sincronização. Inicie o servidor vLLM do aluno com --enable-lora e ele enviará apenas o adaptador, cerca de 160 MB em r=32:

CUDA_VISIBLE_DEVICES=1 VLLM_SERVER_DEV_MODE=1 VLLM_ALLOW_RUNTIME_LORA_UPDATING=1 vllm serve Qwen/Qwen2.5-0.5B-Instruct \
    --port 8000 \
    --weight-transfer-config '{"backend":"nccl"}' \
    --enable-lora \
    --max-lora-rank 32 \
    --max-loras 6

VLLM_ALLOW_RUNTIME_LORA_UPDATING=1 expõe o endpoint para o qual o trainer envia cada novo adapter. Adicione VLLM_WORKER_MULTIPROC_METHOD=spawn se você estiver servindo com tensor-parallel e mantenha --weight-transfer-config em qualquer caso: o trainer só escolhe um modo de sincronização quando inicia, momento em que o servidor já está ativo, e a sincronização mesclada é o fallback. O servidor teacher não é afetado: ele nunca é atualizado.

--max-lora-rank deve ser um dos valores 1, 8, 16, 32, 64, 128, 256, 320, 512. Ele define o rank máximo que o servidor pode atender, e não o rank que ele efetivamente servirá, portanto um adaptador r=4 funciona perfeitamente sob 8. --max-loras deve ser pelo menos max_staleness + 2: o trainer mantém max_staleness + 1 versões de adaptador registradas para que um rollout iniciado sob uma política mais antiga possa ser concluído sob ela, em vez de trocar de política no meio da geração, e cada sincronização carrega a próxima versão antes de descarregar a mais antiga. O trainer verifica ambos os valores ao iniciar e informa com quais valores reiniciar o servidor.

O vLLM carrega adaptadores do disco em vez da rede, portanto o trainer grava cada versão em <output_dir>/.vllm_lora/ e fornece o caminho ao servidor. Se o servidor estiver em uma máquina diferente, esse diretório precisa estar em um sistema de arquivos visível para ambos.

<output_dir>/.vllm_lora/ é um cache de serviço, não o local onde o seu adaptador treinado é armazenado. Ele apenas mantém as versões mais recentes, e cada uma é excluída pouco depois de sair da janela de obsolescência. Os checkpoints são o que preservam o adaptador que você treinou, portanto, não os desative: com save_strategy="no", uma execução de várias horas termina e não deixa nada para trás. O output_dir também precisa ser legível pelo servidor e precisa sobreviver ao término do job, portanto, um armazenamento temporário local do nó não funcionará, mesmo que isso mantivesse as sincronizações fora de um sistema de arquivos compartilhado lento.

A sincronização de adaptadores nem sempre é mais rápida. O vLLM gera tokens aproximadamente 1,33 a 1,39 vezes mais devagar ao servir um adaptador LoRA do que ao servir pesos mesclados, e a geração geralmente é o que domina o tempo real de execução. Em contrapartida, você obtém uma pausa muito mais curta em cada sincronização e cerca de 1% da largura de banda de sincronização. Se o throughput de geração for o seu gargalo, a sincronização mesclada ainda costuma ser a melhor escolha.

Métricas registradas

Um rollout passa por várias etapas antes de se tornar um gradiente. Primeiro, vamos dar uma olhada nas diferentes etapas, de um prompt para a composição final do nosso lote:

Dataset row = PROMPT (message list + optional `teacher_id`)
 └─ ROLLOUT            (1 prompt -> 1 student completion -> 1 teacher scoring call)
     ├─ generate       (student's vLLM /v1/completions, sampled)
     └─ score          (the routed teacher's /v1/completions with `prompt_logprobs`, teacher-forced)
         └─ SAMPLE     (prompt + completion + the teacher's sparse per-position distribution)

════════════ Trainer process boundary: `rollout_buffer` (mp.Queue) ════════════

         └─ SAMPLE (Pulled 1 at a time; dropped if staleness > `max_staleness`)
             └─ ROW            (Planner assigns it to one of `dp` rows, Σ Lᵢ²-balanced)
                 └─ MICRO-BATCH (`dp` rows, one per rank)
                     └─ PACKED ROW (1 concat sequence, `position_ids` reset per sample)
                         └─ FORWARD (`compute_loss`, bs=1, inter-rank padding stripped)
             └─ OPTIMIZER STEP (from `grad_accum` micro-batches)

Lendo de cima para baixo:

  • um rollout é um prompt gerado uma vez e pontuado uma vez. Não há grupo: a destilação não possui uma linha de base de vantagem para calcular entre gerações, portanto, um prompt não é repetido e um rollout gera exatamente uma amostra de treinamento.
  • um sample é aquela conclusão mais os teacher_top_k principais candidatos do professor para cada posição da conclusão. É isso que atravessa a fronteira do processo.
  • além do limite, o trainer planeja as amostras em linhas (uma por rank de DP), empacota cada linha em uma única sequência concatenada e acumula grad_accum micro-lotes em um único passo do otimizador.

Portanto, dividimos nossas métricas com base na etapa (ou entidade) sobre a qual elas se referem:

namespaceentidade que ele conta
rollout/um ciclo de geração e pontuação: quanto tempo levou, até onde chegou
completions/o que o aluno (vLLM) gerou para um prompt
sample/representa uma amostra de treinamento, conforme ela chega à fila
batch/um micro-lote ou um passo do otimizador (construído a partir de uma ou várias amostras)
perf/segundos e FLOPs medidos

Tudo o que a própria perda mede é registrado sem prefixo (jsd, entropy, teacher_entropy), com uma divisão teacher_*/<teacher_id> sob MOPD — veja a divergência abaixo.

Vamos também definir alguns termos para tornar as métricas de tokens inequívocas:

  • Os tokens gerados são aqueles produzidos pelo aluno (completions/*).
  • Os tokens forwarded são todos os tokens que a passagem forward processa: o prompt e os tokens gerados.
  • Os tokens treinados são o subconjunto sobre o qual a perda é calculada, onde completion_mask == 1. Treinado ≠ gerado: uma posição de conclusão para a qual o professor não pontuou nenhum candidato é mascarada na divergência, mas ainda assim é encaminhada.

Como os números de lote se compõem

Uma linha é o que um rank de DP faz o forward em um micro-lote: várias amostras concatenadas em uma única sequência, com position_ids reiniciando em cada fronteira de amostra. O planejador decide em qual linha cada amostra será alocada (equilibrando Σ Lᵢ² para que nenhum rank fique para trás), e um data collator realiza a concatenação.

Em todas as nossas métricas, um “step” sempre significa um passo COMPLETO do otimizador, nunca um micro-lote. gradient_accumulation_steps micro-lotes formam um passo, e toda métrica chamada _per_step é por passo do otimizador, correspondendo a global_step, logging_steps e ao restante do vocabulário do Trainer. Quando uma métrica é por micro-lote, isso é indicado (batch/microbatches_per_step) ou ela é uma quantidade por linha (batch/row_*, batch/samples_per_row).

Portanto, uma etapa contém um número fixo de row-slots:

row-slots per step = gradient_accumulation_steps x world_size

o que faz com que as métricas do lote se componham em uma escada que você pode verificar entre si:

sample                                     sample/forwarded_tokens_mean
  └─ packed into a ROW                     batch/samples_per_row, batch/row_tokens_mean
      └─ one row per DP rank               = one micro-batch
          └─ gradient_accumulation_steps micro-batches = one STEP
                                           batch/samples_per_step, batch/forwarded_tokens_per_step

Portanto, batch/samples_per_step ≈ row-slots x batch/samples_per_row, e o mesmo vale para tokens. Os dois lados só coincidem até a variação entre micro-lotes dentro do passo — as métricas por passo são somas, enquanto as por linha são médias —, então espere uma diferença de fração de um percentual, não uma correspondência exata.

batch/row_fill_frac é o que deve ser monitorado quando as amostras são longas: uma amostra de 10k tokens preenche mal um orçamento de 32k (três cabem, quatro nunca cabem, então o empacotador frequentemente obtém apenas duas e a linha fica ~77% cheia), enquanto amostras de 1k token preenchem-no quase perfeitamente. Isso é quantização, não um bug, e token_budget é o parâmetro de ajuste — tendo em mente que a atenção é O(L²) por sequência, de modo que uma linha mais cheia de sequências longas não custa linearmente mais memória.

A fila de rollout

O worker envia amostras pontuadas para uma fila e o trainer as retira dela. Quatro métricas descrevem essa única fila e respondem a perguntas diferentes:

métricapergunta
sample/rollout_queue_sizequantas amostras estão aguardando no momento
sample/time_in_queue_spor quanto tempo uma única amostra ficou ali antes de ser usada no treinamento — a metade em segundos de sua natureza off-policy
perf/rollout_wait_spor quanto tempo o treinamento ficou bloqueado porque a fila estava vazia
rollout/backpressure_spor quanto tempo a geração (o worker) ficou bloqueada porque a fila estava cheia

As duas últimas métricas (perf/rollout_wait_s e rollout/backpressure_s) são imagens espelhadas e nunca são grandes ao mesmo tempo! Lidas em conjunto com o tamanho da fila, elas indicam qual lado é o gargalo:

  • fila quase vazia, perf/rollout_wait_s alto → limitado pela geração. O treinador está com falta de dados; verifique rollout/generated_tok_s, rollout/concurrency e rollout/score_s.
  • fila quase cheia, rollout/backpressure_s alto → limitado pelo trainer. A geração está sendo limitada e sua saída está envelhecendo na fila, então observe sample/staleness_mean subir.
  • ambos próximos de zero → equilibrado!

Completions

Uma completion é o que o servidor vLLM do aluno gerou para um prompt.

métricasignificado
completions/mean_lengthtokens gerados por rollout
completions/min_length, completions/max_lengthconclusão mais curta e mais longa na janela
completions/clipped_ratiofração de conclusões que não terminaram em EOS, ou seja, foram cortadas por max_completion_length

Rollouts

Um rollout é um prompt levado até o fim: gerado pelo aluno e, em seguida, processado pelo professor com forçamento de professor. Como a chamada ao professor está no caminho crítico de cada rollout (diferentemente do GRPO, em que a pontuação é um loop separado sobre grupos concluídos), um professor lento aparece diretamente como rollout/duration_s.

métricasignificado
rollout/duration_stempo real para um rollout, do despacho até uma amostra pontuada: geração e a chamada ao professor
rollout/score_sdessa, o tempo que a chamada /v1/completions do professor levou
teacher_score_s/<id>Apenas MOPD: o mesmo, por professor. Um especialista lento limita apenas os rollouts roteados para ele, o que a média misturada oculta
rollout/generated_tok_svazão de geração no último intervalo (janela deslizante), de modo que uma parada fica visível
rollout/concurrencyrollouts em andamento, ou seja, sendo gerados ou pontuados
rollout/vllm_retry_totalsolicitações de vLLM repetidas, seja para o servidor do aluno ou para o de um professor. Um servidor degradado, caso contrário, se manifesta como lentidão inexplicada. Este item está aqui, em vez de em completions/, porque ele contabiliza solicitações a um servidor, e não o texto gerado
rollout/backpressure_spor quanto tempo a geração foi bloqueada porque a fila de rollout estava cheia. Veja a fila de rollout

Amostras chegando da fila

Cada amostra é um RolloutSample: um prompt, a conclusão do aluno e a pontuação esparsa do professor para ela. O worker as insere em rollout_buffer; tudo abaixo é medido no lado do trainer, conforme ele as extrai para o treinamento.

métricasignificado
sample/forwarded_tokens_mean, sample/forwarded_tokens_maxtokens em uma amostra: prompt + gerado. O empacotamento é uma questão de nível de linha, veja batch/row_*
sample/trained_tokens_meandessas, quantas são consideradas no cálculo da perda
sample/rollout_queue_sizeamostras pontuadas aguardando na fila
sample/time_in_queue_spor quanto tempo esta amostra ficou nessa fila antes de ser usada no treinamento. Não é o mesmo que perf/rollout_wait_s — veja a fila de rollout
sample/staleness_mean, sample/staleness_maxquantas versões de política estão atrás dos dados. jsd mostra o efeito da off-policyness na perda; isso mostra a causa
sample/dropped_stale_totalamostras descartadas por exceder max_staleness

Lotes

Um micro-lote é world_size linhas (lembre-se de que o packing aplanar em 1 sequência), então um por rank de DP, de modo que o rank i faz o forward da linha i. gradient_accumulation_steps desses formam um passo do otimizador. Um passo então cobre gradient_accumulation_steps × world_size linhas no total. Cada métrica _per_step abaixo é uma soma sobre o passo inteiro e entre todos os ranks; as métricas batch/row_* são médias sobre as linhas.

métricasignificado
batch/forwarded_tokens_per_step, batch/trained_tokens_per_steptokens em um passo do otimizador, encaminhados e treinados, respectivamente
batch/samples_per_stepamostras de treinamento por passo do otimizador
batch/microbatches_per_stepcontados, não lidos da configuração
batch/masked_token_fractokens encaminhados com completion_mask == 0 — a parte do encaminhamento que não gera gradiente
batch/samples_per_row, batch/row_tokens_mean, batch/row_tokens_maxo quão densamente o planejador empacotou a linha de cada rank
batch/row_fill_fractokens da linha em relação a token_budget. Valores baixos indicam que o orçamento não está sendo utilizado
batch/row_imbalancemax Σ Lᵢ² / mean Σ Lᵢ² entre as linhas. A atenção é O(L²), então isso prevê qual rank atrasa o all-reduce de gradientes. 1.0 é perfeito
batch/pad_fracpadding entre ranks. Custa apenas bytes de broadcast; é removido antes do forward
batch/dropped_oversize_totalamostras descartadas por exceder token_budget

A divergência

O que a própria métrica de objetivo mede, em média, sobre os tokens treinados da janela. Estes são os números a serem observados para o aprendizado, em oposição aos de throughput.

métricasignificado
jsda JSD generalizada que a perda minimiza, no beta configurado. Uma queda indica que a distribuição do aluno está convergindo para a do professor
entropya própria entropia preditiva do aluno. Um colapso aqui com uma queda em jsd indica que o aluno está estreitando, em vez de aprender
teacher_entropya entropia do professor sobre os candidatos que ele reportou. Limitada inferiormente ao valor verdadeiro, pois apenas teacher_top_k candidatos cruzam a rede
teacher_jsd/<id>Apenas MOPD: jsd restrito aos tokens pontuados pelo professor. Professores em diferentes domínios podem divergir em taxas muito diferentes, o que o jsd misturado confunde
teacher_entropy/<id>Apenas MOPD: a mesma decomposição de teacher_entropy
teacher_token_frac/<id>Apenas MOPD: a proporção de tokens pontuados que o professor assumiu. O desvio de roteamento é invisível de outra forma: um professor com falta de linhas ainda relata um teacher_jsd/<id> saudável

Não existe entropy por professor: a entropia do aluno é uma propriedade da própria política dele, não de qual professor pontuou a amostra, então a métrica combinada já a cobre.

Desempenho

Throughput mede quantos tokens o trainer processa por segundo. Model FLOPs Utilization (MFU) estima a porcentagem da capacidade teórica de computação dos dispositivos de treinamento utilizada para as passadas de forward e backward do modelo. A capacidade utilizada para essa estimativa depende do hardware e da precisão do treinamento.

O throughput e o MFU são reportados em duas bases de tempo:

  • _fwd_bwd mede o desempenho durante as passadas de forward e backward.
  • _wall_clock mede o desempenho ao longo da etapa completa do otimizador, incluindo as atualizações do otimizador, a sincronização de pesos e o tempo gasto aguardando os rollouts.

Essa diferença ajuda a distinguir o desempenho de computação do custo de espera e coordenação. Ambas as métricas de MFU descrevem apenas os dispositivos de treinamento: elas não incluem a computação ou a capacidade de hardware de servidores externos de rollout ou de teacher. A MFU de tempo real reflete o tempo que o treinador gasta aguardando esses serviços, e não a própria utilização desses serviços.

O MFU é reportado apenas quando a capacidade de computação de pico é conhecida para o dispositivo de treinamento local na precisão selecionada. Caso contrário, o MFU é omitido; o treinamento continua e as métricas de throughput e tempo permanecem disponíveis.

métricasignificado
perf/step_stempo real entre as etapas do otimizador: inclui cálculos, otimizador, sincronização de pesos e esperas na fila
perf/fwd_bwd_sforward + backward, somado sobre os micro-lotes do passo. Este é o denominador de todas as métricas _fwd_bwd abaixo
perf/fwd_sa parte de forward dele. fwd_s / fwd_bwd_s próximo de 1/3 é a divisão usual; valores maiores indicam que o backward é barato ou que o recompute está sendo pago no forward
perf/optimizer_soptimizer.step()
perf/rollout_wait_spor quanto tempo o trainer ficou bloqueado porque a fila estava vazia. Veja a fila de rollout
perf/weight_sync_sum sincronização completa, além de _pause_s (aguardando o vLLM), _barrier_s (desvio de rank) e _transfer_s (os bytes)
perf/forwarded_tok_s_fwd_bwd, perf/forwarded_tok_s_wall_clocktokens encaminhados por segundo em cada base
perf/trained_tok_s_wall_clocko mesmo, contando apenas os tokens que a perda viu
perf/mfu_fwd_bwd, perf/mfu_wall_clockutilização de FLOPs do modelo em cada base

Filosofia de design

Este trainer é mantido intencionalmente minimalista e não foi projetado para se tornar uma solução de uso geral. Se você precisar de um recurso que não é suportado, recomendamos clonar o repositório e adaptar o trainer diretamente às suas necessidades. Novos recursos só serão considerados quando houver demanda significativa da comunidade.

AsyncDistillationConfig

classe trl.experimental.async_distillation.AsyncDistillationConfig

< >

( output_dir: str | None = Noneper_device_train_batch_size: int = 8num_train_epochs: float = 3.0max_steps: int = -1learning_rate: float = 1e-06lr_scheduler_type: transformers.trainer_utils.SchedulerType | str = 'linear'lr_scheduler_kwargs: dict | str | None = Nonewarmup_steps: float = 0optim: transformers.training_args.OptimizerNames | str = 'adamw_torch_fused'optim_args: str | None = Noneweight_decay: float = 0.0adam_beta1: float = 0.9adam_beta2: float = 0.999adam_epsilon: float = 1e-08optim_target_modules: None | str | list[str] = Nonegradient_accumulation_steps: int = 1average_tokens_across_devices: bool = Truemax_grad_norm: float = 1.0label_smoothing_factor: float = 0.0bf16: bool | None = Nonefp16: bool = Falsebf16_full_eval: bool = Falsefp16_full_eval: bool = Falsetf32: bool | None = Nonegradient_checkpointing: bool = Truegradient_checkpointing_kwargs: dict[str, typing.Any] | str | None = Nonetorch_compile: bool = Falsetorch_compile_backend: str | None = Nonetorch_compile_mode: str | None = Noneuse_liger_kernel: bool = Falseliger_kernel_config: dict[str, bool] | None = Noneuse_cache: bool = Falseneftune_noise_alpha: float | None = Nonetorch_empty_cache_steps: int | None = Noneauto_find_batch_size: bool = Falselogging_strategy: transformers.trainer_utils.IntervalStrategy | str = 'steps'logging_steps: float = 1logging_first_step: bool = Falselog_on_each_node: bool = Truelogging_nan_inf_filter: bool = Trueinclude_num_input_tokens_seen: str | bool = 'no'log_level: str = 'passive'log_level_replica: str = 'warning'disable_tqdm: bool | None = Nonereport_to: None | str | list[str] = 'none'run_name: str | None = Noneproject: str = 'huggingface'trackio_space_id: str | None = Nonetrackio_bucket_id: str | None = Nonetrackio_static_space_id: typing.Union[str, NoneType, typing.Literal[False]] = Noneeval_strategy: transformers.trainer_utils.IntervalStrategy | str = 'no'eval_steps: float | None = Noneeval_delay: float = 0per_device_eval_batch_size: int = 8prediction_loss_only: bool = Falseeval_on_start: bool = Falseeval_do_concat_batches: bool = Trueeval_use_gather_object: bool = Falseeval_accumulation_steps: int | None = Noneinclude_for_metrics: list[str] = <factory>batch_eval_metrics: bool = Falsesave_only_model: bool = Falsesave_strategy: transformers.trainer_utils.SaveStrategy | str = 'steps'save_steps: float = 500save_on_each_node: bool = Falsesave_total_limit: int | None = Noneenable_jit_checkpoint: bool = Falsepush_to_hub: bool = Falsehub_token: str | None = Nonehub_private_repo: bool | None = Nonehub_model_id: str | None = Nonehub_strategy: transformers.trainer_utils.HubStrategy | str = 'every_save'hub_always_push: bool = Falsehub_revision: str | None = Noneload_best_model_at_end: bool = Falsemetric_for_best_model: str | None = Nonegreater_is_better: bool | None = Noneignore_data_skip: bool = Truerestore_callback_states_from_checkpoint: bool = Falsefull_determinism: bool = Falseseed: int = 42data_seed: int | None = Noneuse_cpu: bool = Falseaccelerator_config: dict | str | None = Noneparallelism_config: accelerate.parallelism_config.ParallelismConfig | None = Nonedataloader_drop_last: bool = Falsedataloader_num_workers: int = 0dataloader_pin_memory: bool = Truedataloader_persistent_workers: bool = Falsedataloader_prefetch_factor: int | None = Nonedataloader_multiprocessing_context: str | None = Nonedataloader_in_order: bool = Trueremove_unused_columns: bool = Truelabel_names: list[str] | None = Nonetrain_sampling_strategy: str = 'random'length_column_name: str = 'length'ddp_find_unused_parameters: bool | None = Noneddp_bucket_cap_mb: int | None = Noneddp_broadcast_buffers: bool | None = Noneddp_static_graph: bool | None = Noneddp_backend: str | None = Noneddp_timeout: int = 1800fsdp: str | None = Nonefsdp_config: dict[str, typing.Any] | str | None = Nonedeepspeed: dict | str | None = Nonedebug: str | list[transformers.debug_utils.DebugOption] = ''skip_memory_metrics: bool = Truedo_train: bool = Falsedo_eval: bool = Falsedo_predict: bool = Falseresume_from_checkpoint: str | None = Nonelocal_rank: int = -1model_init_kwargs: dict[str, typing.Any] | str | None = Nonedtype: str = 'float32'trust_remote_code: bool = Falserouter_aux_loss_coef: float | None = Nonemax_completion_length: int = 2048temperature: float = 1.0top_p: float = 1.0top_k: int = 0min_p: float | None = Nonerepetition_penalty: float = 1.0chat_template_kwargs: dict | str | None = Nonevllm_server_base_url: str = 'http://localhost:8000'vllm_server_timeout: float = 1800.0teacher_server_urls: dict[str, str] | str | None = Nonebeta: float = 0.0teacher_temperature: float = 1.0teacher_top_k: int = 8add_tail_bucket: bool = Truetoken_budget: int | None = Nonemax_concurrent_rollouts: int | None = Nonemax_staleness: int = 4queue_maxsize: int = 1024weight_sync_steps: int = 1rollout_worker_timeout: float = 300.0log_completions: bool = Falselog_completions_steps: int = 100num_completions_to_print: int | None = None )

Parâmetros que controlam o modelo

  • model_init_kwargs (dict[str, Any] ou str, opcional) — Argumentos de palavra-chave para from_pretrained, usados ao instanciar o modelo aluno a partir de um caminho. O valor de revision também é usado ao carregar a classe de processamento.
  • dtype (str, opcional, padrão é "float32") — Tipo de dados para carregar o modelo student, um dos "auto", "bfloat16", "float16" ou "float32". O padrão é "float32" porque a divergência entre treinamento e inferência, contra a qual os treinadores assíncronos são medidos (Defeating the Training-Inference Mismatch via FP16, explicada para eles em Defeating the trainer-generator precision mismatch in TRL) ) é sensível à própria precisão do treinador. Fechar essa lacuna de ponta a ponta também exige servir o servidor vLLM do student no mesmo dtype (vllm serve --dtype). Um dtypevllm serve --dtype). A dtype em model_init_kwargs tem precedência. Os servidores de teacher não são afetados: eles nunca são atualizados.

    Isso apenas define o dtype em que os pesos são carregados. Sob FSDP com precisão mista (bf16=True é o padrão), o accelerate faz o upcast de todos os parâmetros treináveis para fp32 antes do sharding, de modo que os pesos mestres e o estado do otimizador ficam em fp32, independentemente do que dtype indique, enquanto o forward e o backward são executados em bf16 ou fp16. Os checkpoints são então gravados nesse dtype de precisão mista, e não em fp32. Sem FSDP, nada faz upcast: dtype="bfloat16" com bf16=True mantém pesos e estados do otimizador em bf16, o que corresponde a treinamento puro em bf16.

  • trust_remote_code (bool, opcional, padrão False) — Se permite carregar modelos e tokenizers que incluem código Python personalizado do Hub. Encaminhado para from_pretrained e from_pretrained.
  • router_aux_loss_coef (float, opcional) — Coeficiente da perda auxiliar de balanceamento de carga para modelos Mixture-of-Experts (MoE), adicionada à perda de treinamento com esse peso. Quando não definido, o valor declarado na configuração do modelo é utilizado. Defina como 0.0 para desativá-la. Falha quando usado com um modelo não-MoE.

Parâmetros que controlam a geração

  • max_completion_length (int, opcional, padrão 2048) — Número máximo de tokens a serem gerados por conclusão.
  • temperature (float, opcional, padrão 1.0) — Temperatura para amostragem das conclusões on-policy do aluno.
  • top_p (float, opcional, padrão 1.0) — Parâmetro de amostragem Top-p (núcleo) para geração on-policy.
  • top_k (int, opcional, padrão 0) — Parâmetro de amostragem top-k para geração on-policy. 0 desativa o filtro top-k.
  • min_p (float, opcional) — Probabilidade mínima do token, que será escalada pela probabilidade do token mais provável. Deve ser um valor entre 0.0 e 1.0. Valores típicos estão na faixa de 0.01-0.2.
  • repetition_penalty (float, opcional, padrão 1.0) — Float que penaliza novos tokens com base em sua presença no prompt e no texto gerado até o momento. Valores > 1.0 incentivam o modelo a usar novos tokens, enquanto valores < 1.0 incentivam o modelo a repetir tokens.
  • chat_template_kwargs (dict[str, Any], opcional) — Argumentos de palavra-chave adicionais para passar à função apply_chat_template ao gerar completions.

Parâmetros que controlam os servidores vLLM

  • vllm_server_base_url (str, opcional, padrão "http --//localhost:8000"): URL base do servidor vLLM do aluno, usada tanto para geração quanto para o streaming de atualizações de pesos.
  • vllm_server_timeout (float, opcional, padrão 1800.0) — Tempo limite, em segundos, para chamadas aos servidores vLLM: aguardar o servidor do aluno ficar pronto na inicialização, cada solicitação de geração ou pontuação e cada chamada de sincronização de pesos. Aguardar o servidor e as chamadas de sincronização de pesos geram erro quando o tempo limite é excedido; uma solicitação de geração ou pontuação que exceder o tempo limite é reprocessada.
  • teacher_server_urls (dict[str, str], opcional, padrão {"default" -- "http://localhost:8001"}): Servidor(es) vLLM do teacher, cada um iniciado com vllm serve <teacher-model> --logprobs-mode processed_logprobs --max-logprobs -1. Cada teacher é estático: este trainer nunca transmite atualizações de pesos para nenhum deles, portanto um teacher não precisa de VLLM_SERVER_DEV_MODE nem de um backend de transferência de pesos, apenas dessas duas flags, que tornam sua pontuação exata (veja teacher_temperature e teacher_top_k). Todos os teachers devem compartilhar o tokenizer do student: as conclusões são enviadas como ids de tokens brutos, e os ids que um teacher reporta de volta são usados diretamente para indexar o próprio vocabulário do student em compute_loss. Um teacher com um vocabulário diferente treina contra os tokens errados, silenciosamente se seu vocabulário não for maior que o do student. A pontuação é uma solicitação teacher-forced contra os próprios tokens de conclusão do student (max_tokens=1, prompt_logprobs=teacher_top_k, temperature=teacher_temperature), a mesma solicitação que get_sequence_logprobs() emite para os trainers de teacher-servidor síncronos. Devido a esse token descartável, um teacher precisa de um --max-model-len maior que o do student, ou as conclusões mais longas não podem ser pontuadas. Uma única entrada pontua todas as amostras (destilação on-policy de teacher único simples, o padrão). Múltiplas entradas habilitam MOPD (destilação on-policy de múltiplos teachers, veja MOPD): a coluna teacher_id de cada linha de treinamento seleciona qual entrada a pontua, por exemplo {"math": "http://localhost:8002", "code": "http://localhost:8003"} com um teacher_id de "math" ou "code" por linha.

Parâmetros que controlam a perda de destilação

  • beta (float, opcional, padrão 0.0) — Coeficiente de interpolação para a Divergência Generalizada de Jensen-Shannon. 0.0 é KL forward (mean-seeking), 1.0 é KL reverse (mode-seeking), e valores intermediários interpolam, seguindo a mesma formulação de JSD generalizada que o DistillationTrainer calcula internamente. O suporte sobre o qual a divergência é calculada varia conforme o regime, espelhando o ServerDistillationTrainer: em beta=0.0, é utilizado o suporte completo reportado pelo teacher, de largura teacher_top_k (mais o bucket de cauda), pois o peso do KL forward é exatamente o que esse suporte fornece. Em beta != 0.0, o suporte é reduzido para apenas dois candidatos — o token top-1 do próprio teacher e o token real/realizado da completion — pois esses são os únicos dois tokens para os quais o protocolo de rede garante um logprob do teacher sem transmitir um vocabulário mais amplo (ou completo); qualquer coisa mais ampla seria apenas uma aproximação probabilística de cobrir os tokens prováveis do próprio student, não uma garantia.
  • teacher_temperature (float, opcional, padrão 1.0) — Temperatura do softmax da divergência, aplicada aos dois lados: enviada ao teacher para que o vLLM calcule seus logprobs nessa temperatura no servidor (exato, não um reescalonamento no cliente) e aplicada aos próprios logits do student em compute_loss, espelhando a única temperature do ServerDistillationTrainer. Não tem relação com temperature, que controla apenas como o student amostra suas conclusões. O servidor do teacher deve ser executado com --logprobs-mode processed_logprobs para que isso afete os logprobs retornados; sem isso, o teacher relata silenciosamente logprobs brutos e essa configuração afeta apenas o lado do student.
  • teacher_top_k (int, opcional, padrão 8) — Número de tokens candidatos por posição solicitados ao teacher via prompt_logprobs. Apenas esses candidatos (além do token realizado, que o vLLM sempre relata mesmo quando está fora do top-k, além de um bucket de cauda que captura a massa de probabilidade restante, veja add_tail_bucket) são usados para aproximar a distribuição do teacher; o vocabulário completo nunca é transmitido via HTTP. O lado do student da divergência é exato (calculado localmente, não aproximado), pois o student é o modelo em treinamento e seus logits completos já estão disponíveis em compute_loss. 8 é um padrão leve para testes de fumaça; configurações de destilação on-policy em produção em frameworks de RL adjacentes usam um suporte esparsos do teacher em aproximadamente a mesma faixa (miles usa 16 por padrão, EasyOPD usa 64), portanto aumentar esse valor para 16–64 é razoável quando o treinamento for de fato iniciado. Qualquer valor acima de 20 exige que o servidor do teacher tenha sido iniciado com --max-logprobs -1, o que remove o limite padrão de logprob por token do vLLM.
  • add_tail_bucket (bool, opcional, padrão True) — Se deve adicionar um bucket de cauda representando a massa de probabilidade restante fora de teacher_top_k, para evitar uma divergência trivialmente pequena quando teacher_top_k é pequeno.
  • token_budget (int, opcional) — Número máximo de tokens reais empacotados em uma única linha (um forward de um rank DP). Um TokenBudgetBatcher forma micro-lotes equilibrados por Σ Lᵢ² cujas linhas permanecem dentro desse orçamento, limitando a memória de pico independentemente da contagem de amostras (o número de amostras por linha se torna dinâmico). Se None (padrão), é definido como o max_model_len do servidor vLLM do aluno (consultado no início do treinamento) — o limite para o comprimento de prompt + completude — de modo que nenhuma amostra de rollout possa exceder o orçamento. Uma amostra mais longa que token_budget não cabe em nenhuma linha e é descartada com um aviso, contabilizada como batch/dropped_oversize_total.

Parâmetros que controlam o pipeline de rollout assíncrono

  • max_concurrent_rollouts (int, opcional) — Número máximo de rollouts em andamento simultaneamente nos servidores vLLM do aluno e dos professores, seja gerando ou sendo pontuados. Se None, é definido como max(max_staleness, 1) * per_device_train_batch_size * gradient_accumulation_steps * num_processes.
  • max_staleness (int, opcional, padrão 4) — Número máximo de etapas de atualização de pesos que uma amostra de rollout pode ficar atrás da versão atual do modelo antes de ser descartada.
  • queue_maxsize (int, opcional, padrão 1024) — Número máximo de amostras de rollout a serem armazenadas em buffer na fila de rollout.
  • weight_sync_steps (int, opcional, padrão 1) — Número de etapas de treinamento entre as sincronizações de pesos para o servidor vLLM do aluno.
  • rollout_worker_timeout (float, opcional, padrão 300.0) — Segundos desde o último heartbeat do worker de rollout após os quais o trainer o considera travado e interrompe a execução.

Parâmetros que controlam o registro de logs

  • log_completions (bool, opcional, padrão False) — Se deve registrar uma amostra de pares (prompt, completion) a cada log_completions_steps amostras pontuadas.
  • log_completions_steps (int, opcional, padrão 100) — Número de amostras pontuadas entre os registros de completions. Usado apenas se log_completions for True. Contado em amostras que o worker de rollout pontuou, não em passos do otimizador: o worker roda em um processo separado do trainer e não tem visibilidade sobre self.state.global_step.
  • num_completions_to_print (int, opcional) — Número de conclusões a serem exibidas com rich. Se None, todas as conclusões são registradas.

Classe de configuração para o experimental.async_distillation.AsyncDistillationTrainer.

Esta classe inclui apenas os parâmetros específicos para a destilação on-policy assíncrona. Para a lista completa de argumentos de treinamento, consulte a documentação de TrainingArguments. Observe que os valores padrão nesta classe podem diferir daqueles em TrainingArguments. Sua estrutura espelha a de AsyncGRPOConfig (pipeline assíncrono, servidor vLLM e campos de log são os mesmos), com os campos de clipping/grupo do GRPO substituídos pelos campos de perda de destilação do professor abaixo.

Esses parâmetros possuem valores padrão diferentes de TrainingArguments:

  • logging_steps: O valor padrão é 1 em vez de 500.
  • gradient_checkpointing: O padrão é True em vez de False.
  • bf16: Padrão é True se fp16 não estiver definido, em vez de False.
  • learning_rate: O valor padrão é 1e-6 em vez de 5e-5.
  • ignore_data_skip: O valor padrão é True em vez de False; o loop de skip-and-replay do Trainer base não se aplica à fila de rollout assíncrono.

AsyncDistillationTrainer

classe trl.experimental.async_distillation.AsyncDistillationTrainer

< >

( model: str | PreTrainedModelargs: trl.experimental.async_distillation.async_distillation_config.AsyncDistillationConfig | None = Nonetrain_dataset: datasets.arrow_dataset.Dataset | datasets.iterable_dataset.IterableDataset | None = Noneprocessing_class: transformers.tokenization_utils_base.PreTrainedTokenizerBase | None = Nonecallbacks: list[transformers.trainer_callback.TrainerCallback] | None = Noneoptimizers: tuple[typing.Optional[torch.optim.Optimizer], typing.Optional[torch.optim.lr_scheduler.LambdaLR]] = (None, None)peft_config: PeftConfig | None = None )

Parâmetros

  • model (str ou PreTrainedModel) — Modelo de aluno a ser treinado. Pode ser:
    • Uma string, sendo o model id de um modelo pré-treinado hospedado em um repositório de modelos no huggingface.co, ou um caminho para um directory contendo pesos de modelo salvos usando save_pretrained. Carregado com from_pretrained.
    • Um objeto PreTrainedModel. Sua implementação de atenção é alterada para o backend FlashAttention do trainer para treinamento sem padding.

    O nome do modelo na configuração é usado para identificar o modelo aluno em seu servidor vLLM.

  • args (experimental.async_distillation.AsyncDistillationConfig, opcional) — Configuração para este trainer. Se None, uma configuração padrão é usada.
  • train_dataset (Dataset ou IterableDataset) — Dataset a ser usado para o treinamento. Deve incluir uma coluna "prompt" (formato conversacional). Colunas adicionais são ignoradas.
  • processing_class (PreTrainedTokenizerBase, opcional) — Classe de processamento usada para processar os dados. Se for None, ela é carregada a partir do nome do modelo com from_pretrained. Se não tiver um token de padding, tokenizer.eos_token é usado.
  • callbacks (lista de TrainerCallback, opcional) — Lista de callbacks para personalizar o loop de treinamento, adicionados aos callbacks padrão (veja aqui).
  • optimizers (tuple[torch.optim.Optimizer | None, torch.optim.lr_scheduler.LambdaLR | None], opcional, padrão (None, None)) — Uma tupla contendo o otimizador e o agendador a serem usados. O padrão é AdamW e um agendamento linear controlado por args.
  • peft_config (PeftConfig, opcional) — Configuração PEFT usada para envolver o modelo. Se for None, o modelo não é envolvido. Quando o servidor vLLM do aluno foi iniciado com --enable-lora, um adaptador LoRA é sincronizado com ele como um adaptador, em vez de como pesos mesclados; consulte a seção LoRA da documentação do AsyncDistillation.

Contraparte assíncrona de DistillationTrainer, arquitetada exatamente como AsyncGRPOTrainer: um worker de rollout em segundo plano gera as conclusões on-policy do aluno e as pontua em relação a um servidor de professor enquanto o treinamento prossegue de forma concorrente, desacoplando o rollout do loop de atualização de gradiente. Onde a perda de gradiente de política com clipping do GRPO lê uma vantagem relativa ao grupo, o compute_loss deste trainer lê uma distribuição de professor esparsa por posição e minimiza um JSD generalizado em relação a ela (veja AsyncDistillationConfig.beta), sempre on-policy: o aluno gera cada conclusão em que ele treina.

Diferentemente do DistillationTrainer síncrono, este trainer suporta apenas a pontuação contra um teacher servido via HTTP (um servidor vLLM externo). Uma passagem de forward de um teacher local (in-process, GPU) não pode ser executada dentro do processo filho gerado pelo worker de rollout, que tem CUDA desabilitado, da mesma forma que as funções de recompensa do GRPO podem, e precisaria, em vez disso, ser executada de volta no processo principal de treinamento; esse caminho ainda não foi implementado.

Exemplo:

>>> from trl.experimental.async_distillation import AsyncDistillationTrainer
>>> from datasets import load_dataset

>>> dataset = load_dataset("trl-lib/DeepMath-103K", split="train")

>>> trainer = AsyncDistillationTrainer(
...     model="Qwen/Qwen2.5-0.5B-Instruct",
...     train_dataset=dataset,
... )
>>> trainer.train()
Update on GitHub