As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Dicas e armadilhas de configuração da SageMaker Distributed Model Parallelism Library
Analise as dicas e armadilhas a seguir antes de usar a biblioteca de SageMaker paralelismo de modelos da Amazon. Essa lista inclui dicas que são aplicáveis em todos os frameworks. Para obter TensorFlow dicas PyTorch específicas, consulte Modificar um script TensorFlow de treinamento eModificar um script PyTorch de treinamento, respectivamente.
Tamanho do lote e número de microlotes
-
A biblioteca é mais eficiente quando o tamanho do lote é aumentado. Para casos de uso em que o modelo cabe em um dispositivo único, mas pode ser treinado apenas com um tamanho de lote pequeno, o tamanho do lote pode e deve ser aumentado após a integração da biblioteca. O paralelismo de modelos economiza memória para modelos grandes, habilitando o treinamento usando tamanhos do lote que antes não cabiam na memória.
-
Escolher um número de microlotes muito pequenos ou muito grandes pode reduzir a performance. A biblioteca executa cada microlote sequencialmente em cada dispositivo, portanto, o tamanho do microlote (tamanho do lote dividido pelo número de microlotes) deve ser grande o suficiente para utilizar totalmente cada GPU. Ao mesmo tempo, a eficiência do pipeline aumenta com o número de microlotes, portanto, é importante encontrar o equilíbrio certo. Normalmente, um bom ponto de partida é experimentar 2 ou 4 microlotes, aumentando o tamanho do lote até o limite de memória e, em seguida, experimentar tamanhos do lote e números de microlotes maiores. À medida que o número de microlotes aumenta, tamanhos do lote maiores podem se tornar viáveis se um pipeline intercalado for usado.
-
O tamanho do lote deve ser sempre divisível pelo número de microlotes. Observe que, dependendo do tamanho do conjunto de dados, às vezes, o último lote de cada epoch pode ser menor que o resto e esse lote menor também precisa ser divisível pelo número de microlotes. Se não estiver, você pode definir
drop_remainder=True
atf.Dataset.batch()
chamada (in TensorFlow) ou definirdrop_last=True
inDataLoader
(in PyTorch), para que esse último lote pequeno não seja usado. Se você estiver usando uma API diferente para o pipeline de dados, talvez seja necessário ignorar manualmente o último lote sempre que ele não for divisível pelo número de microlotes.
Particionamento manual
-
Se você usa o particionamento manual, esteja atento aos parâmetros que são consumidos por várias operações e módulos em seu modelo, como a tabela de incorporação nas arquiteturas do transformador. Módulos que compartilham o mesmo parâmetro devem ser colocados no mesmo dispositivo para que estejam corretos. Quando o particionamento automático é usado, a biblioteca aplica automaticamente essa restrição.
Preparação de dados
-
Se o modelo usar várias entradas, certifique-se de semear as operações aleatórias em seu data pipeline (por exemplo, embaralhar) com
smp.dp_rank()
. Se o conjunto de dados estiver sendo fragmentado de forma determinística em dispositivos em paralelo de dados, certifique-se de que o fragmento seja indexado porsmp.dp_rank()
. Isso irá garantir que a ordem dos dados vistos em todas as classificações que formam uma partição de modelo seja consistente.
Retornar tensores de smp.DistributedModel
-
Qualquer tensor retornado da função
smp.DistributedModel.call
(for TensorFlow) ousmp.DistributedModel.forward
(for) é transmitido para PyTorch todas as outras classificações, a partir da classificação que calculou esse tensor específico. Como resultado, qualquer tensor que não é necessário fora dos métodos de chamada e encaminhamento (ativações intermediárias, por exemplo) não deve ser retornado, pois isso causa comunicação desnecessária e sobrecarga da memória e prejudica a performance.
O Decorator do @smp.step
-
Se uma função decorada por
smp.step
tiver um argumento de tensor que não tenha uma dimensão de lote, o nome do argumento deverá ser fornecido na listanon_split_inputs
durante a chamada desmp.step
. Isso evita que a biblioteca tente dividir o tensor em microlotes. Para obter mais informações, consultesmp.step
na documentação de API.
Atrasar a inicialização do parâmetro
Para modelos muito grandes com mais de 100 bilhões de parâmetros, a inicialização do peso por meio da memória da CPU pode resultar em um out-of-memory erro. Para contornar isso, a biblioteca oferece um gerenciador de contexto smp.delay_param_initialization
. Isso atrasa a alocação física dos parâmetros até que eles sejam movidos para a GPU durante a primeira execução de uma função decorada por smp.step
. Isso evita o uso desnecessário de memória da CPU durante a inicialização do treinamento. Use o gerenciador de contexto ao criar um objeto de modelo, conforme exibido no código a seguir.
with smp.delay_param_initialization(enabled=True): model = MyModel()
Paralelismo de tensores para PyTorch
-
Se você estiver usando uma semente para resultados determinísticos, defina a semente baseada em
smp.dp_rank()
(por exemplo,torch.manual_seed(42 + smp.dp_rank())
). Se você não fizer isso, partições diferentes de umnn.Parameter
serão inicializadas da mesma forma, afetando a convergência. -
SageMakerA biblioteca de paralelismo de modelos usa NCCL para implementar os coletivos necessários para a distribuição dos módulos. Especialmente para modelos menores, se muitas chamadas de NCCL forem programadas na GPU ao mesmo tempo, o uso de memória poderá aumentar devido ao espaço adicional usado pela NCCL. Para neutralizar isso,
smp
controla as chamadas de NCCL para que, a qualquer momento, o número de operações contínuas de NCCL seja menor ou igual a um determinado limite. O limite padrão é 8, mas isso pode ser ajustado usando a variável de ambienteSMP_NCCL_THROTTLE_LIMIT
. Se você observar o uso de memória maior do que o esperado ao usar o paralelismo de tensores, tente reduzir esse limite. No entanto, escolher um limite muito pequeno pode causar perda de taxa de transferência. Para desativar completamente o controle de utilização, você pode definirSMP_NCCL_THROTTLE_LIMIT=-1
. -
A seguinte identidade, que é válida quando o grau de paralelismo de tensores é 1, não é válida quando o grau de paralelismo de tensores é maior que 1:
smp.mp_size() * smp.dp_size() == smp.size()
. Isso ocorre porque o grupo em paralelo de tensores faz parte do grupo de paralelismo do modelo e do grupo de paralelismo de dados. Se seu código tiver referências existentes amp_rank
,mp_size
,MP_GROUP
e assim por diante, e se você quiser trabalhar apenas com o grupo em paralelo do pipeline, talvez seja necessário substituir as referências porsmp.pp_size()
. As seguintes identidades são sempre verdadeiras:-
smp.mp_size() * smp.rdp_size() == smp.size()
-
smp.pp_size() * smp.dp_size() == smp.size()
-
smp.pp_size() * smp.tp_size() * smp.rdp_size() == smp.size()
-
-
Uma vez que o wrapper do
smp.DistributedModel
modifica os parâmetros do modelo quando o paralelismo de tensores está ativado, o otimizador deve ser criado após a chamada desmp.DistributedModel
, com os parâmetros distribuídos. Por exemplo, o seguinte não funciona:## WRONG model = MyModel() optimizer = SomeOptimizer(model.parameters()) model = smp.DistributedModel(model) # optimizer now has outdated parameters!
Em vez disso, o otimizador deve ser criado com os seguintes parâmetros de
smp.DistributedModel
:## CORRECT model = smp.DistributedModel(MyModel()) optimizer = SomeOptimizer(model.optimizers())
-
Quando um módulo é substituído por sua contraparte distribuída por meio de paralelismo de tensores, o módulo distribuído não herda seus pesos do módulo original e inicializa novos pesos. Isso significa que, por exemplo, se os pesos precisarem ser inicializados em uma chamada específica (por exemplo, por meio de uma chamada de
load_state_dict
), isso precisará acontecer após a chamada desmp.DistributedModel
, quando a distribuição do módulo ocorrer. -
Ao acessar diretamente os parâmetros dos módulos distribuídos, observe que o peso não tem o mesmo formato do módulo original. Por exemplo:
with smp.tensor_parallelism(): linear = nn.Linear(60, 60) # will pass assert tuple(linear.weight.shape) == (60, 60) distributed_linear = smp.DistributedModel(linear) # will fail. the number of input channels will have been divided by smp.tp_size() assert tuple(distributed_linear.module.weight.shape) == (60, 60)
-
O uso de
torch.utils.data.distributed.DistributedSampler
é altamente recomendado para paralelismo de tensores. Isso garante que cada classificação em paralelo de dados receba o mesmo número de amostras de dados, o que evita interrupções que possam resultar de diferentesdp_rank
s realizando um número de etapas diferentes. -
Se você usar a
join
API daDistributedDataParallel
classe PyTorch's para lidar com casos em que diferentes classificações paralelas de dados têm números diferentes de lotes, você ainda precisa garantir que as classificações que estão na mesmaTP_GROUP
tenham o mesmo número de lotes; caso contrário, os coletivos de comunicação usados na execução distribuída de módulos podem travar. Classificações que estão em diferentesTP_GROUP
s podem ter diferentes números de lotes, desde que a API dojoin
seja usada. -
Se você quiser que seu modelo tenha um ponto de verificação e usar o paralelismo de tensores, considere o seguinte:
-
Para evitar paradas e condições de corrida ao salvar e carregar modelos ao usar o paralelismo de tensores, certifique-se de chamar as funções apropriadas dos seguintes estados do modelo e do otimizador dentro de uma classificação de paralelismo de dados reduzidos.
-
Se você estiver fazendo a transição de um script em paralelo do pipeline existente e habilitando o tensor em paralelo para o script, certifique-se de modificar qualquer bloco de
if smp.dp_rank() == 0
usado para salvar e carregar os blocos deif smp.rdp_rank() == 0
. Caso contrário, isso pode fazer com que a tarefa de treinamento pare.
Para obter mais informações sobre o ponto de verificação de um modelo com paralelismo de tensores, consulte Pontos de verificação de um modelo distribuído.
-