понедельник, 19 ноября 2018 г.

TensorFlow: использование GPU

Поддерживаемые устройства

На типичной системе присутствует несколько вычислительных устройств. В TensorFlow поддерживаются устройства типов CPU и GPU. Они представляются как строки. Например:

  • "/cpu:0": CPU вашей машины.
  • "/device:GPU:0": GPU вашей машины, если он у вас присутствует.
  • "/device:GPU:1": второй GPU вашей машины и т.д.

Если TensorFlow операция имеет обе, CPU и GPU, реализации, то приоритет будет отдан GPU устройствам, когда операция назначена на устройство. Например, matmul имеет CPU и GPU ядра. На системе с устройствами cpu:0 и gpu:0, gpu:0 будет выбрано для выполнения matmul.

Логирование размещения на устройства

Для того чтобы выяснить каким устройствам назначены ваши операции и тензоры, создайте сессию с log_device_placement конфигурационной опцией, установленной в True.

# Создаем граф
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
# Создаем сессию с log_device_placement установленным в True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Выполняем операцию.
print(sess.run(c))

Должен быть следующий вывод:

Device mapping:
/job:localhost/replica:0/task:0/device:GPU:0 
-> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/device:GPU:0
a: /job:localhost/replica:0/task:0/device:GPU:0
MatMul: /job:localhost/replica:0/task:0/device:GPU:0
[[ 22.  28.]
 [ 49.  64.]]

Ручное размещение на устройствах

Если вы бы хотели, чтобы определенная операция выполнялась на устройстве по вашему выбору, вместо автоматически выбранного, используйте tf.device, чтобы создать контекст устройства, так чтобы все операции внутри этого контекста имели размещение на одном и том же устройстве.

# Создаем граф.
with tf.device('/cpu:0'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
# Создаем сессию с log_device_placement установленным в True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Выполняем операцию.
print(sess.run(c))

Теперь будет видно, что a и b назначены на cpu:0. Ввиду того, что устройство не определено явно в MatMul операции, окружение исполнения TensorFlow выберет устройство, основываясь на операции и доступных устройствах (gpu:0 в этом примере) и автоматически скопирует тензоры между устройствами если необходимо.

Device mapping:
/job:localhost/replica:0/task:0/device:GPU:0 
-> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/cpu:0
a: /job:localhost/replica:0/task:0/cpu:0
MatMul: /job:localhost/replica:0/task:0/device:GPU:0
[[ 22.  28.]
 [ 49.  64.]]

Позволение роста памяти GPU

По умолчанию, TensorFlow картирует почти все из элементов GPU памяти всех GPU (объект CUDA_VISIBLE_DEVICES) видимых процессу. Это сделано для более эффективного использования относительно точных ресурсов GPU памяти на устройствах, за счет уменьшения фрагментации памяти.

В некоторых случаях для процесса желательно занимать только часть доступной памяти, или наращивать использование памяти только когда процесс требуется в этом. TensorFlow предоставляет две Config опции на Session для контролирования этого.

Первая - это allow_growth опция, которая пробует занимать необходимое количество GPU памяти, основываясь на размере занятой памяти в среде исполнения: сначала занимается очень малый размер памяти, и по мере исполнения Session с увеличением потребности в GPU памяти, область GPU памяти расширяется соотвественно требуемому для TensorFlow процесса. Следует отметить, что мы не освобождаем память, ввиду того что это может привести к еще большей фрагментации памяти. Для того чтобы включить эту опцию, установите опцию в ConfigProto:

config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config, ...)

Второй метод - это per_process_gpu_memory_fraction опция, которая определяет долю общего объема памяти, которую должен выделять каждый видимый GPU. Например, вы можете сказать TensorFlow выделять только 40% всей памяти каждого GPU:

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.4
session = tf.Session(config=config, ...)

Это полезно, если вы хотите по-настоящему ограничить количество GPU памяти, доступной для TensorFlow процесса.

Использование единственного GPU в системе с многими GPU

Если существует более одного GPU в вашей системе, GPU с наименьшим ID будет выбран по умолчанию. Если вы хотели бы использовать исполнение на другом GPU, вам необходимо задать предпочтение явно:

# Создаем граф.
with tf.device('/device:GPU:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)
# Создаем сессию с log_device_placement установленным в True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Выполняем операцию.
print(sess.run(c))

Если устройство, которое вы определили не существует, вы получите InvalidArgumentError:

InvalidArgumentError: Invalid argument: 
Cannot assign a device to node 'b':
Could not satisfy explicit device specification '/device:GPU:2'
   [[{ {node b}} = Const[dtype=DT_FLOAT, 
   value=Tensor, _device="/device:GPU:2"]()]]

Если вы предпочитаете, чтобы TensorFlow автоматически выбирал существующее и поддерживаемое устройство для исполнения операций в случае, если определенное устройство не существует, вы можете установить allow_soft_placement в True в конфигурационной опции при создании сессии.

# Создаем граф.
with tf.device('/device:GPU:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)
# Создаем сессию с allow_soft_placement и log_device_placement 
# установленными в True.
sess = tf.Session(config=tf.ConfigProto(
      allow_soft_placement=True, log_device_placement=True))
# Выполняем операцию.
print(sess.run(c))

Использование нескольких GPU

Если вы хотели бы, чтобы TensorFlow выполнялся на нескольких GPU, вы можете конструировать вашу модель в многомерной манере, где каждый уровень назначен к различным GPU. Например:

# Создаем граф.
c = []
for d in ['/device:GPU:2', '/device:GPU:3']:
  with tf.device(d):
    a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3])
    b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2])
    c.append(tf.matmul(a, b))
with tf.device('/cpu:0'):
  sum = tf.add_n(c)
# Создаем сессию с log_device_placement установленными в True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Выполняем операцию.
print(sess.run(sum))

Вы увидите следующий вывод:

Device mapping:
/job:localhost/replica:0/task:0/device:GPU:0 
-> device: 0, name: Tesla K20m, pci bus
id: 0000:02:00.0
/job:localhost/replica:0/task:0/device:GPU:1 
-> device: 1, name: Tesla K20m, pci bus
id: 0000:03:00.0
/job:localhost/replica:0/task:0/device:GPU:2 
-> device: 2, name: Tesla K20m, pci bus
id: 0000:83:00.0
/job:localhost/replica:0/task:0/device:GPU:3 
-> device: 3, name: Tesla K20m, pci bus
id: 0000:84:00.0
Const_3: /job:localhost/replica:0/task:0/device:GPU:3
Const_2: /job:localhost/replica:0/task:0/device:GPU:3
MatMul_1: /job:localhost/replica:0/task:0/device:GPU:3
Const_1: /job:localhost/replica:0/task:0/device:GPU:2
Const: /job:localhost/replica:0/task:0/device:GPU:2
MatMul: /job:localhost/replica:0/task:0/device:GPU:2
AddN: /job:localhost/replica:0/task:0/cpu:0
[[  44.   56.]
 [  98.  128.]]