Resumo da memória da GPU e utilização de aprendizado profundo

Acredito que muitas pessoas, inclusive eu, têm muitas reclamações sobre a memória da GPU. Problemas como CUDA sem memória têm nos incomodado. O artigo de hoje é para analisá-lo, talvez seja útil. ajuda

Em primeiro lugar , vamos falar brevemente sobre a importância da GPU. Tomando o Pytorch como exemplo, ele usa CUDA e cuDNN para converter vários cálculos realizados durante a inferência do modelo de aprendizado profundo em multiplicação de matrizes para acelerar, de modo a alcançar a operação do ano de o macaco até o presente Dez a cem vezes mais rápido.
Quanto à memória de vídeo que amamos e odiamos, a alteração da memória em execução da GPU quando os dados são lidos é usada como referência. O mecanismo de implementação específico geralmente é por meio do processo de trabalho + fila, permitindo que vários trabalhadores leiam e pré-processem dados de forma assíncrona. Enfileirar , e então o supervisor treina o processo principal para buscar dados do outro lado da fila. Se a fila estiver cheia e o processo de treinamento não tiver tempo para buscar dados, o processo de trabalho será bloqueado e a memória em execução não crescerá sem limite.

torch.utils.data.DataLoader(datasets[x], batch_size=batch_size, shuffle=True, num_workers=8, pin_memory=True)

Nota: O processamento de dados não relacionados e as operações de cálculo, como impressão de logs e cálculo de eta, também ocupam muito tempo de processamento da CPU.
Vamos falar brevemente sobre a memória de vídeo de controle batch_size (não completamente proporcional, os parâmetros do próprio modelo e os dados estendidos também ocupam a memória de vídeo), num_workers controlam a utilização da GPU (para que os dados de carregamento da CPU e a velocidade de processamento da GPU possam corresponder, o sistema operacional da Lenovo pode ser entendido), os dois últimos serão mencionados

Obviamente, o carregamento de dados, pré-processamento, pós-processamento, etc. são colocados na CPU, ou seja, outras tarefas de IO para leitura e gravação. E é uma boa maneira de ajustar a utilização da GPU ajustando o número de num_workers. É melhor definir a quantidade em um intervalo relativamente grande (4-8 pode ser considerado), mas não quanto maior, melhor. Porque quanto maior for, embora existam mais threads, o consumo de cada thread também é grande, então aumentará a carga na CPU, reduzindo assim a utilização da GPU. O número de num_workers geralmente é usado em conjunto com o número de batch_size.
Além disso , quando seu servidor ou computador possui uma grande quantidade de memória e bom desempenho, é recomendável ativar o pin_memory , o que evita a necessidade de transferir dados da CPU para o cache RAM e depois transferi-los para a GPU; para Quando Verdadeiro, ele é mapeado diretamente para o bloco de memória relevante da GPU, economizando um pouco de tempo de transmissão de dados.
Observe aqui que, ao carregar dados, é recomendável carregar todos os dados no __init__ do conjunto de dados ou no pipeline chamando o método de importação de dados em vez de escrever no método getitem.

Portanto, para isso, um método aproximado é adotar o modo ddp (sem muitos threads) quando houver várias GPUs e usar o método de memória compartilhada para permitir que o processo de rank0 jogue esses dados de anotação na memória compartilhada e, em seguida, todos other O processo é somente leitura e mapeia esta memória, realizando cópia zero.

Se a taxa de utilização da GPU for baixa , o primeiro é aumentar o tamanho do lote, aumentar o uso de memória da GPU e tentar usar toda a memória em vez de deixar metade dela. A memória vazia é usada por outros programas, e a eficiência de ambas as tarefas será muito baixa. Em segundo lugar, ao carregar dados, defina o número de encadeamentos num_workers um pouco maior, 8, 16 etc. são recomendados e habilite pin_memory=True. Não coloque toda a tarefa no processo principal, pois isso consome CPU, e a velocidade e o desempenho são extremamente baixos .

Em segundo lugar , considere a influência do próprio modelo, ou seja, os parâmetros do modelo e a resposta de cada camada, que estão relacionados à complexidade do modelo. Tomando o Resnet50 como exemplo, existem 26 milhões de parâmetros. Se a precisão do ponto flutuante de 32 bits é usado, então a memória ocupada é: 26M * 32 bits = 99MB . Ao mesmo tempo, ele tem 16 milhões de respostas, ocupando memória: 16M*32bit = 64MB .
A resposta não está diretamente relacionada ao número de parâmetros do modelo. As camadas convolucionais podem ter respostas muito grandes, mas poucos parâmetros; as camadas de ativação podem até não ter parâmetros.

Talvez você sinta que é apenas um pouco mais de 100 milhões neste ponto, e o impacto não é grande. Não, este é o momento para o batchsize estrear.

Para utilizar efetivamente o mecanismo SIMD da GPU, os dados devem ser inseridos na rede na forma de mini-lote.
Se você quiser preencher o caminho comum de 1024 bits com números de ponto flutuante de 32 bits, precisará calcular 32 amostras ao mesmo tempo.
Ao usar mini-batch, apenas uma cópia dos parâmetros do modelo ainda é salva, mas as respostas de cada camada precisam ser dobradas de acordo com o tamanho do mini-batch.

ResNet com 50 camadas, mini-lote=32, cada resposta de camada ocupa memória: 64MB*32 = 2GB

No entanto , 2G não é o fim. Na biblioteca de aprendizado profundo, o método de redução geralmente é usado para converter o cálculo de convolução em multiplicação de matrizes**. Ao calcular esse tipo de convolução, a resposta da camada frontal X XX precisa ser ampliada em K^2 vezes.

ResNet de 50 camadas, ao considerar o efeito de redução, a resposta de cada camada ocupa 7,5 GB de memória

Outra coisa a se atentar é qual é o padrão para o final do seu treinamento de rede. É baseado em época ou iteração. Se a iteração for usada como padrão, se você aumentar o batch_size, o tempo de treinamento da rede aumentará naturalmente. Nesse momento, a iteração precisa ser descontada pela metade quando o batch_size dobrar. Se a época for usada como padrão, nenhuma outra alteração será necessária ao alterar o batch_size.

Uso de memória de vídeo ≈ uso de memória de vídeo do modelo + batch_size × uso de memória de vídeo de cada amostra, pode ser visualizado de maneira simples

ResNet50 foi usado como exemplo antes, e a substituição a seguir por GPT-2 pode estar mais próxima da estrutura de rede atual.

introdução

Para um modelo GPT-2 com parâmetros de 1,5B, são necessários 3 GB de memória para armazenar seus pesos (ou parâmetros) com precisão de 16 bits, mas não pode ser treinado em uma GPU com 32 GB de memória de vídeo. Durante o treinamento do modelo, a maior parte da sobrecarga de memória é usada para estados do modelo , como estados do otimizador, gradientes e parâmetros. Além do estado do modelo, a sobrecarga de memória restante vem de estados residuais , como valores de ativação, cache da área de preparação e fragmentação de memória.

Estados modelo

Tomando Adam como exemplo, ele precisa armazenar duas partes do estado do otimizador : momento médio do tempo e variação dos gradientes. Portanto, ao usar o Adam para treinamento do modelo, é necessário haver espaço de memória suficiente para armazenar a estimativa de momento e o valor de cópia da variação do gradiente. Além disso, é preciso haver espaço suficiente para armazenar o gradiente e o peso do próprio modelo . O estado do otimizador geralmente ocupa uma grande parte da memória, especialmente no treinamento de precisão mista.

No treinamento de precisão mista (fp16/32), os parâmetros do modelo e os valores de ativação são salvos no formato fp16, e os pesos fp16 e os valores de ativação também são usados ​​para cálculo na propagação para frente e para trás. No entanto, para calcular com mais eficiência e garantir a correção das atualizações de gradiente (erros de arredondamento ocorrerão no treinamento de precisão mista), geralmente uma cópia dos pesos fp32 e do status do otimizador será copiada ao mesmo tempo.

Exemplo: Para um modelo com tamanho de parâmetro γ, use Adam e precisão mista para treinamento. Primeiro, requer 2γ de memória para armazenar os parâmetros e pesos copiados por fp16 e 4γ de memória para armazenar os parâmetros, estimativa de momento e variação de gradiente de cópias de fp32, respectivamente. K é usado aqui para representar o múltiplo de sobrecarga de memória adicional necessário para armazenar o estado do otimizador e K = 12 para Adam de precisão mista. Em resumo, a sobrecarga de memória necessária para treinar tal modelo é 2γ + 2γ + Kγ = 16γ. Para um modelo GPT-2 com um tamanho de parâmetro de 1,5B, o consumo de memória de treinamento necessário é de pelo menos 24GB, o que é muito maior do que o tamanho do parâmetro de 3GB do próprio modelo no formato fp16.

Estados residuais

Ativação

Os valores de ativação podem ocupar uma grande parte da memória durante o treinamento. A memória ocupada pelo valor de ativação do modelo de estrutura Transformer é proporcional ao tamanho de Transformers_layers * hidden_dimensions * sequence_length * batch_size . Para o modelo GPT-2, a memória do valor de ativação é 12 * hidden_dim * bsz * seq_length * Transformer_layers . Portanto, para um modelo GPT-2 com parâmetros de 1,5 B, comprimento de sequência de 1 K e tamanho de lote de 32, a memória necessária é de 60 GB.

Embora a tecnologia de verificação de ativação possa reduzir a memória ocupada pelo valor de ativação em aproximadamente tempos raiz ao custo de recalcular o valor de ativação em 33% (isso pode reduzir a memória do valor de ativação exigida pelo modelo acima para cerca de 8 GB), para For maior o modelo, o efeito ainda é limitado (o modelo GPT com parâmetros de 100B ainda precisa de 60G de memória com base no ponto de verificação de ativação).

Buffers temporários

Cache de área temporária para armazenar resultados de cálculos intermediários, como redução total de gradientes ou cálculo de norma de gradiente (cálculo de norma de gradiente) que funde todos os resultados de gradiente em um único buffer nivelado antes da redução total para melhorar a taxa de transferência. Para um modelo com parâmetros de 1,5B, o uso de memória do buffer nivelado em fp32 atingirá 6GB.

Fragmentação de memória

Mesmo que o tamanho da memória seja maior que a sobrecarga de memória necessária para o treinamento do modelo em teoria, devido à existência de fragmentação da memória (a memória livre aparece em diferentes locais de maneira descontínua, resultando em nenhuma memória contínua para atender às necessidades de treinamento do modelo) , ainda pode haver erro de falta de memória. Ao treinar um modelo supergrande, em alguns casos extremos, haverá um problema de OOM (falta de memória) quando ainda houver 30% da memória restante.

Uma maneira simples de usar a memória de vídeo no blade

1. Para usar efetivamente o SIMD, se a precisão for dobrada, o tamanho do lote deve ser dobrado. Não é possível reduzir o consumo de memória. Mas você pode usar a aceleração de precisão mista apex da Nvidia e a memória pytorch é cortada diretamente pela metade .
2. Operação no local : reescrever diretamente a resposta original sem abrir uma nova memória. Muitas funções de ativação podem fazer isso. Como nn.ReLU(inplace=True), ou seja, use o sinalizador de operação inplace tanto quanto possível .
É um pouco mais complicado, analisando todo o grafo da rede, você pode encontrar respostas que precisam ser usadas apenas uma vez, que podem compartilhar memória com as respostas subsequentes. Por exemplo, o mecanismo de compartilhamento de memória do MxNet .
3. Cálculo para armazenamento : descubra os resultados de resposta que são fáceis de calcular (como a saída da camada de função de ativação) em vez de armazenamento e calcule temporariamente quando necessário. Usando essa abordagem, o exemplo MxNet foi capaz de reduzir o consumo de memória de uma rede ResNet de 50 camadas em um fator de quatro.

Confira também as 10+ maneiras de enganar aqui

Por fim, se você puder otimizar a operação do algoritmo e otimizar o uso de memória por tamanho de lote na estrutura do gráfico de rede, poderá instalar naturalmente mais tamanhos de lote para fazer uso total do núcleo da GPU.

referência

  1. https://www.zhihu.com/question/476086761
  2. https://blog.csdn.net/shenxiaolu1984/article/details/71522141
  3. https://zhuanlan.zhihu.com/p/362556069
  4. https://blog.csdn.net/qq_45756171/article/details/122910838
  5. https://zhuanlan.zhihu.com/p/348122818
  6. https://blog.csdn.net/qq_34405401/article/details/108519823
  7. https://zhuanlan.zhihu.com/p/520898642
  8. https://zhuanlan.zhihu.com/p/31558973
  9. https://www.cnblogs.com/jiangkejie/p/10098995.html

Acho que você gosta

Origin blog.csdn.net/weixin_42455006/article/details/127653740
Recomendado
Clasificación