Mieszana precyzja

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Przegląd

Precyzja mieszana to użycie zarówno 16-bitowych, jak i 32-bitowych typów zmiennoprzecinkowych w modelu podczas uczenia, aby przyspieszyć jego działanie i zużywać mniej pamięci. Dzięki utrzymywaniu niektórych części modelu w typach 32-bitowych w celu zapewnienia stabilności numerycznej, model będzie miał krótszy czas kroku i będzie trenował równie dobrze pod względem metryk oceny, takich jak dokładność. W tym przewodniku opisano, jak korzystać z interfejsu API Keras o mieszanej precyzji w celu przyspieszenia modeli. Korzystanie z tego interfejsu API może zwiększyć wydajność ponad 3 razy na nowoczesnych procesorach graficznych i 60% na TPU.

Obecnie większość modeli używa typu float32 dtype, który zajmuje 32 bity pamięci. Istnieją jednak dwa mniej precyzyjne typy d, float16 i bfloat16, z których każdy zajmuje 16 bitów pamięci. Nowoczesne akceleratory mogą szybciej wykonywać operacje w 16-bitowych dtypes, ponieważ mają wyspecjalizowany sprzęt do wykonywania 16-bitowych obliczeń, a 16-bitowe dtypes mogą być szybciej odczytywane z pamięci.

Procesory graficzne NVIDIA mogą uruchamiać operacje w float16 szybciej niż w float32, a TPU mogą wykonywać operacje w bfloat16 szybciej niż w float32. Dlatego te mniej precyzyjne typy dtypes powinny być używane, gdy tylko jest to możliwe, na tych urządzeniach. Jednak zmienne i kilka obliczeń powinny nadal znajdować się w float32 z powodów numerycznych, aby model trenował do tej samej jakości. API Keras o mieszanej precyzji pozwala na użycie kombinacji float16 lub bfloat16 z float32, aby uzyskać korzyści wydajnościowe z float16/bfloat16 oraz stabilność numeryczną z float32.

Ustawiać

import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import mixed_precision

Obsługiwany sprzęt

Chociaż precyzja mieszana będzie działać na większości sprzętu, przyspieszy to tylko modele na najnowszych procesorach graficznych NVIDIA i TPU w chmurze. Procesory graficzne NVIDIA obsługują kombinację float16 i float32, podczas gdy jednostki TPU obsługują kombinację bfloat16 i float32.

Wśród procesorów graficznych NVIDIA te z możliwościami obliczeniowymi 7.0 lub nowszymi odczują największą wydajność dzięki mieszanej precyzji, ponieważ mają specjalne jednostki sprzętowe, zwane rdzeniami tensorowymi, które przyspieszają mnożenie i konwolucję macierzy typu float16. Starsze procesory graficzne nie oferują żadnych korzyści w zakresie wydajności matematycznej przy użyciu mieszanej precyzji, jednak oszczędność pamięci i przepustowości może umożliwić pewne przyspieszenie. Możesz sprawdzić możliwości obliczeniowe swojego procesora graficznego na stronie internetowej NVIDIA CUDA GPU . Przykładami procesorów graficznych, które najbardziej skorzystają z mieszanej precyzji, są procesory graficzne RTX, V100 i A100.

Możesz sprawdzić typ GPU w następujący sposób. Polecenie istnieje tylko wtedy, gdy zainstalowane są sterowniki NVIDIA, więc w przeciwnym razie poniższe polecenie spowoduje wyświetlenie błędu.

nvidia-smi -L
GPU 0: Tesla V100-SXM2-16GB (UUID: GPU-99e10c4d-de77-42ee-4524-6c41c4e5e47d)

Wszystkie jednostki TPU w chmurze obsługują bfloat16.

Nawet na procesorach i starszych procesorach graficznych, gdzie nie oczekuje się przyspieszenia, interfejsy API o mieszanej precyzji mogą być nadal używane do testowania jednostek, debugowania lub po prostu do wypróbowania interfejsu API. Jednak na procesorach precyzja mieszana będzie działać znacznie wolniej.

Ustawianie polityki dtype

Aby użyć mieszanej precyzji w Keras, musisz utworzyć tf.keras.mixed_precision.Policy , zwykle określaną jako polityka dtype . Zasady dtype określają, w jakich warstwach dtypes będą działać. W tym przewodniku utworzysz politykę z ciągu 'mixed_float16' i ustawisz ją jako politykę globalną. Spowoduje to, że kolejno tworzone warstwy będą używać precyzji mieszanej z mieszanką float16 i float32.

policy = mixed_precision.Policy('mixed_float16')
mixed_precision.set_global_policy(policy)
INFO:tensorflow:Mixed precision compatibility check (mixed_float16): OK
Your GPU will likely run quickly with dtype policy mixed_float16 as it has compute capability of at least 7.0. Your GPU: Tesla V100-SXM2-16GB, compute capability 7.0

Krótko mówiąc, możesz bezpośrednio przekazać ciąg do set_global_policy , co jest zwykle wykonywane w praktyce.

# Equivalent to the two lines above
mixed_precision.set_global_policy('mixed_float16')

Polityka określa dwa ważne aspekty warstwy: dtype, w którym wykonywane są obliczenia warstwy, oraz dtype zmiennych warstwy. Powyżej utworzyłeś mixed_float16 (tj. mixed_precision.Policy utworzoną przez przekazanie ciągu 'mixed_float16' do jego konstruktora). Dzięki tej zasadzie warstwy używają obliczeń float16 i zmiennych float32. Obliczenia są wykonywane w float16 dla wydajności, ale zmienne muszą być przechowywane w float32 dla stabilności numerycznej. Możesz bezpośrednio zapytać o te właściwości zasad.

print('Compute dtype: %s' % policy.compute_dtype)
print('Variable dtype: %s' % policy.variable_dtype)
Compute dtype: float16
Variable dtype: float32

Jak wspomniano wcześniej, zasada mixed_float16 w największym stopniu poprawi wydajność procesorów graficznych NVIDIA z możliwościami obliczeniowymi co najmniej 7.0. Zasady będą działać na innych procesorach graficznych i procesorach, ale mogą nie poprawić wydajności. W przypadku jednostek TPU należy zamiast tego użyć zasady mixed_bfloat16 .

Budowanie modelu

Następnie zacznijmy budować prosty model. Bardzo małe modele zabawek zazwyczaj nie korzystają z mieszanej precyzji, ponieważ narzut związany ze środowiskiem wykonawczym TensorFlow zazwyczaj dominuje nad czasem wykonania, co sprawia, że ​​poprawa wydajności na GPU jest znikoma. Dlatego zbudujmy dwie duże warstwy Dense z 4096 jednostkami każda, jeśli używany jest GPU.

inputs = keras.Input(shape=(784,), name='digits')
if tf.config.list_physical_devices('GPU'):
  print('The model will run with 4096 units on a GPU')
  num_units = 4096
else:
  # Use fewer units on CPUs so the model finishes in a reasonable amount of time
  print('The model will run with 64 units on a CPU')
  num_units = 64
dense1 = layers.Dense(num_units, activation='relu', name='dense_1')
x = dense1(inputs)
dense2 = layers.Dense(num_units, activation='relu', name='dense_2')
x = dense2(x)
The model will run with 4096 units on a GPU

Każda warstwa ma politykę i domyślnie używa polityki globalnej. Każda z warstw Dense ma zatem mixed_float16 , ponieważ wcześniej ustawiłeś politykę globalną na mixed_float16 . Spowoduje to, że gęste warstwy będą wykonywać obliczenia float16 i będą miały zmienne float32. Wrzucają swoje dane wejściowe do float16, aby wykonać obliczenia float16, co w rezultacie powoduje, że ich dane wyjściowe będą float16. Ich zmienne to float32 i będą rzutowane na float16, gdy warstwy są wywoływane, aby uniknąć błędów wynikających z niezgodności typu dtype.

print(dense1.dtype_policy)
print('x.dtype: %s' % x.dtype.name)
# 'kernel' is dense1's variable
print('dense1.kernel.dtype: %s' % dense1.kernel.dtype.name)
<Policy "mixed_float16">
x.dtype: float16
dense1.kernel.dtype: float32

Następnie utwórz prognozy wyjściowe. Zwykle prognozy wyjściowe można utworzyć w następujący sposób, ale nie zawsze jest to stabilne numerycznie w przypadku float16.

# INCORRECT: softmax and model output will be float16, when it should be float32
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
print('Outputs dtype: %s' % outputs.dtype.name)
Outputs dtype: float16

Aktywacja softmax na końcu modelu powinna być float32. Ponieważ zasadą dtype jest mixed_float16 , aktywacja softmax normalnie zawierałaby tensory float16 obliczające dtype i wyprowadzające tensory float16.

Można to naprawić, oddzielając warstwy Dense i softmax oraz przekazując dtype='float32' do warstwy softmax:

# CORRECT: softmax and model output are float32
x = layers.Dense(10, name='dense_logits')(x)
outputs = layers.Activation('softmax', dtype='float32', name='predictions')(x)
print('Outputs dtype: %s' % outputs.dtype.name)
Outputs dtype: float32

Przekazanie dtype='float32' do konstruktora warstwy softmax nadpisuje politykę dtype warstwy jako politykę float32 , która wykonuje obliczenia i przechowuje zmienne w float32. Równoważnie można było zamiast tego przekazać dtype=mixed_precision.Policy('float32') ; warstwy zawsze konwertują argument dtype na politykę. Ponieważ warstwa Activation nie ma żadnych zmiennych, zmienna dtype polityki jest ignorowana, ale dtype obliczeniowe polityki float32 powoduje, że softmax i dane wyjściowe modelu są float32.

Dodanie softmaxu float16 w środku modelu jest w porządku, ale softmax na końcu modelu powinien być w float32. Powodem jest to, że jeśli pośrednim tensorem płynącym od softmaxu do straty jest float16 lub bfloat16, mogą wystąpić problemy numeryczne.

Możesz nadpisać dtype dowolnej warstwy jako float32, przekazując dtype='float32' , jeśli uważasz, że nie będzie ona stabilna numerycznie z obliczeniami float16. Ale zazwyczaj jest to konieczne tylko w ostatniej warstwie modelu, ponieważ większość warstw ma wystarczającą precyzję z mixed_float16 i mixed_bfloat16 .

Nawet jeśli model nie kończy się softmaxem, dane wyjściowe nadal powinny być float32. Chociaż nie jest to konieczne w przypadku tego konkretnego modelu, dane wyjściowe modelu można rzutować na float32 w następujący sposób:

# The linear activation is an identity function. So this simply casts 'outputs'
# to float32. In this particular case, 'outputs' is already float32 so this is a
# no-op.
outputs = layers.Activation('linear', dtype='float32')(outputs)

Następnie zakończ i skompiluj model oraz wygeneruj dane wejściowe:

model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop(),
              metrics=['accuracy'])

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

Ten przykład rzutuje dane wejściowe z int8 na float32. Nie rzutujesz na float16, ponieważ dzielenie przez 255 odbywa się na procesorze, który wykonuje operacje float16 wolniej niż operacje float32. W tym przypadku różnica wydajności jest znikoma, ale generalnie powinieneś uruchomić matematykę przetwarzania danych wejściowych w float32, jeśli działa na procesorze. Pierwsza warstwa modelu będzie rzutować dane wejściowe na float16, ponieważ każda warstwa przesyła dane zmiennoprzecinkowe do swojego typu dtype obliczeniowego.

Pobierane są początkowe wagi modelu. Umożliwi to ponowne trenowanie od podstaw poprzez obciążenie ciężarów.

initial_weights = model.get_weights()

Trening modelu za pomocą Model.fit

Następnie wytrenuj model:

history = model.fit(x_train, y_train,
                    batch_size=8192,
                    epochs=5,
                    validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=2)
print('Test loss:', test_scores[0])
print('Test accuracy:', test_scores[1])
Epoch 1/5
6/6 [==============================] - 2s 78ms/step - loss: 4.9609 - accuracy: 0.4132 - val_loss: 0.6643 - val_accuracy: 0.8437
Epoch 2/5
6/6 [==============================] - 0s 34ms/step - loss: 0.7752 - accuracy: 0.7789 - val_loss: 0.3098 - val_accuracy: 0.9175
Epoch 3/5
6/6 [==============================] - 0s 34ms/step - loss: 0.3620 - accuracy: 0.8848 - val_loss: 0.3149 - val_accuracy: 0.8969
Epoch 4/5
6/6 [==============================] - 0s 34ms/step - loss: 0.2998 - accuracy: 0.9066 - val_loss: 0.2988 - val_accuracy: 0.9068
Epoch 5/5
6/6 [==============================] - 0s 33ms/step - loss: 0.2298 - accuracy: 0.9285 - val_loss: 0.5062 - val_accuracy: 0.8414
313/313 - 0s - loss: 0.5163 - accuracy: 0.8392
Test loss: 0.5163048505783081
Test accuracy: 0.8392000198364258

Zwróć uwagę, że model drukuje czas na krok w dziennikach: na przykład „25 ms/krok”. Pierwsza epoka może być wolniejsza, ponieważ TensorFlow poświęca trochę czasu na optymalizację modelu, ale później czas na krok powinien się ustabilizować.

Jeśli korzystasz z tego przewodnika w Colab, możesz porównać wydajność mieszanej precyzji z float32. Aby to zrobić, zmień politykę z mixed_float16 na float32 w sekcji „Ustawianie polityki dtype”, a następnie uruchom ponownie wszystkie komórki do tego momentu. W przypadku procesorów graficznych z możliwościami obliczeniowymi 7.X, czas na krok powinien znacznie wzrosnąć, co wskazuje, że mieszana precyzja przyspieszyła model. Pamiętaj, aby zmienić zasady z powrotem na mixed_float16 i ponownie uruchomić komórki przed kontynuowaniem korzystania z przewodnika.

W przypadku procesorów graficznych o mocy obliczeniowej co najmniej 8,0 (w przypadku procesorów graficznych Ampere i wyższych) prawdopodobnie nie zauważysz żadnej poprawy wydajności w modelu zabawkowym w tym przewodniku w przypadku korzystania z precyzji mieszanej w porównaniu z modelem float32. Wynika to z użycia TensorFloat-32 , który automatycznie używa matematyki o niższej precyzji w niektórych operacjach float32, takich jak tf.linalg.matmul . TensorFloat-32 zapewnia niektóre z zalet wydajności mieszanej precyzji podczas korzystania z float32. Jednak w rzeczywistych modelach nadal można zauważyć znaczną poprawę wydajności dzięki mieszanej precyzji ze względu na oszczędność przepustowości pamięci i operacje, których TensorFloat-32 nie obsługuje.

Jeśli używasz mieszanej precyzji na TPU, nie zobaczysz tak dużego wzrostu wydajności w porównaniu z mieszaną precyzją na GPU, zwłaszcza na GPU przed amperem. Dzieje się tak, ponieważ TPU wykonują pewne operacje w bfloat16 pod maską, nawet przy domyślnej polityce dtype float32. Jest to podobne do tego, w jaki sposób procesory graficzne Ampere domyślnie używają TensorFloat-32. W porównaniu z procesorami graficznymi Ampere, TPU zwykle osiągają mniejszy wzrost wydajności przy mieszanej precyzji w modelach rzeczywistych.

W przypadku wielu modeli ze świata rzeczywistego precyzja mieszana umożliwia również podwojenie rozmiaru partii bez wyczerpywania się pamięci, ponieważ tensory typu float16 zajmują połowę pamięci. Nie dotyczy to jednak tego modelu zabawki, ponieważ prawdopodobnie można go uruchomić w dowolnym typie dtype, w którym każda partia składa się z całego zestawu danych MNIST zawierającego 60 000 obrazów.

Skalowanie strat

Skalowanie strat to technika, którą tf.keras.Model.fit wykonuje automatycznie z polityką mixed_float16 , aby uniknąć niedopełnienia liczbowego. W tej sekcji opisano, czym jest skalowanie strat, a w następnej opisano, jak używać go z niestandardową pętlą treningową.

Niedopełnienie i przepełnienie

Typ danych float16 ma wąski zakres dynamiczny w porównaniu z float32. Oznacza to, że wartości powyżej \(65504\) przepełnią się do nieskończoności, a wartości poniżej \(6.0 \times 10^{-8}\) będą niedopełnione do zera. float32 i bfloat16 mają znacznie wyższy zakres dynamiki, dzięki czemu overflow i underflow nie stanowią problemu.

Na przykład:

x = tf.constant(256, dtype='float16')
(x ** 2).numpy()  # Overflow
inf
x = tf.constant(1e-5, dtype='float16')
(x ** 2).numpy()  # Underflow
0.0

W praktyce rzadko dochodzi do przepełnienia pływakiem16. Dodatkowo, niedomiar również rzadko występuje podczas przejazdu do przodu. Jednak podczas cofania nachyleń może dojść do zera. Skalowanie strat to technika zapobiegająca temu niedopełnieniu.

Przegląd skalowania strat

Podstawowa koncepcja skalowania strat jest prosta: po prostu pomnóż stratę przez pewną dużą liczbę, powiedzmy \(1024\), a otrzymasz wartość skali strat . Spowoduje to również skalowanie gradientów o \(1024\) , co znacznie zmniejszy prawdopodobieństwo niedopełnienia. Po obliczeniu ostatecznych gradientów podziel je przez \(1024\) , aby przywrócić im prawidłowe wartości.

Pseudokod tego procesu to:

loss_scale = 1024
loss = model(inputs)
loss *= loss_scale
# Assume `grads` are float32. You do not want to divide float16 gradients.
grads = compute_gradient(loss, model.trainable_variables)
grads /= loss_scale

Wybór skali strat może być trudny. Jeśli skala strat jest zbyt niska, gradienty mogą nadal spadać do zera. Jeśli jest zbyt wysoki, pojawia się odwrotny problem: gradienty mogą przelewać się do nieskończoności.

Aby rozwiązać ten problem, TensorFlow dynamicznie określa skalę strat, dzięki czemu nie musisz wybierać jej ręcznie. Jeśli używasz tf.keras.Model.fit , skalowanie strat jest wykonywane za Ciebie, więc nie musisz wykonywać żadnej dodatkowej pracy. Jeśli używasz niestandardowej pętli treningowej, musisz jawnie użyć specjalnej otoki optymalizatora tf.keras.mixed_precision.LossScaleOptimizer , aby użyć skalowania strat. Zostało to opisane w następnej sekcji.

Trenowanie modelu za pomocą niestandardowej pętli treningowej

Do tej pory trenowałeś model Keras z mieszaną precyzją przy użyciu tf.keras.Model.fit . Następnie użyjesz mieszanej precyzji z niestandardową pętlą treningową. Jeśli nie wiesz jeszcze, czym jest niestandardowa pętla treningowa, najpierw przeczytaj przewodnik dotyczący treningu niestandardowego .

Przeprowadzenie niestandardowej pętli treningowej z mieszaną precyzją wymaga dwóch zmian w porównaniu z uruchomieniem jej w trybie float32:

  1. Zbuduj model z mieszaną precyzją (już to zrobiłeś)
  2. Jawnie użyj skalowania strat, jeśli używany jest mixed_float16 .

W kroku (2) użyjesz klasy tf.keras.mixed_precision.LossScaleOptimizer , która otacza optymalizator i stosuje skalowanie strat. Domyślnie dynamicznie określa skalę strat, więc nie musisz jej wybierać. Skonstruuj LossScaleOptimizer w następujący sposób.

optimizer = keras.optimizers.RMSprop()
optimizer = mixed_precision.LossScaleOptimizer(optimizer)

Jeśli chcesz, możesz wybrać jawną skalę strat lub w inny sposób dostosować zachowanie skalowania strat, ale zdecydowanie zaleca się zachowanie domyślnego zachowania skalowania strat, ponieważ okazało się, że działa dobrze we wszystkich znanych modelach. Zapoznaj się z dokumentacją tf.keras.mixed_precision.LossScaleOptimizer , jeśli chcesz dostosować zachowanie skalowania strat.

Następnie zdefiniuj obiekt utraty i tf.data.Dataset s:

loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
train_dataset = (tf.data.Dataset.from_tensor_slices((x_train, y_train))
                 .shuffle(10000).batch(8192))
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(8192)

Następnie zdefiniuj funkcję kroku szkolenia. Użyjesz dwóch nowych metod z optymalizatora skali strat do skalowania strat i odskalowania gradientów:

  • get_scaled_loss(loss) : Mnoży stratę przez skalę strat
  • get_unscaled_gradients(gradients) : Pobiera listę przeskalowanych gradientów jako dane wejściowe i dzieli je przez skalę strat, aby je przeskalować

Te funkcje muszą być używane, aby zapobiec niedopełnieniu na pochyłościach. LossScaleOptimizer.apply_gradients zastosuje następnie gradienty, jeśli żaden z nich nie ma wartości Inf lub NaN . Zaktualizuje również skalę strat, zmniejszając ją o połowę, jeśli gradienty miały Inf s lub NaN s i potencjalnie zwiększając ją w przeciwnym razie.

@tf.function
def train_step(x, y):
  with tf.GradientTape() as tape:
    predictions = model(x)
    loss = loss_object(y, predictions)
    scaled_loss = optimizer.get_scaled_loss(loss)
  scaled_gradients = tape.gradient(scaled_loss, model.trainable_variables)
  gradients = optimizer.get_unscaled_gradients(scaled_gradients)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))
  return loss

LossScaleOptimizer prawdopodobnie pominie kilka pierwszych kroków na początku treningu. Skala strat zaczyna się wysoko, dzięki czemu można szybko określić optymalną skalę strat. Po kilku krokach skala strat ustabilizuje się i bardzo niewiele kroków zostanie pominiętych. Proces ten przebiega automatycznie i nie wpływa na jakość treningu.

Teraz zdefiniuj krok testowy:

@tf.function
def test_step(x):
  return model(x, training=False)

Załaduj początkowe wagi modelu, aby ponownie przeszkolić od zera:

model.set_weights(initial_weights)

Na koniec uruchom niestandardową pętlę treningową:

for epoch in range(5):
  epoch_loss_avg = tf.keras.metrics.Mean()
  test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='test_accuracy')
  for x, y in train_dataset:
    loss = train_step(x, y)
    epoch_loss_avg(loss)
  for x, y in test_dataset:
    predictions = test_step(x)
    test_accuracy.update_state(y, predictions)
  print('Epoch {}: loss={}, test accuracy={}'.format(epoch, epoch_loss_avg.result(), test_accuracy.result()))
Epoch 0: loss=4.869325160980225, test accuracy=0.7221999764442444
Epoch 1: loss=0.4893573224544525, test accuracy=0.878000020980835
Epoch 2: loss=0.36011582612991333, test accuracy=0.9440000057220459
Epoch 3: loss=0.27391332387924194, test accuracy=0.9318000078201294
Epoch 4: loss=0.247697651386261, test accuracy=0.933899998664856

Wskazówki dotyczące wydajności GPU

Oto kilka wskazówek dotyczących wydajności podczas używania mieszanej precyzji na procesorach graficznych.

Zwiększenie wielkości partii

Jeśli nie ma to wpływu na jakość modelu, spróbuj użyć podwójnej wielkości partii, używając mieszanej precyzji. Ponieważ tensory float16 zużywają połowę pamięci, często pozwala to podwoić rozmiar wsadu bez wyczerpania pamięci. Zwiększenie rozmiaru partii zwykle zwiększa przepustowość uczenia, tj. elementy uczące na sekundę, na których może działać model.

Zapewnienie użycia rdzeni tensorowych GPU

Jak wspomniano wcześniej, nowoczesne procesory graficzne NVIDIA wykorzystują specjalną jednostkę sprzętową o nazwie Tensor Cores, która może bardzo szybko mnożyć macierze float16. Jednak rdzenie tensorowe wymagają, aby pewne wymiary tensorów były wielokrotnością 8. W poniższych przykładach argument jest pogrubiony wtedy i tylko wtedy, gdy musi być wielokrotnością liczby 8, aby można było użyć rdzeni tensorowych.

  • tf.keras.layers.Dense ( jednostki=64 )
  • tf.keras.layers.Conv2d( filtry=48 , kernel_size=7, stride=3)
    • I podobnie dla innych warstw splotowych, takich jak tf.keras.layers.Conv3d
  • tf.keras.layers.LSTM ( jednostki=64 )
    • I podobnie w przypadku innych RNN, takich jak tf.keras.layers.GRU
  • tf.keras.Model.fit(epoki=2, wielkość_wsadu=128 )

Jeśli to możliwe, staraj się używać rdzeni tensorowych. Jeśli chcesz dowiedzieć się więcej, przewodnik dotyczący wydajności głębokiego uczenia NVIDIA opisuje dokładne wymagania dotyczące korzystania z rdzeni Tensor, a także inne informacje o wydajności związane z rdzeniami Tensor.

XLA

XLA to kompilator, który może dodatkowo zwiększyć wydajność mieszanej precyzji, a także w mniejszym stopniu wydajność float32. Szczegółowe informacje można znaleźć w przewodniku XLA .

Wskazówki dotyczące wydajności Cloud TPU

Podobnie jak w przypadku procesorów graficznych, powinieneś spróbować podwoić rozmiar partii podczas korzystania z Cloud TPU, ponieważ tensory bfloat16 zużywają połowę pamięci. Podwojenie rozmiaru partii może zwiększyć przepustowość uczenia.

TPU nie wymaga żadnego innego dostrajania o mieszanej precyzji, aby uzyskać optymalną wydajność. Wymagają już użycia XLA. TPU czerpie korzyści z posiadania pewnych wymiarów będących wielokrotnościami \(128\), ale dotyczy to zarówno typu float32, jak i mieszanej precyzji. Zapoznaj się z przewodnikiem dotyczącym wydajności Cloud TPU, aby uzyskać ogólne wskazówki dotyczące wydajności TPU, które dotyczą mieszanej precyzji oraz tensorów typu float32.

Streszczenie

  • Jeśli używasz jednostek TPU lub procesorów graficznych NVIDIA z co najmniej zdolnością obliczeniową 7.0, powinieneś używać precyzji mieszanej, ponieważ zwiększy to wydajność nawet trzykrotnie.
  • Możesz użyć mieszanej precyzji z następującymi liniami:

    # On TPUs, use 'mixed_bfloat16' instead
    mixed_precision.set_global_policy('mixed_float16')
    
  • Jeśli Twój model kończy się softmax, upewnij się, że jest to float32. I niezależnie od tego, na czym kończy się Twój model, upewnij się, że dane wyjściowe to float32.

  • Jeśli używasz niestandardowej pętli treningowej z mixed_float16 , oprócz powyższych wierszy musisz otoczyć optymalizator tf.keras.mixed_precision.LossScaleOptimizer . Następnie optimizer.get_scaled_loss , aby przeskalować straty, optimizer.get_unscaled_gradients , aby przeskalować gradienty.

  • Podwój rozmiar partii uczącej, jeśli nie zmniejsza to dokładności oceny

  • W przypadku procesorów graficznych upewnij się, że większość wymiarów tensora jest wielokrotnością \(8\) , aby zmaksymalizować wydajność

Więcej przykładów mieszanej precyzji przy użyciu interfejsu API tf.keras.mixed_precision w oficjalnym repozytorium modeli . Większość oficjalnych modeli, takich jak ResNet i Transformer , będzie działać z mieszaną precyzją przez przekazanie --dtype=fp16 .