Utiliser un GPU

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Le code TensorFlow et les modèles tf.keras s'exécuteront de manière transparente sur un seul GPU sans qu'aucune modification de code ne soit nécessaire.

Le moyen le plus simple de fonctionner sur plusieurs GPU, sur une ou plusieurs machines, consiste à utiliser les stratégies de distribution .

Ce guide est destiné aux utilisateurs qui ont essayé ces approches et ont constaté qu'ils avaient besoin d'un contrôle précis de la façon dont TensorFlow utilise le GPU. Pour savoir comment déboguer les problèmes de performances pour les scénarios à un ou plusieurs GPU, consultez le guide Optimiser les performances du GPU TensorFlow .

Installer

Assurez-vous que la dernière version du GPU TensorFlow est installée.

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

Aperçu

TensorFlow prend en charge l'exécution de calculs sur divers types d'appareils, y compris les CPU et les GPU. Ils sont représentés par des identifiants de chaîne, par exemple :

  • "/device:CPU:0" : Le CPU de votre machine.
  • "/GPU:0" : notation abrégée pour le premier GPU de votre machine visible par TensorFlow.
  • "/job:localhost/replica:0/task:0/device:GPU:1" : nom complet du deuxième GPU de votre machine visible par TensorFlow.

Si une opération TensorFlow a à la fois des implémentations CPU et GPU, par défaut, le périphérique GPU est prioritaire lorsque l'opération est attribuée. Par exemple, tf.matmul a à la fois des noyaux CPU et GPU et sur un système avec des périphériques CPU:0 et GPU:0 , le périphérique GPU:0 est sélectionné pour exécuter tf.matmul sauf si vous demandez explicitement de l'exécuter sur un autre périphérique.

Si une opération TensorFlow n'a pas d'implémentation GPU correspondante, l'opération revient au périphérique CPU. Par exemple, étant donné que tf.cast n'a qu'un noyau CPU, sur un système avec des périphériques CPU:0 et GPU:0 , le périphérique CPU:0 est sélectionné pour exécuter tf.cast , même s'il est demandé de s'exécuter sur le périphérique GPU:0 .

Emplacement du périphérique de journalisation

Pour savoir à quels appareils vos opérations et vos tenseurs sont affectés, placez tf.debugging.set_log_device_placement(True) comme première instruction de votre programme. L'activation de la journalisation du placement des périphériques entraîne l'impression de toutes les allocations ou opérations Tensor.

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)

Le code ci-dessus imprimera une indication que l' MatMul a été exécutée sur GPU:0 .

Placement manuel de l'appareil

Si vous souhaitez qu'une opération particulière s'exécute sur un appareil de votre choix au lieu de ce qui est automatiquement sélectionné pour vous, vous pouvez utiliser with tf.device pour créer un contexte d'appareil, et toutes les opérations dans ce contexte s'exécuteront sur le même appareil désigné .

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)

Vous verrez que maintenant a et b sont assignés à CPU:0 . Étant donné qu'un appareil n'a pas été explicitement spécifié pour l'opération MatMul , l'environnement d'exécution TensorFlow en choisira un en fonction de l'opération et des appareils disponibles ( GPU:0 dans cet exemple) et copiera automatiquement les tenseurs entre les appareils si nécessaire.

Limiter la croissance de la mémoire GPU

Par défaut, TensorFlow mappe la quasi-totalité de la mémoire GPU de tous les GPU (sous réserve de CUDA_VISIBLE_DEVICES ) visibles pour le processus. Ceci est fait pour utiliser plus efficacement les ressources de mémoire GPU relativement précieuses sur les appareils en réduisant la fragmentation de la mémoire. Pour limiter TensorFlow à un ensemble spécifique de GPU, utilisez la méthode 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

Dans certains cas, il est souhaitable que le processus n'alloue qu'un sous-ensemble de la mémoire disponible ou n'augmente l'utilisation de la mémoire qu'en fonction des besoins du processus. TensorFlow fournit deux méthodes pour contrôler cela.

La première option consiste à activer la croissance de la mémoire en appelant tf.config.experimental.set_memory_growth , qui tente d'allouer uniquement la quantité de mémoire GPU nécessaire pour les allocations d'exécution : il commence par allouer très peu de mémoire, et au fur et à mesure que le programme s'exécute et plus de mémoire GPU est nécessaire, la région de mémoire GPU est étendue pour le processus TensorFlow. La mémoire n'est pas libérée car cela peut entraîner une fragmentation de la mémoire. Pour activer la croissance de la mémoire pour un GPU spécifique, utilisez le code suivant avant d'allouer des Tensors ou d'exécuter des opérations.

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

Une autre façon d'activer cette option consiste à définir la variable d'environnement TF_FORCE_GPU_ALLOW_GROWTH sur true . Cette configuration est spécifique à la plate-forme.

La deuxième méthode consiste à configurer un périphérique GPU virtuel avec tf.config.set_logical_device_configuration et à définir une limite stricte sur la mémoire totale à allouer sur le 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

Ceci est utile si vous souhaitez vraiment limiter la quantité de mémoire GPU disponible pour le processus TensorFlow. Il s'agit d'une pratique courante pour le développement local lorsque le GPU est partagé avec d'autres applications telles qu'une interface graphique de poste de travail.

Utilisation d'un seul GPU sur un système multi-GPU

Si vous avez plus d'un GPU dans votre système, le GPU avec l'ID le plus bas sera sélectionné par défaut. Si vous souhaitez exécuter sur un GPU différent, vous devrez spécifier explicitement la préférence :

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

Si le périphérique que vous avez spécifié n'existe pas, vous obtiendrez une RuntimeError : .../device:GPU:2 unknown device .

Si vous souhaitez que TensorFlow choisisse automatiquement un appareil existant et pris en charge pour exécuter les opérations au cas où celui spécifié n'existerait pas, vous pouvez appeler 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)

Utilisation de plusieurs GPU

Le développement pour plusieurs GPU permettra à un modèle de s'adapter aux ressources supplémentaires. Si vous développez sur un système avec un seul GPU, vous pouvez simuler plusieurs GPU avec des périphériques virtuels. Cela permet de tester facilement les configurations multi-GPU sans nécessiter de ressources supplémentaires.

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

Une fois que plusieurs GPU logiques sont disponibles pour l'environnement d'exécution, vous pouvez les utiliser avec tf.distribute.Strategy ou avec un placement manuel.

Avec tf.distribute.Strategy

La meilleure pratique pour utiliser plusieurs GPU consiste à utiliser tf.distribute.Strategy . Voici un exemple simple :

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

Ce programme exécutera une copie de votre modèle sur chaque GPU, en divisant les données d'entrée entre eux, également connu sous le nom de « parallélisme des données ».

Pour plus d'informations sur les stratégies de distribution, consultez le guide ici .

Placement manuel

tf.distribute.Strategy fonctionne sous le capot en répliquant le calcul sur tous les appareils. Vous pouvez implémenter manuellement la réplication en construisant votre modèle sur chaque GPU. Par example:

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)