Google I/O è un involucro! Recupera le sessioni di TensorFlow Visualizza le sessioni

Usa una GPU

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza l'origine su GitHub Scarica quaderno

Il codice TensorFlow e i modelli tf.keras verranno eseguiti in modo trasparente su una singola GPU senza che siano necessarie modifiche al codice.

Il modo più semplice per eseguire su più GPU, su una o più macchine, è utilizzare le strategie di distribuzione .

Questa guida è per gli utenti che hanno provato questi approcci e hanno scoperto di aver bisogno di un controllo dettagliato su come TensorFlow utilizza la GPU. Per informazioni su come eseguire il debug dei problemi di prestazioni per scenari con GPU singola e multi-GPU, consulta la guida Ottimizzazione delle prestazioni della GPU TensorFlow .

Impostare

Assicurati di avere installato l'ultima versione di TensorFlow gpu.

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

Panoramica

TensorFlow supporta l'esecuzione di calcoli su una varietà di tipi di dispositivi, tra cui CPU e GPU. Sono rappresentati con identificatori di stringa, ad esempio:

  • "/device:CPU:0" : La CPU della tua macchina.
  • "/GPU:0" : notazione abbreviata per la prima GPU della tua macchina visibile a TensorFlow.
  • "/job:localhost/replica:0/task:0/device:GPU:1" : nome completo della seconda GPU della tua macchina visibile a TensorFlow.

Se un'operazione TensorFlow ha implementazioni sia CPU che GPU, per impostazione predefinita, al dispositivo GPU viene assegnata la priorità quando viene assegnata l'operazione. Ad esempio, tf.matmul ha sia kernel CPU che GPU e su un sistema con dispositivi CPU:0 e GPU:0 , il dispositivo GPU:0 viene selezionato per eseguire tf.matmul a meno che tu non richieda esplicitamente di eseguirlo su un altro dispositivo.

Se un'operazione TensorFlow non ha un'implementazione GPU corrispondente, l'operazione ritorna al dispositivo CPU. Ad esempio, poiché tf.cast ha solo un kernel CPU, su un sistema con dispositivi CPU:0 e GPU:0 , il dispositivo CPU:0 viene selezionato per eseguire tf.cast , anche se richiesto per l'esecuzione sul dispositivo GPU:0 .

Posizionamento del dispositivo di registrazione

Per scoprire a quali dispositivi sono assegnate le tue operazioni e tensori, inserisci tf.debugging.set_log_device_placement(True) come prima istruzione del tuo programma. L'abilitazione della registrazione del posizionamento del dispositivo determina la stampa di eventuali allocazioni o operazioni del 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)

Il codice precedente stamperà un'indicazione che l' MatMul è stata eseguita su GPU:0 .

Posizionamento manuale del dispositivo

Se desideri che un'operazione particolare venga eseguita su un dispositivo di tua scelta invece di quello che è selezionato automaticamente per te, puoi utilizzare with tf.device per creare un contesto di dispositivo e tutte le operazioni all'interno di quel contesto verranno eseguite sullo stesso dispositivo designato .

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)

Vedrai che ora a sono assegnati b CPU:0 . Poiché un dispositivo non è stato specificato in modo esplicito per l'operazione MatMul , il runtime TensorFlow ne sceglierà uno in base all'operazione e ai dispositivi disponibili ( GPU:0 in questo esempio) e copierà automaticamente i tensori tra i dispositivi, se necessario.

Limitare la crescita della memoria della GPU

Per impostazione predefinita, TensorFlow mappa quasi tutta la memoria GPU di tutte le GPU (soggetto a CUDA_VISIBLE_DEVICES ) visibile al processo. Questo viene fatto per utilizzare in modo più efficiente le risorse di memoria GPU relativamente preziose sui dispositivi riducendo la frammentazione della memoria. Per limitare TensorFlow a un set specifico di GPU, utilizzare il metodo 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

In alcuni casi è auspicabile che il processo allochi solo un sottoinsieme della memoria disponibile o aumenti l'utilizzo della memoria solo in base alle esigenze del processo. TensorFlow fornisce due metodi per controllare questo.

La prima opzione è attivare la crescita della memoria chiamando tf.config.experimental.set_memory_growth , che tenta di allocare solo la quantità di memoria GPU necessaria per le allocazioni di runtime: inizia allocando pochissima memoria e quando il programma viene eseguito e è necessaria più memoria GPU, la regione di memoria GPU viene estesa per il processo TensorFlow. La memoria non viene rilasciata poiché può causare la frammentazione della memoria. Per attivare la crescita della memoria per una GPU specifica, usa il codice seguente prima di allocare qualsiasi tensore o eseguire qualsiasi operazione.

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

Un altro modo per abilitare questa opzione consiste nell'impostare la variabile ambientale TF_FORCE_GPU_ALLOW_GROWTH su true . Questa configurazione è specifica della piattaforma.

Il secondo metodo consiste nel configurare un dispositivo GPU virtuale con tf.config.set_logical_device_configuration e impostare un limite rigido sulla memoria totale da allocare sulla 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

Questo è utile se vuoi davvero limitare la quantità di memoria GPU disponibile al processo TensorFlow. Questa è una pratica comune per lo sviluppo locale quando la GPU è condivisa con altre applicazioni come una GUI per workstation.

Utilizzo di una singola GPU su un sistema multi-GPU

Se hai più di una GPU nel tuo sistema, la GPU con l'ID più basso verrà selezionata per impostazione predefinita. Se desideri eseguire su una GPU diversa, dovrai specificare la preferenza in modo esplicito:

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

Se il dispositivo che hai specificato non esiste, otterrai un RuntimeError : .../device:GPU:2 unknown device .

Se desideri che TensorFlow scelga automaticamente un dispositivo esistente e supportato per eseguire le operazioni nel caso in cui quello specificato non esista, puoi chiamare 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)

Utilizzo di più GPU

Lo sviluppo per più GPU consentirà a un modello di scalare con le risorse aggiuntive. Se stai sviluppando su un sistema con una singola GPU, puoi simulare più GPU con dispositivi virtuali. Ciò consente di testare facilmente le configurazioni multi-GPU senza richiedere risorse aggiuntive.

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

Una volta che sono disponibili più GPU logiche per il runtime, è possibile utilizzare più GPU con tf.distribute.Strategy o con il posizionamento manuale.

Con tf.distribute.Strategy

La procedura consigliata per l'utilizzo di più GPU consiste nell'usare tf.distribute.Strategy . Qui c'è un semplice esempio:

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

Questo programma eseguirà una copia del tuo modello su ciascuna GPU, suddividendo i dati di input tra di loro, noto anche come " parallelismo dei dati ".

Per ulteriori informazioni sulle strategie di distribuzione, consulta la guida qui .

Posizionamento manuale

tf.distribute.Strategy funziona sotto il cofano replicando il calcolo su tutti i dispositivi. Puoi implementare manualmente la replica costruendo il tuo modello su ciascuna GPU. Per esempio:

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)