Path: blob/master/site/pt-br/guide/mixed_precision.ipynb
25115 views
Copyright 2019 The TensorFlow Authors.
Precisão mista
Visão geral
A precisão mista é o uso de tipos de ponto flutuante de 16 e 32 bits num modelo durante o treinamento para fazê-lo funcionar mais rápido e usar menos memória. Ao manter certas partes do modelo nos tipos de 32 bits para estabilidade numérica, o modelo terá um tempo de passo menor e treinará igualmente em termos de métricas de avaliação, como exatidão. Este guia descreve como usar a API de precisão mista do Keras para acelerar seus modelos. O uso desta API pode melhorar o desempenho em mais de 3 vezes em GPUs modernas, 60% em TPUs e mais de 2 vezes nas CPUs Intel mais recentes.
Hoje, a maioria dos modelos usa o dtype float32, que ocupa 32 bits de memória. No entanto, existem dois dtypes de menor precisão, float16 e bfloat16, cada um ocupando 16 bits de memória. Os aceleradores modernos podem executar operações mais rapidamente nos dtypes de 16 bits, pois possuem hardware especializado para executar computações de 16 bits e os dtypes de 16 bits podem ser lidos da memória mais rapidamente.
As GPUs NVIDIA podem executar operações em float16 mais rapidamente do que em float32, e TPUs e CPUs Intel suportados podem executar operações em bfloat16 mais rapidamente do em que float32. Portanto, esses dtypes de menor precisão devem ser usados sempre que possível nesses dispositivos. No entanto, variáveis e algumas computações ainda devem estar em float32 por motivos numéricos para que o modelo seja treinado com a mesma qualidade. A API de precisão mista Keras permite que você use uma combinação de float16 ou bfloat16 com float32, para obter os benefícios de desempenho de float16/bfloat16 e os benefícios de estabilidade numérica de float32.
Observação: Neste guia, o termo "estabilidade numérica" refere-se a como a qualidade de um modelo é afetada pelo uso de um dtype de menor precisão em vez de um dtype de maior precisão. Uma operação é "numericamente instável" em float16 ou bfloat16 se executá-la num desses dtypes fizer com que o modelo tenha pior exatidão de avaliação ou outras métricas em comparação com a execução da operação em float32.
Configuração
Hardware suportado
Embora a precisão mista funcione na maioria dos hardwares, ela só acelerará os modelos em GPUs NVIDIA recentes, TPUs de nuvem e CPUs Intel recentes. As GPUs NVIDIA suportam uma combinação de float16 e float32, enquanto TPUs e CPUs Intel suportam uma combinação de bfloat16 e float32.
Entre as GPUs NVIDIA, aquelas com capacidade de computação 7.0 ou superior apresentarão a maior vantagem de desempenho com a precisão mista porque possuem unidades de hardware especiais, chamadas Tensor Cores, para acelerar multiplicações e convoluções de matrizes float16. GPUs mais antigas não oferecem nenhuma vantagem de desempenho matemático para o uso de precisão mista; no entanto, a economia de memória e largura de banda pode permitir algumas acelerações. Você pode pesquisar a capacidade de computação da sua GPU na página CUDA GPU da NVIDIA. Exemplos de GPUs que mais se beneficiarão da precisão mista incluem GPUs RTX, V100 e A100.
As CPUs da Intel, começando com os processadores Intel Xeon de 4ª geração (codinome Sapphire Rapids), apresentarão o maior benefício de desempenho com precisão mista, pois podem acelerar cálculos bfloat16 usando instruções AMX (requer Tensorflow 2.12 ou posterior).
Observação: ao executar este guia no Google Colab, o runtime da GPU normalmente tem um P100 conectado. O P100 tem capacidade computacional 6.0 e não se espera que apresente uma aceleração significativa. Se estiver executando no runtime da CPU, pode haver lentidão, pois o runtime provavelmente tem uma CPU sem AMX.
Você pode verificar seu tipo de GPU com o seguinte. O comando só existe se os drivers NVIDIA estiverem instalados, caso contrário, o seguinte gerará um erro.
Todas as TPUs de nuvem suportam bfloat16.
Mesmo em CPUs Intel mais antigas, em outras CPUs x86 sem AMX e em GPUs mais antigas, onde nenhuma aceleração é esperada, APIs de precisão mista ainda podem ser usadas para testes de unidade, depuração ou apenas para testar a API. No entanto, mixed_bfloat16 em CPUs sem instruções AMX e mixed_float16 em todas as CPUs x86 serão executados significativamente mais devagar.
Configurando a política de dtypes
Para usar precisão mista em Keras, você precisa criar uma tf.keras.mixed_precision.Policy
, normalmente chamada de dtype policy. As políticas de dtype especificam os dtypes nos quais as camadas serão executadas. Neste guia, você construirá uma política a partir da string 'mixed_float16'
e a definirá como a política global. Isto fará com que as camadas criadas posteriormente usem precisão mista com uma mistura de float16 e float32.
Resumindo, você pode passar diretamente uma string para set_global_policy
, o que normalmente é feito na prática.
A política especifica dois aspectos importantes de uma camada: o dtype em que os cálculos da camada são feitos e o dtype das variáveis de uma camada. Acima, você criou uma política mixed_float16
(ou seja, uma mixed_precision.Policy
criada passando a string 'mixed_float16'
para seu construtor). Com esta política, as camadas usam computações float16 e variáveis float32. As computações são feitas em float16 por questões de desempenho, mas as variáveis devem ser mantidas em float32 para estabilidade numérica. Você pode consultar diretamente essas propriedades da política.
Conforme mencionado anteriormente, a política mixed_float16
melhorará significativamente o desempenho em GPUs NVIDIA com capacidade de computação de pelo menos 7.0. A política será executada em outras GPUs e CPUs, mas poderá não melhorar o desempenho. Para TPUs e CPUs, a política mixed_bfloat16
deve ser usada.
Construindo o modelo
Agora vamos começar a construir um modelo simples. Modelos de brinquedo muito pequenos normalmente não se beneficiam da precisão mista, porque a sobrecarga do runtime do TensorFlow normalmente domina o tempo de execução, tornando qualquer melhoria de desempenho na GPU insignificante. Portanto, vamos construir duas grandes camadas Dense
com 4.096 unidades cada se uma GPU for usada.
Toda camada tem uma política e usará a política global por padrão. Cada uma das camadas Dense
, portanto, tem a política mixed_float16
porque você definiu a política global como mixed_float16
anteriormente. Isto fará com que as camadas densas façam computações float16 e tenham variáveis float32. Elas convertem suas entradas em float16 para fazer computações em float16, o que faz com que suas saídas sejam float16 como resultado. Suas variáveis são float32 e serão convertidas em float16 quando as camadas forem chamadas para evitar erros de incompatibilidade de dtype.
Em seguida, crie as previsões de saída. Normalmente, você pode criar as previsões de saída da seguinte maneira, mas isto nem sempre é numericamente estável com float16.
Uma ativação softmax no final do modelo deve ser float32. Como a política dtype é mixed_float16
, a ativação do softmax normalmente faria um float16 computar o dtype e produzir tensores float16 como saída.
Isto pode ser corrigido separando as camadas Dense e softmax e passando dtype='float32'
para a camada softmax:
Passar dtype='float32'
para o construtor da camada softmax substitui a política dtype da camada pela política float32
, que faz computações e mantém as variáveis em float32. De forma equivalente, você poderia ter passado dtype=mixed_precision.Policy('float32')
; camadas sempre convertem o argumento dtype em uma política. Como a camada Activation
não tem variáveis, a variável dtype da política é ignorada, mas a computação do dtype de float32 da política faz com que o softmax e a saída do modelo sejam float32.
Acrescentar um softmax float16 no meio de um modelo é bom, mas um softmax no final do modelo deve estar em float32. A razão é que se o tensor intermediário que flui do softmax para a perda for float16 ou bfloat16, poderão ocorrer problemas numéricos.
Você pode sobrepor o dtype de qualquer camada para float32 passando dtype='float32'
se achar que não ele será numericamente estável com computações float16. Mas normalmente, isto só é necessário na última camada do modelo, já que a maioria das camadas tem precisão suficiente com mixed_float16
e mixed_bfloat16
.
Mesmo que o modelo não termine em softmax, as saídas ainda devem ser float32. Embora desnecessário para este modelo específico, as saídas do modelo podem ser convertidas em float32 com o seguinte:
Em seguida, finalize e compile o modelo e gere os dados de entrada:
Este exemplo faz o cast dos dados de entrada de int8 para float32. Você não faz cast para float16, pois a divisão por 255 está na CPU, que executa operações float16 mais lentamente que operações float32. Nesse caso, a diferença de desempenho é insignificante, mas em geral você deve executar a matemática do processamento de entrada em float32 se for executada na CPU. A primeira camada do modelo converterá as entradas para float16, à medida que cada camada converte entradas de ponto flutuante para seu dtype de computação.
Os pesos iniciais do modelo são recuperados. Isto permitirá treinar do zero novamente carregando os pesos.
Treinando o modelo com Model.fit
Em seguida, treine o modelo:
Observe que o modelo imprime o tempo por cada passo nos logs: por exemplo, "25ms/step". A primeira época pode ser mais lenta, pois o TensorFlow passa algum tempo otimizando o modelo, mas depois o tempo por passo deve se estabilizar.
Se você estiver executando este guia no Colab, poderá comparar o desempenho da precisão mista com float32. Para fazer isso, altere a política de mixed_float16
para float32
na seção "Configurando a política dtype" e execute novamente todas as células até este ponto. Em GPUs com capacidade de computação 7.X, você verá o tempo por passo aumentar significativamente, indicando que a precisão mista acelerou o modelo. Não esqueça de alterar a política de volta para mixed_float16
e executar novamente as células antes de continuar com o guia.
Em GPUs com capacidade de computação de pelo menos 8,0 (GPUs Ampere e superiores), você provavelmente não verá nenhuma melhoria de desempenho no modelo de brinquedo deste guia ao usar precisão mista em comparação com float32. Isto se deve ao uso de TensorFloat-32 , que usa automaticamente matemática de menor precisão em certas operações float32, como tf.linalg.matmul
. O TensorFloat-32 oferece algumas das vantagens de desempenho da precisão mista ao usar float32. No entanto, em modelos do mundo real, você ainda experimentará melhorias significativas de desempenho com precisão mista devido à economia de largura de banda de memória e operações que o TensorFloat-32 não suporta.
Ao executar precisão mista numa TPU, você não verá tanto ganho de desempenho em comparação com a execução de precisão mista em GPUs, especialmente GPUs pré-Ampere. Isto ocorre porque as TPUs realizam certas operações em bfloat16 nos bastidores, mesmo com a política dtype padrão de float32. Isso é semelhante a como as GPUs Ampere usam o TensorFloat-32 por padrão. Em comparação com as GPUs Ampere, as TPUs normalmente apresentam menos ganhos de desempenho com precisão mista em modelos do mundo real.
Para muitos modelos do mundo real, a precisão mista também permite dobrar o tamanho do lote sem ficar sem memória, já que os tensores float16 ocupam metade da memória. No entanto, isto não se aplica a este modelo de brinquedo, pois provavelmente você poderá executar o modelo em qualquer dtype em que cada lote consista em todo o dataset MNIST de 60.000 imagens.
Escalonamento de perdas
O escalonamento de perdas é uma técnica que tf.keras.Model.fit
executa automaticamente com a política mixed_float16
para evitar underflow numérico. Esta seção descreve o que é o escalonamento de perdas e a próxima seção descreve como usá-lo com um loop de treinamento personalizado.
Observação: Ao usar a política mixed_bfloat16
, não há necessidade de escalonar perdas.
Underflow e overflow
O tipo de dados float16 possui uma faixa dinâmica estreita em comparação com float32. Isso significa que valores acima de irão transbordar positivamente (overflow) para o infinito e valores abaixo de irão transbordar negativamente (underflow) para zero. float32 e bfloat16 têm uma faixa dinâmica muito maior, de modo que overflow e underflow não são um problema.
Por exemplo:
Na prática, o overflow com float16 raramente ocorre. Além disso, o underflow também raramente ocorre durante o passo para frente. No entanto, durante o passo para trás, os gradientes podem sofrer um underflow para zero. O escalonamento de perdas é uma técnica para evitar esse underflow.
Visão geral do escalonamento de perdas
O conceito básico do escalonamento de perdas é simples: basta multiplicar a perda por algum número grande, digamos , que você obterá o valor da escala de perdas. Isto fará com que os gradientes também aumentem em , reduzindo bastante a chance de underflow. Depois que os gradientes finais forem calculados, divida-os por para trazê-los de volta aos valores corretos.
O pseudocódigo para este processo é:
Escolher uma escala de perdas pode ser complicado. Se a escala de perdas for muito baixa, poderá ainda ocorrer um underflow dos gradientes a zero. Se for muito alto, ocorre o problema oposto: poderá haver overflow dos gradientes até o infinito.
Para solucionar esse problema, o TensorFlow determina dinamicamente a escala de perda para que você não precise escolher uma manualmente. Se você usar tf.keras.Model.fit
, o escalonamento de perdas será feito para você, para que você não precise fazer nenhum trabalho extra. Se você usar um loop de treinamento personalizado, deverá usar explicitamente o wrapper especial do otimizador tf.keras.mixed_precision.LossScaleOptimizer
para usar o escalonamento de perdas. Isso é descrito na próxima seção.
Treinando o modelo com um loop de treinamento personalizado
Até agora, você treinou um modelo Keras com precisão mista usando tf.keras.Model.fit
. A seguir, você usará precisão mista com um loop de treinamento personalizado. Se você ainda não sabe o que é um ciclo de treinamento personalizado, leia primeiro o guia de treinamento personalizado.
A execução de um loop de treinamento personalizado com precisão mista requer duas alterações em relação à execução em float32:
Construir o modelo com precisão mista (você já fez isso)
Usar o escalonamento de perdas explicitamente se
mixed_float16
for usado.
Para o passo (2), você usará a classe tf.keras.mixed_precision.LossScaleOptimizer
, que envolve um otimizador e aplica o escalonamento de perdas. Por padrão, ele determina dinamicamente a escala de perdas para que você não precise escolher uma. Construa um LossScaleOptimizer
da seguinte maneira.
Se você quiser, é possível escolher uma escala de perdas explícita ou personalizar o comportamento do escalonamento de perdas, mas é altamente recomendável manter o comportamento padrão de escalonamento de perdas, pois ele funciona bem em todos os modelos conhecidos. Veja a documentação tf.keras.mixed_precision.LossScaleOptimizer
se desejar personalizar o comportamento do escalonamento de perdas.
Em seguida, defina o objeto de perda e os tf.data.Dataset
:
Depois defina a função do passo de treinamento. Você usará dois novos métodos do otimizador de escala de perdas para escalonar a perda e desescalonar os gradientes:
get_scaled_loss(loss)
: multiplica a perda pela escala de perdaget_unscaled_gradients(gradients)
: recebe uma lista de gradientes escalonados como entradas e divide cada um pela escala de perda para desescaloná-los
Essas funções devem ser utilizadas para evitar underflow nos gradientes. LossScaleOptimizer.apply_gradients
aplicará gradientes se nenhum deles tiver valores Inf
ou NaN
. Ele também atualizará a escala de perdas, reduzindo-a pela metade se os gradientes tiverem valores Inf
ou NaN
e potencialmente aumentando-a caso contrário.
O LossScaleOptimizer
provavelmente pulará os primeiros passos no início do treinamento. A escala de perdas começa alta para que a escala ideal possa ser determinada rapidamente. Após alguns passos, a escala de perdas se estabilizará e poucos passos serão ignorados. Este processo acontece automaticamente e não afeta a qualidade do treinamento.
Agora, defina o passo de teste:
Carregue os pesos iniciais do modelo para poder treinar novamente do zero:
Por fim, execute o loop de treinamento personalizado:
Dicas de desempenho da GPU
Aqui estão algumas dicas de desempenho ao usar precisão mista em GPUs.
Aumentando o tamanho do lote
Se isso não afetar a qualidade do modelo, tente executar com o dobro do tamanho do lote ao usar precisão mista. Como os tensores float16 usam metade da memória, isto geralmente permite dobrar o tamanho do lote sem ficar sem memória. Aumentar o tamanho do lote normalmente aumenta o rendimento do treinamento, ou seja, os elementos de treinamento por segundo em que seu modelo pode ser executado.
Garantindo que os Tensor Cores da GPU sejam usados
Conforme mencionado anteriormente, as GPUs NVIDIA modernas usam uma unidade de hardware especial chamada Tensor Cores, que pode multiplicar matrizes float16 muito rapidamente. No entanto, os Tensor Cores exigem que certas dimensões dos tensores sejam múltiplos de 8. Nos exemplos abaixo, um argumento fica em negrito se e somente se precisar ser um múltiplo de 8 para que os Tensor Cores sejam usados.
tf.keras.layers.Dense(units=64)
tf.keras.layers.Conv2d(filters=48, kernel_size=7, stride=3)
E da mesma forma para outras camadas convolucionais, como tf.keras.layers.Conv3d
tf.keras.layers.LSTM(units=64)
E da mesma forma para outras RNNs, como tf.keras.layers.GRU
tf.keras.Model.fit(epochs=2, batch_size=128)
Você deve tentar usar Tensor Cores sempre que possível. Se você quiser saber mais, veja o o guia de desempenho de aprendizado profundo da NVIDIA que descreve os requisitos exatos para usar Tensor Cores, bem como outras informações de desempenho relacionadas aos Tensor Cores.
XLA
O XLA é um compilador que pode aumentar ainda mais o desempenho de precisão mista, bem como o desempenho do float32 em menor grau. Consulte o guia XLA para mais detalhes.
Dicas de desempenho do TPU na nuvem
Assim como acontece com as GPUs, você deve tentar dobrar o tamanho do lote ao usar TPUs em nuvem porque os tensores bfloat16 usam metade da memória. Dobrar o tamanho do lote pode aumentar o rendimento do treinamento.
As TPUs não requerem nenhum outro ajuste específico de precisão mista para obter o desempenho ideal. Elas já exigem o uso de XLA. As TPUs se beneficiam do fato de certas dimensões serem múltiplos de , mas isto se aplica igualmente ao tipo float32 e à precisão mista. Consulte o guia de desempenho da TPU na nuvem para dicas gerais de desempenho para TPUs, que se aplicam à precisão mista e também aos tensores float32.
Resumo
Você deve usar precisão mista se usar TPUs, GPUs NVIDIA com capacidade de computação pelo menos 7.0 ou CPUs Intel com suporte para instruções AMX, pois isto melhorará o desempenho em até três vezes.
Você pode usar precisão mista com as seguintes linhas:
Se o seu modelo terminar em softmax, garanta que seja float32. E independentemente de como termina seu modelo, certifique-se de que a saída seja float32.
Se você usar um loop de treinamento personalizado com
mixed_float16
, além das linhas acima, será necessário empacotar seu otimizador com um wrappertf.keras.mixed_precision.LossScaleOptimizer
. Em seguida, chameoptimizer.get_scaled_loss
para escalonar a perda eoptimizer.get_unscaled_gradients
para desescalonar os gradientes.Se você usa um loop de treinamento personalizado com
mixed_bfloat16
, definir a global_policy mencionada acima será suficiente.Dobre o tamanho do lote de treinamento se isso não reduzir a exatidão da avaliação
Em GPUs, certifique-se de que a maioria das dimensões do tensor sejam múltiplos de para maximizar o desempenho
Para um exemplo de precisão mista usando a API tf.keras.mixed_precision
, veja funções e classes relacionadas ao desempenho do treinamento. Confira os modelos oficiais, como o Transformer, para mais detalhes.