# Depuração

A depuração de problemas em treinamento distribuído geralmente se enquadra em uma dessas categorias: problemas numéricos, falhas de comunicação, erros de tempo de execução e erros de compilação.

## Detecção de subfluxo e estouro

Underflow e overflow ocorrem quando as ativações ou os pesos atingem `inf` ou `nan`, ou quando `loss=NaN`. Para detectar esses casos, ative o módulo `DebugUnderflowOverflow` em `TrainingArguments.debug()`, ou importe-o e adicione-o ao seu próprio loop de treinamento.

```py
from transformers import TrainingArguments

args = TrainingArguments(
    debug="underflow_overflow",
    ...
)
```

```py
from transformers.debug_utils import DebugUnderflowOverflow

debug_overflow = DebugUnderflowOverflow(model)
```

[DebugUnderflowOverflow](/docs/transformers/main/pt/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow) insere hooks no modelo para testar as variáveis de entrada e saída e os pesos correspondentes do modelo após cada chamada de forward. Quando `inf` ou `nan` é detectado em pelo menos um elemento das ativações ou dos pesos, o módulo imprime um relatório como o mostrado abaixo.

O exemplo abaixo é para treinamento com precisão mista fp16 usando [google/mt5-small](https://huggingface.co/google/mt5-small).

```shell
Detected inf/nan during batch_number=0
Last 21 forward frames:
abs min  abs max  metadata
                  encoder.block.1.layer.1.DenseReluDense.dropout Dropout
0.00e+00 2.57e+02 input[0]
0.00e+00 2.85e+02 output
[...]
                  encoder.block.2.layer.0 T5LayerSelfAttention
6.78e-04 3.15e+03 input[0]
2.65e-04 3.42e+03 output[0]
             None output[1]
2.25e-01 1.00e+04 output[2]
                  encoder.block.2.layer.1.layer_norm T5LayerNorm
8.69e-02 4.18e-01 weight
2.65e-04 3.42e+03 input[0]
1.79e-06 4.65e+00 output
                  encoder.block.2.layer.1.DenseReluDense.wi_0 Linear
2.17e-07 4.50e+00 weight
1.79e-06 4.65e+00 input[0]
2.68e-06 3.70e+01 output
                  encoder.block.2.layer.1.DenseReluDense.wi_1 Linear
8.08e-07 2.66e+01 weight
1.79e-06 4.65e+00 input[0]
1.27e-04 2.37e+02 output
                  encoder.block.2.layer.1.DenseReluDense.dropout Dropout
0.00e+00 8.76e+03 input[0]
0.00e+00 9.74e+03 output
                  encoder.block.2.layer.1.DenseReluDense.wo Linear
1.01e-06 6.44e+00 weight
0.00e+00 9.74e+03 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.DenseReluDense T5DenseGatedGeluDense
1.79e-06 4.65e+00 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.dropout Dropout
3.18e-04 6.27e+04 input[0]
0.00e+00      inf output
```

A primeira linha mostra o número do lote em que o erro ocorreu. Neste caso, ocorreu no lote 0.

Cada quadro descreve o módulo sobre o qual ele relata. Por exemplo, o quadro abaixo relata sobre `encoder.block.2.layer.1.layer_norm`, a normalização de camada na primeira camada do segundo bloco do codificador. As chamadas de forward são para `T5LayerNorm`.

```shell
                  encoder.block.2.layer.1.layer_norm T5LayerNorm
8.69e-02 4.18e-01 weight
2.65e-04 3.42e+03 input[0]
1.79e-06 4.65e+00 output
```

O último quadro relata a função `Dropout.forward`, que chama o atributo `dropout` dentro da classe `DenseReluDense`. O estouro (`inf`) ocorreu no segundo bloco do codificador no primeiro lote. O maior elemento de entrada foi 6.27e+04.

```shell
                  encoder.block.2.layer.1.DenseReluDense T5DenseGatedGeluDense
1.79e-06 4.65e+00 input[0]
3.18e-04 6.27e+04 output
                  encoder.block.2.layer.1.dropout Dropout
3.18e-04 6.27e+04 input[0]
0.00e+00      inf output
```

As ativações de saída de `T5DenseGatedGeluDense.forward` atingiram um máximo de 6.27e+04, que está próximo do máximo do fp16, de 6.4e+04. No próximo passo, o `Dropout` renormaliza os pesos após zerar alguns elementos, fazendo com que o máximo ultrapasse 6.4e+04 e causando o estouro.

Agora que você sabe onde o erro está ocorrendo, investigue o código de modelagem em [modeling_t5.py](https://github.com/huggingface/transformers/blob/main/src/transformers/models/t5/modeling_t5.py).

```py
class T5DenseGatedGeluDense(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.wi_0 = nn.Linear(config.d_model, config.d_ff, bias=False)
        self.wi_1 = nn.Linear(config.d_model, config.d_ff, bias=False)
        self.wo = nn.Linear(config.d_ff, config.d_model, bias=False)
        self.dropout = nn.Dropout(config.dropout_rate)
        self.gelu_act = ACT2FN["gelu_new"]

    def forward(self, hidden_states):
        hidden_gelu = self.gelu_act(self.wi_0(hidden_states))
        hidden_linear = self.wi_1(hidden_states)
        hidden_states = hidden_gelu * hidden_linear
        hidden_states = self.dropout(hidden_states)
        hidden_states = self.wo(hidden_states)
        return hidden_states
```

Uma solução é alternar para fp32 alguns passos antes de os valores ficarem grandes demais, para que os números não transbordem ao serem multiplicados ou somados. Outra opção é desativar temporariamente o treinamento com precisão mista (`amp`).

```py
import torch

def forward(self, hidden_states):
    device_type = hidden_states.device.type
    if torch.is_autocast_enabled(device_type):
        with torch.amp.autocast(device_type, enabled=False):
            return self._forward(hidden_states)
    else:
        return self._forward(hidden_states)
```

O relatório cobre apenas as entradas e saídas de quadros completos. Para analisar valores intermediários dentro de qualquer função `forward`, adicione `detect_overflow` após cada chamada de forward para rastrear `inf` ou `nan` em `forwarded_states`.

```py
from transformers.debug_utils import detect_overflow

class T5LayerFF(nn.Module):
    [...]

    def forward(self, hidden_states):
        forwarded_states = self.layer_norm(hidden_states)
        detect_overflow(forwarded_states, "after layer_norm")
        forwarded_states = self.DenseReluDense(forwarded_states)
        detect_overflow(forwarded_states, "after DenseReluDense")
        return hidden_states + self.dropout(forwarded_states)
```

Configure o número de quadros impressos por [DebugUnderflowOverflow](/docs/transformers/main/pt/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow).

```py
from transformers.debug_utils import DebugUnderflowOverflow

debug_overflow = DebugUnderflowOverflow(model, max_frames_to_save=100)
```

### Rastreamento em lote

[DebugUnderflowOverflow](/docs/transformers/main/pt/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow) também pode rastrear os valores absolutos mínimos e máximos em cada lote com a detecção de underflow e overflow desativada. Isso ajuda você a localizar onde os valores começam a divergir no seu modelo.

O exemplo abaixo rastreia os lotes 1 e 3 (os lotes são indexados a partir de zero).

```py
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3])
```

```shell
                  *** Starting batch number=1 ***
abs min  abs max  metadata
                  shared Embedding
1.01e-06 7.92e+02 weight
0.00e+00 2.47e+04 input[0]
5.36e-05 7.92e+02 output
[...]
                  decoder.dropout Dropout
1.60e-07 2.27e+01 input[0]
0.00e+00 2.52e+01 output
                  decoder T5Stack
     not a tensor output
                  lm_head Linear
1.01e-06 7.92e+02 weight
0.00e+00 1.11e+00 input[0]
6.06e-02 8.39e+01 output
                   T5ForConditionalGeneration
     not a tensor output

                  *** Starting batch number=3 ***
abs min  abs max  metadata
                  shared Embedding
1.01e-06 7.92e+02 weight
0.00e+00 2.78e+04 input[0]
5.36e-05 7.92e+02 output
[...]
```

[DebugUnderflowOverflow](/docs/transformers/main/pt/internal/trainer_utils#transformers.debug_utils.DebugUnderflowOverflow) relata muitos frames, o que facilita identificar onde os valores divergem. Se você souber que o problema está em torno do lote 150, foque o rastreamento nos lotes 149 e 150 para comparar onde os números começam a diferir.

Você também pode interromper o trace após um número específico de lote, por exemplo, o lote 3.

```py
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3], abort_after_batch_num=3)
```

## Comunicação

O treinamento distribuído requer comunicação entre processos e entre nós, o que é uma fonte comum de erros.

Baixe o script abaixo para diagnosticar problemas de rede e, em seguida, execute-o para testar a comunicação entre GPUs. O comando abaixo testa duas GPUs. Ajuste `--nproc_per_node` e `--nnodes` para o seu sistema.

```bash
wget https://raw.githubusercontent.com/huggingface/transformers/main/scripts/distributed/torch-distributed-gpu-test.py
python -m torch.distributed.run --nproc_per_node 2 --nnodes 1 torch-distributed-gpu-test.py
```

O script exibe `OK` se ambas as GPUs se comunicarem e alocarem memória com sucesso. Consulte o script de diagnóstico para mais detalhes e uma receita para executá-lo em um ambiente SLURM.

Defina `NCCL_DEBUG=INFO` para obter uma saída detalhada de depuração do NCCL.

```bash
NCCL_DEBUG=INFO python -m torch.distributed.run --nproc_per_node 2 --nnodes 1 torch-distributed-gpu-test.py
```

## DeepSpeed

Ao encontrar um erro, verifique primeiro se o DeepSpeed é a causa. Tente novamente a configuração sem o DeepSpeed e, se o erro persistir, reporte o problema. Para problemas não relacionados à integração com o Transformers, abra uma issue no [repositório](https://github.com/microsoft/DeepSpeed) do DeepSpeed.

Para problemas relacionados à integração com o Transformers, inclua as seguintes informações.

* O arquivo de configuração completo do DeepSpeed.
* Os argumentos de linha de comando para [Trainer](/docs/transformers/main/pt/main_classes/trainer#transformers.Trainer) ou o [TrainingArguments](/docs/transformers/main/pt/main_classes/trainer#transformers.TrainingArguments), caso você esteja configurando o [Trainer](/docs/transformers/main/pt/main_classes/trainer#transformers.Trainer) manualmente (não despeje todo o [TrainingArguments](/docs/transformers/main/pt/main_classes/trainer#transformers.TrainingArguments), que contém muitas entradas irrelevantes).
* As saídas desses comandos.

    ```bash
    python -c 'import torch; print(f"torch: {torch.__version__}")'
    python -c 'import transformers; print(f"transformers: {transformers.__version__}")'
    python -c 'import deepspeed; print(f"deepspeed: {deepspeed.__version__}")'
    ```

* Um link para um notebook do Google Colab para reproduzir o problema.
* Um dataset padrão ou não personalizado ou um exemplo existente para reproduzir o problema.

### Processo encerrado na inicialização

Se o processo do DeepSpeed for encerrado durante a inicialização sem um traceback, o programa tentou alocar mais memória de CPU do que está disponível ou permitido. O kernel do sistema operacional encerra o processo em ambos os casos.

Verifique se o seu arquivo de configuração tem `offload_optimizer`, `offload_param` ou ambos configurados para descarregar para a CPU.

Se você tiver NVMe e ZeRO-3 configurados, tente fazer o offload para o NVMe em vez disso. [Estime](https://deepspeed.readthedocs.io/en/latest/memory.html) primeiro os requisitos de memória do seu modelo.

### Perda NaN

A perda NaN geralmente ocorre quando um modelo é pré-treinado em bf16 e, em seguida, é usado com fp16 (isso é especialmente comum com modelos treinados em TPU). Use fp32 ou bf16 se o seu hardware suportar (TPUs, GPUs Ampere ou mais recentes).

O fp16 também pode causar estouro. Se o seu arquivo de configuração for semelhante ao abaixo, você pode ver erros de estouro nos logs.

```json
{
    "fp16": {
        "enabled": "auto",
        "loss_scale": 0,
        "loss_scale_window": 1000,
        "initial_scale_power": 16,
        "hysteresis": 2,
        "min_loss_scale": 1
    }
}
```

O erro `OVERFLOW!` abaixo significa que o escalonador de perda do DeepSpeed não conseguiu encontrar um coeficiente de escala para superar o estouro da perda. Tente um valor maior para `initial_scale_power` (32 geralmente funciona).

```bash
0%|                                                                                                                             | 0/189 [00:00<?, ?it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 262144, reducing to 262144
  1%|▌                                                                                                                    | 1/189 [00:00<01:26,  2.17it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 262144, reducing to 131072.0
  1%|█▏
 [...]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 14%|████████████████▌                                                                                                   | 27/189 [00:14<01:13,  2.21it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 15%|█████████████████▏                                                                                                  | 28/189 [00:14<01:13,  2.18it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
 15%|█████████████████▊                                                                                                  | 29/189 [00:15<01:13,  2.18it/s]
 [deepscale] OVERFLOW! Rank 0 Skipping step. Attempted loss scale: 1, reducing to 1
[...]
```

## DeepSpeed CUDA

O DeepSpeed compila código CUDA C++, o que é uma causa comum de erros de compilação para extensões do PyTorch que exigem CUDA. Esses erros dependem de como o CUDA está instalado no seu sistema.

```bash
pip install deepspeed
```

> [!TIP]
> Para quaisquer outros problemas de instalação, [abra uma issue](https://github.com/microsoft/DeepSpeed/issues) com a equipe do DeepSpeed.

### Kits de ferramentas não idênticos

O PyTorch vem com seu próprio toolkit CUDA, mas o DeepSpeed requer uma versão idêntica do CUDA instalada em todo o sistema. Se você instalou o PyTorch com `cudatoolkit==10.2` no seu ambiente Python, também precisará ter o CUDA 10.2 instalado em todos os lugares.

A localização exata varia conforme o sistema, mas `/usr/local/cuda-10.2` é o caminho mais comum em sistemas Unix. Depois que o CUDA estiver configurado e adicionado ao seu `PATH`, localize a instalação com este comando.

```bash
which nvcc
```

### Vários toolkits

Seu sistema pode ter mais de um toolkit CUDA instalado.

```text
/usr/local/cuda-10.2
/usr/local/cuda-11.0
```

Os instaladores de pacotes geralmente definem os caminhos para a última versão instalada. Se a compilação falhar porque não consegue encontrar a versão correta do CUDA, configure `PATH` e `LD_LIBRARY_PATH` para apontar para o caminho correto.

Verifique primeiro estas variáveis de ambiente.

```bash
echo $PATH
echo $LD_LIBRARY_PATH
```

`PATH` lista os locais dos executáveis. `LD_LIBRARY_PATH` lista os locais das bibliotecas compartilhadas. As entradas anteriores têm prioridade, e `:` separa múltiplas entradas. Adicione o caminho correto do CUDA no início para priorizá-lo.

```bash
# adjust the version and full path if needed
export PATH=/usr/local/cuda-10.2/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-10.2/lib64:$LD_LIBRARY_PATH
```

Verifique também se os diretórios atribuídos existem. O subdiretório `lib64` contém objetos CUDA `.so`, como `libcudart.so`. Verifique os nomes de arquivo reais e atualize conforme necessário.

### Versões anteriores

Versões mais antigas do CUDA às vezes exigem versões mais antigas do compilador. Por exemplo, se o CUDA exigir `gcc-7`, mas seu sistema tiver apenas `gcc-9`, a compilação falhará. Instale o compilador mais antigo necessário e crie um link simbólico para que o sistema de compilação do CUDA possa localizá-lo.

```bash
# adjust the path to your system
sudo ln -s /usr/bin/gcc-7  /usr/local/cuda-10.2/bin/gcc
sudo ln -s /usr/bin/g++-7  /usr/local/cuda-10.2/bin/g++
```

### Pré-compilação

Se você ainda estiver com problemas para instalar o DeepSpeed ou compilá-lo em tempo de execução, pré-compile os módulos do DeepSpeed primeiro. Execute os comandos abaixo para uma compilação local.

```bash
git clone https://github.com/deepspeedai/DeepSpeed/
cd DeepSpeed
rm -rf build
TORCH_CUDA_ARCH_LIST="8.6" DS_BUILD_CPU_ADAM=1 DS_BUILD_UTILS=1 pip install . \
--global-option="build_ext" --global-option="-j8" --no-cache -v \
--disable-pip-version-check 2>&1 | tee build.log
```

> [!TIP]
> Adicione `DS_BUILD_AIO=1` ao comando de build para usar o offload NVMe. Certifique-se de instalar o pacote libaio-dev em todo o sistema.

Em seguida, defina a arquitetura da sua GPU em `TORCH_CUDA_ARCH_LIST`. Uma lista completa das GPUs da NVIDIA e suas respectivas arquiteturas está disponível na [página de GPUs CUDA](https://developer.nvidia.com/cuda-gpus). Para verificar a versão do PyTorch que corresponde à sua arquitetura, execute o comando abaixo.

```bash
python -c "import torch; print(torch.cuda.get_arch_list())"
```

Encontre a arquitetura de uma GPU com o seguinte comando.

```bash
CUDA_VISIBLE_DEVICES=0 python -c "import torch; print(torch.cuda.get_device_capability())"
```

Execute o comando a seguir para encontrar a arquitetura da GPU `0`. A saída exibe os valores `major` e `minor` que, juntos, formam a arquitetura da GPU. O exemplo abaixo mostra a arquitetura `8.6`.

```bash
CUDA_VISIBLE_DEVICES=0 python -c "import torch; \
print(torch.cuda.get_device_properties(torch.device('cuda')))
"_CudaDeviceProperties(name='GeForce RTX 3090', major=8, minor=6, total_memory=24268MB, multi_processor_count=82)"
```

Para um resultado de `8, 6`, defina `TORCH_CUDA_ARCH_LIST="8.6"`. Para várias GPUs com arquiteturas diferentes, liste-as como `TORCH_CUDA_ARCH_LIST="6.1;8.6"`.

Você pode omitir `TORCH_CUDA_ARCH_LIST` e deixar o programa de build detectar a arquitetura da GPU automaticamente, mas ela pode não corresponder à GPU real na máquina de destino. Definir a arquitetura explicitamente é mais confiável.

Para treinar em várias máquinas com a mesma configuração, compile uma roda binária.

```bash
git clone https://github.com/deepspeedai/DeepSpeed/
cd DeepSpeed
rm -rf build
TORCH_CUDA_ARCH_LIST="8.6" DS_BUILD_CPU_ADAM=1 DS_BUILD_UTILS=1 \
python setup.py build_ext -j8 bdist_wheel
```

Isso gera uma wheel binária como `dist/deepspeed-0.3.13+8cd046f-cp38-cp38-linux_x86_64.whl`. Instale-a localmente ou em outra máquina.

```bash
pip install deepspeed-0.3.13+8cd046f-cp38-cp38-linux_x86_64.whl
```

