Użyj GPU

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

Kod tf.keras będą działać w przejrzysty sposób na pojedynczym procesorze graficznym bez konieczności wprowadzania zmian w kodzie.

Najprostszym sposobem na uruchomienie na wielu procesorach graficznych, na jednej lub wielu maszynach, jest użycie Strategii Dystrybucji .

Ten przewodnik jest przeznaczony dla użytkowników, którzy wypróbowali te podejścia i stwierdzili, że potrzebują precyzyjnej kontroli nad tym, jak TensorFlow korzysta z GPU. Aby dowiedzieć się, jak debugować problemy z wydajnością w scenariuszach z jednym i wieloma procesorami graficznymi, zobacz przewodnik Optymalizacja wydajności GPU TensorFlow .

Ustawiać

Upewnij się, że masz zainstalowaną najnowszą wersję procesora graficznego TensorFlow.

import tensorflow as tf
print("Num GPUs Available: ", len(tf.config.list_physical_devices('GPU')))
Num GPUs Available:  1

Przegląd

TensorFlow obsługuje wykonywanie obliczeń na różnych typach urządzeń, w tym CPU i GPU. Są one reprezentowane przez identyfikatory ciągów, na przykład:

  • "/device:CPU:0" : Procesor twojego komputera.
  • "/GPU:0" : Skrócona notacja dla pierwszego procesora graficznego Twojej maszyny, który jest widoczny dla TensorFlow.
  • "/job:localhost/replica:0/task:0/device:GPU:1" : w pełni kwalifikowana nazwa drugiego GPU komputera, który jest widoczny dla TensorFlow.

Jeśli operacja TensorFlow ma implementacje zarówno procesora CPU, jak i GPU, domyślnie urządzenie GPU ma priorytet, gdy operacja jest przypisywana. Na przykład tf.matmul ma zarówno jądra CPU, jak i GPU, a w systemie z urządzeniami CPU:0 i GPU:0 urządzenie GPU:0 jest wybrane do uruchomienia tf.matmul , chyba że wyraźnie zażądasz uruchomienia go na innym urządzeniu.

Jeśli operacja TensorFlow nie ma odpowiedniej implementacji GPU, operacja wraca do urządzenia z procesorem. Na przykład, ponieważ tf.cast ma tylko jądro procesora, w systemie z urządzeniami CPU:0 i GPU:0 , urządzenie CPU:0 jest wybierane do uruchomienia tf.cast , nawet jeśli zostanie uruchomione na urządzeniu GPU:0 .

Umieszczenie urządzenia rejestrującego

Aby dowiedzieć się, do jakich urządzeń są przypisane twoje operacje i tensory, umieść tf.debugging.set_log_device_placement(True) jako pierwszą instrukcję twojego programu. Włączenie rejestrowania rozmieszczenia urządzeń powoduje, że wszystkie alokacje lub operacje Tensora zostaną wydrukowane.

tf.debugging.set_log_device_placement(True)

# Create some tensors
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)

print(c)
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:0
tf.Tensor(
[[22. 28.]
 [49. 64.]], shape=(2, 2), dtype=float32)

Powyższy kod wyświetli informację, że MatMul została wykonana na GPU:0 .

Ręczne umieszczanie urządzenia

Jeśli chcesz, aby konkretna operacja została uruchomiona na wybranym urządzeniu, a nie na tym, co jest automatycznie wybierane, możesz użyć with tf.device do utworzenia kontekstu urządzenia, a wszystkie operacje w tym kontekście będą uruchamiane na tym samym wyznaczonym urządzeniu .

tf.debugging.set_log_device_placement(True)

# Place tensors on the CPU
with tf.device('/CPU:0'):
  a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
  b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])

# Run on the GPU
c = tf.matmul(a, b)
print(c)
Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:0
tf.Tensor(
[[22. 28.]
 [49. 64.]], shape=(2, 2), dtype=float32)

Zobaczysz, że teraz a i b są przypisane do CPU:0 . Ponieważ urządzenie nie zostało wyraźnie określone dla operacji MatMul , środowisko wykonawcze TensorFlow wybierze jedno na podstawie operacji i dostępnych urządzeń (w tym przykładzie GPU:0 ) i w razie potrzeby automatycznie skopiuje tensory między urządzeniami.

Ograniczenie wzrostu pamięci GPU

Domyślnie TensorFlow mapuje prawie całą pamięć GPU wszystkich procesorów graficznych (z zastrzeżeniem CUDA_VISIBLE_DEVICES ) widocznych dla procesu. Ma to na celu bardziej efektywne wykorzystanie stosunkowo cennych zasobów pamięci GPU na urządzeniach poprzez zmniejszenie fragmentacji pamięci. Aby ograniczyć TensorFlow do określonego zestawu GPU, użyj metody tf.config.set_visible_devices .

gpus = tf.config.list_physical_devices('GPU')
if gpus:
  # Restrict TensorFlow to only use the first GPU
  try:
    tf.config.set_visible_devices(gpus[0], 'GPU')
    logical_gpus = tf.config.list_logical_devices('GPU')
    print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPU")
  except RuntimeError as e:
    # Visible devices must be set before GPUs have been initialized
    print(e)
1 Physical GPUs, 1 Logical GPU

W niektórych przypadkach pożądane jest, aby proces alokował tylko podzbiór dostępnej pamięci lub zwiększał użycie pamięci tylko w miarę potrzeb procesu. TensorFlow udostępnia dwie metody kontrolowania tego.

Pierwszą opcją jest włączenie przyrostu pamięci przez wywołanie tf.config.experimental.set_memory_growth , który próbuje przydzielić tylko tyle pamięci GPU, ile jest potrzebne do alokacji środowiska wykonawczego: alokuje bardzo mało pamięci, a gdy program jest uruchamiany i potrzeba więcej pamięci GPU, region pamięci GPU jest rozszerzony dla procesu TensorFlow. Pamięć nie jest uwalniana, ponieważ może prowadzić do fragmentacji pamięci. Aby włączyć wzrost pamięci dla określonego GPU, użyj poniższego kodu przed przydzieleniem jakichkolwiek tensorów lub wykonaniem jakichkolwiek operacji.

gpus = tf.config.list_physical_devices('GPU')
if gpus:
  try:
    # Currently, memory growth needs to be the same across GPUs
    for gpu in gpus:
      tf.config.experimental.set_memory_growth(gpu, True)
    logical_gpus = tf.config.list_logical_devices('GPU')
    print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs")
  except RuntimeError as e:
    # Memory growth must be set before GPUs have been initialized
    print(e)
Physical devices cannot be modified after being initialized

Innym sposobem włączenia tej opcji jest ustawienie zmiennej środowiskowej TF_FORCE_GPU_ALLOW_GROWTH na true . Ta konfiguracja jest specyficzna dla platformy.

Druga metoda polega na skonfigurowaniu wirtualnego urządzenia GPU za pomocą tf.config.set_logical_device_configuration i ustawieniu sztywnego limitu całkowitej pamięci do przydzielenia na GPU.

gpus = tf.config.list_physical_devices('GPU')
if gpus:
  # Restrict TensorFlow to only allocate 1GB of memory on the first GPU
  try:
    tf.config.set_logical_device_configuration(
        gpus[0],
        [tf.config.LogicalDeviceConfiguration(memory_limit=1024)])
    logical_gpus = tf.config.list_logical_devices('GPU')
    print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs")
  except RuntimeError as e:
    # Virtual devices must be set before GPUs have been initialized
    print(e)
Virtual devices cannot be modified after being initialized

Jest to przydatne, jeśli chcesz naprawdę ograniczyć ilość pamięci GPU dostępnej dla procesu TensorFlow. Jest to powszechna praktyka w przypadku programowania lokalnego, gdy procesor graficzny jest współużytkowany z innymi aplikacjami, takimi jak graficzny interfejs użytkownika stacji roboczej.

Korzystanie z jednego procesora graficznego w systemie z wieloma procesorami graficznymi

Jeśli masz więcej niż jeden procesor GPU w swoim systemie, domyślnie wybrany zostanie procesor GPU o najniższym identyfikatorze. Jeśli chcesz pracować na innym GPU, musisz wyraźnie określić preferencje:

tf.debugging.set_log_device_placement(True)

try:
  # Specify an invalid GPU device
  with tf.device('/device:GPU:2'):
    a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
    b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
    c = tf.matmul(a, b)
except RuntimeError as e:
  print(e)
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:0

Jeśli określone urządzenie nie istnieje, otrzymasz RuntimeError : .../device:GPU:2 unknown device .

Jeśli chcesz, aby TensorFlow automatycznie wybierał istniejące i obsługiwane urządzenie do uruchamiania operacji w przypadku, gdy określone nie istnieje, możesz wywołać tf.config.set_soft_device_placement(True) .

tf.config.set_soft_device_placement(True)
tf.debugging.set_log_device_placement(True)

# Creates some tensors
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)

print(c)
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:0
tf.Tensor(
[[22. 28.]
 [49. 64.]], shape=(2, 2), dtype=float32)

Korzystanie z wielu procesorów graficznych

Programowanie dla wielu procesorów graficznych pozwoli na skalowanie modelu z dodatkowymi zasobami. W przypadku programowania w systemie z jednym procesorem GPU można symulować wiele procesorów GPU za pomocą urządzeń wirtualnych. Umożliwia to łatwe testowanie konfiguracji z wieloma procesorami graficznymi bez konieczności korzystania z dodatkowych zasobów.

gpus = tf.config.list_physical_devices('GPU')
if gpus:
  # Create 2 virtual GPUs with 1GB memory each
  try:
    tf.config.set_logical_device_configuration(
        gpus[0],
        [tf.config.LogicalDeviceConfiguration(memory_limit=1024),
         tf.config.LogicalDeviceConfiguration(memory_limit=1024)])
    logical_gpus = tf.config.list_logical_devices('GPU')
    print(len(gpus), "Physical GPU,", len(logical_gpus), "Logical GPUs")
  except RuntimeError as e:
    # Virtual devices must be set before GPUs have been initialized
    print(e)
Virtual devices cannot be modified after being initialized

Gdy w środowisku wykonawczym dostępnych jest wiele logicznych procesorów graficznych, można wykorzystać wiele procesorów graficznych za pomocą tf.distribute.Strategy lub umieszczania ręcznego.

Z tf.distribute.Strategy

Najlepszą praktyką przy korzystaniu z wielu procesorów graficznych jest użycie tf.distribute.Strategy . Oto prosty przykład:

tf.debugging.set_log_device_placement(True)
gpus = tf.config.list_logical_devices('GPU')
strategy = tf.distribute.MirroredStrategy(gpus)
with strategy.scope():
  inputs = tf.keras.layers.Input(shape=(1,))
  predictions = tf.keras.layers.Dense(1)(inputs)
  model = tf.keras.models.Model(inputs=inputs, outputs=predictions)
  model.compile(loss='mse',
                optimizer=tf.keras.optimizers.SGD(learning_rate=0.2))
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:GPU:0',)
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op RandomUniform in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Sub in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Mul in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AddV2 in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Fill in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Fill in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:CPU:0
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:CPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Fill in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:CPU:0
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
Executing op ReadVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op Identity in device /job:localhost/replica:0/task:0/device:CPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op VarHandleOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op AssignVariableOp in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op NoOp in device /job:localhost/replica:0/task:0/device:GPU:0

Ten program uruchomi kopię twojego modelu na każdym GPU, dzieląc dane wejściowe między nie, co jest również znane jako „ równoległość danych ”.

Aby uzyskać więcej informacji na temat strategii dystrybucji, zapoznaj się z przewodnikiem tutaj .

Ręczne umieszczanie

tf.distribute.Strategy działa pod maską, replikując obliczenia na różnych urządzeniach. Możesz ręcznie zaimplementować replikację, konstruując swój model na każdym GPU. Na przykład:

tf.debugging.set_log_device_placement(True)

gpus = tf.config.list_logical_devices('GPU')
if gpus:
  # Replicate your computation on multiple GPUs
  c = []
  for gpu in gpus:
    with tf.device(gpu.name):
      a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
      b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
      c.append(tf.matmul(a, b))

  with tf.device('/CPU:0'):
    matmul_sum = tf.add_n(c)

  print(matmul_sum)
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:GPU:0
Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:0
tf.Tensor(
[[22. 28.]
 [49. 64.]], shape=(2, 2), dtype=float32)