Accelerate documentation
Solução de problemas
Solução de problemas
Este guia fornece soluções para alguns problemas que você pode encontrar ao usar o Accelerate. Nem todos os erros são cobertos, pois o Accelerate é uma biblioteca ativa que está em constante evolução e existem muitos casos de uso diferentes e configurações de treinamento distribuído. Se as soluções descritas aqui não ajudarem com o seu erro específico, consulte a seção Peça ajuda para saber onde e como obter ajuda.
Registro
O registro de logs pode ajudar a identificar de onde vem um erro. Em uma configuração distribuída com múltiplos processos, o registro de logs pode ser um desafio, mas o Accelerate fornece a utilidade logging() para garantir que os logs sejam sincronizados.
Para solucionar um problema, use logging() em vez do módulo padrão de logging do Python. Defina o nível de verbosidade (INFO, DEBUG, WARNING, ERROR, CRITICAL) com o parâmetro log_level e, em seguida, você pode:
- Exporte o
log_levelcomo a variável de ambienteACCELERATE_LOG_LEVEL. - Passe o
log_leveldiretamente paraget_logger.
Por exemplo, para definir log_level="INFO":
from accelerate.logging import get_logger
logger = get_logger(__name__, log_level="DEBUG")Por padrão, o log é chamado apenas nos processos principais. Para chamá-lo em todos os processos, passe main_process_only=False.
Se o log deve ser chamado em todos os processos e em ordem, passe também in_order=True.
from accelerate.logging import get_logger
logger = get_logger(__name__, log_level="DEBUG")
# log all processes
logger.debug("thing_to_log", main_process_only=False)
# log all processes in order
logger.debug("thing_to_log", main_process_only=False, in_order=True)Código pendurado e erros de tempo limite
Existem muitas razões pelas quais seu código pode estar travando. Vamos ver como resolver alguns dos problemas mais comuns que podem causar o travamento do seu código.
Formas de tensor incompatíveis
Formas de tensor incompatíveis são um problema comum que pode fazer com que seu código fique travado por um tempo significativo em um ambiente distribuído.
Ao executar scripts em um ambiente distribuído, funções como Accelerator.gather() e Accelerator.reduce() são necessárias para obter tensors entre dispositivos e realizar operações neles de forma coletiva. Essas (e outras) funções dependem de torch.distributed para executar uma operação de gather, que exige que os tensors tenham a mesma forma exata em todos os processos. Quando as formas dos tensors não correspondem, seu código fica travado e, eventualmente, você encontrará uma exceção de timeout.
Você pode usar o modo de depuração operacional do Accelerate para detectar esse problema imediatamente. Recomendamos habilitar esse modo durante a configuração do accelerate config, mas você também pode ativá-lo pela CLI, como variável de ambiente ou editando manualmente o arquivo config.yaml.
accelerate launch --debug {my_script.py} --arg1 --arg2Se você ativar o modo de depuração como uma variável de ambiente, não precisa chamar accelerate launch.
ACCELERATE_DEBUG_MODE="1" torchrun {my_script.py} --arg1 --arg2Adicione debug: true ao seu arquivo config.yaml.
compute_environment: LOCAL_MACHINE
debug: trueDepois de ativar o modo de depuração, você deve receber um traceback que aponta para o problema de incompatibilidade de forma do tensor.
Traceback (most recent call last):
File "/home/zach_mueller_huggingface_co/test.py", line 18, in <module>
main()
File "/home/zach_mueller_huggingface_co/test.py", line 15, in main
broadcast_tensor = broadcast(tensor)
File "/home/zach_mueller_huggingface_co/accelerate/src/accelerate/utils/operations.py", line 303, in wrapper
accelerate.utils.operations.DistributedOperationException:
Cannot apply desired operation due to shape mismatches. All shapes across devices must be valid.
Operation: `accelerate.utils.operations.broadcast`
Input shapes:
- Process 0: [1, 5]
- Process 1: [1, 2, 5]Parada antecipada
Para o early stopping em treinamento distribuído, se cada processo tiver uma condição de parada específica (por exemplo, a perda de validação), ela pode não estar sincronizada entre todos os processos. Como resultado, um break pode ocorrer no processo 0, mas não no processo 1, o que fará com que seu código fique pendurado indefinidamente até que um timeout ocorra.
Se você tiver condições de parada antecipada, use os métodos set_trigger e check_trigger para garantir que todos os processos sejam encerrados corretamente.
# Assume `should_do_breakpoint` is a custom-defined function that returns a conditional,
# and that conditional might be true only on process 1
if should_do_breakpoint(loss):
accelerator.set_trigger()
# Later in the training script when we need to check for the breakpoint
if accelerator.check_trigger():
breakVersões baixas do kernel no Linux
No Linux com versão do kernel < 5.5, processos travados foram relatados. Para evitar esse problema, atualize seu sistema para uma versão posterior do kernel.
MPI
Se o seu trabalho de treinamento distribuído de CPU usando MPI estiver travando, verifique se você configurou SSH sem senha (usando chaves) entre os nós. Isso significa que, para todos os nós no seu hostfile, você deve ser capaz de fazer SSH de um nó para outro sem ser solicitado a uma senha.
Em seguida, tente executar o comando mpirun como uma verificação de sanidade. Por exemplo, o comando abaixo deve exibir os nomes dos hosts para cada um dos nós.
mpirun -f hostfile -n {number of nodes} -ppn 1 hostnameMemória insuficiente
Um dos erros mais frustrantes ao executar scripts de treinamento é encontrar “Out-of-Memory” em dispositivos como CUDA, XPU ou CPU. O script inteiro precisa ser reiniciado e todo o progresso é perdido.
Para resolver esse problema, o Accelerate oferece a utilidade find_executable_batch_size(), que é fortemente baseada no toma. Essa utilidade refaz a execução de código que falha devido a condições de OOM (out-of-memory) e reduz automaticamente os tamanhos de lote. Para cada condição de OOM, o algoritmo diminui o tamanho do lote pela metade e refaz a execução do código até que ele tenha sucesso.
Para usar find_executable_batch_size(), reestruture sua função de treinamento para incluir uma função interna com find_executable_batch_size e crie seus dataloaders dentro dela. No mínimo, isso exige apenas 4 novas linhas de código.
A função interna deve receber o tamanho do lote como primeiro parâmetro, mas não passamos um para ela quando é chamada. O wrapper cuidará disso para você. Qualquer objeto (modelos, otimizadores) que consuma memória de dispositivo e seja passado para o Accelerator também deve ser declarado dentro da função interna.
def training_function(args):
accelerator = Accelerator()
+ @find_executable_batch_size(starting_batch_size=args.batch_size)
+ def inner_training_loop(batch_size):
+ nonlocal accelerator # Ensure they can be used in our context
+ accelerator.free_memory() # Free all lingering references
model = get_model()
model.to(accelerator.device)
optimizer = get_optimizer()
train_dataloader, eval_dataloader = get_dataloaders(accelerator, batch_size)
lr_scheduler = get_scheduler(
optimizer,
num_training_steps=len(train_dataloader)*num_epochs
)
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
)
train(model, optimizer, train_dataloader, lr_scheduler)
validate(model, eval_dataloader)
+ inner_training_loop()Resultados não reproduzíveis entre configurações de dispositivos
Se você alterou a configuração de dispositivos e observa um desempenho diferente do modelo, é provável que você não tenha atualizado seu script ao migrar de uma configuração para outra. Mesmo que você esteja usando o mesmo script com o mesmo tamanho de lote, os resultados ainda serão diferentes em um TPU, em multi-GPU e em GPU única.
Por exemplo, se você estava treinando em uma única GPU com um tamanho de lote de 16 e passa para uma configuração com duas GPUs, precisa alterar o tamanho do lote para 8 para manter o mesmo tamanho de lote efetivo. Isso ocorre porque, ao treinar com Accelerate, o tamanho do lote passado para o dataloader é o tamanho do lote por GPU.
Para garantir que você possa reproduzir os resultados entre as configurações, use a mesma semente, ajuste o tamanho do lote de acordo e considere escalar a taxa de aprendizado.
Para mais detalhes e uma referência rápida sobre tamanhos de lote, consulte o guia Comparando o desempenho entre diferentes configurações de dispositivos.
Problemas de desempenho em GPUs diferentes
Se sua configuração multi-GPU consiste em GPUs diferentes, você pode encontrar alguns problemas de desempenho:
- Pode haver um desequilíbrio na memória de GPU entre as GPUs. Nesse caso, a GPU com menor memória limitará o tamanho do lote ou o tamanho do modelo que pode ser carregado nas GPUs.
- Se você estiver usando GPUs com perfis de desempenho diferentes, o desempenho será determinado pela GPU mais lenta que você estiver usando, pois as outras GPUs terão que esperar que ela conclua sua carga de trabalho.
GPUs muito diferentes dentro do mesmo setup podem levar a gargalos de desempenho.
Peça ajuda
Se nenhuma das soluções e conselhos aqui ajudou a resolver seu problema, você sempre pode procurar ajuda na comunidade e na equipe do Accelerate.
Peça ajuda nos fóruns do Hugging Face publicando sua pergunta na categoria Accelerate. Certifique-se de escrever uma postagem descritiva com o contexto relevante sobre sua configuração e código reproduzível para maximizar a probabilidade de que seu problema seja resolvido!
Faça uma pergunta no Discord e deixe que a equipe e a comunidade te ajudem.
Crie uma Issue no repositório GitHub do Accelerate se você acredita ter encontrado um bug relacionado à biblioteca. Inclua o contexto sobre o bug e detalhes sobre sua configuração distribuída para nos ajudar a entender melhor o que está errado e como podemos corrigi-lo.