@nikkyoya(u2331987) 2021.1.15

使用GPU训练:控制显存资源

背景问题 使用Tf.keras训练分类模型,训练中报错:Blas GEMM launch failed ```powershell InternalError: Blas GEMM launch failed : a.shape=(32, 784), b.shape=(784, 300), m=32, n=300, k=784 [[node sequential/dense/MatMul (defined at :1) ]] [Op:__inference_train_function_494]

Function call stack: train_function

  1. **错误原因****
  2. 1. 由于其他pythonx程序占用了GPU资源,导致现有程序无法分配足够的资源去执行当前程序;
  3. 1. tensorflow-gpu默认占用所有GPU的几乎全部显存(为了减少内存碎片,更有效地利用设备上相对宝贵的 GPU 内存资源)
  4. <a name="CsCpq"></a>
  5. ### 解决方法
  6. <a name="wToUj"></a>
  7. #### (一)获取当前主机上特定运算设备的列表
  8. ```python
  9. # 获取所有GPU组成list
  10. gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
  11. # 获取所有CPU组成list
  12. cpus = tf.config.experimental.list_physical_devices(device_type='CPU')
  13. print(gpus, cpus)

(二)限制当前程序可用GPU范围

方法一:设置程序可见CPU范围tf.config.experimental.set_visible_devices

设置之后,当前程序只会使用自己可见的设备,不可见的设备不会被当前程序使用。

gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
  # Restrict TensorFlow to only use the first GPU
  try:
    tf.config.experimental.set_visible_devices(gpus[0], 'GPU')
    logical_gpus = tf.config.experimental.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)


方法二:使用环境变量 CUDA_VISIBLE_DEVICES

在终端输入

export CUDA_VISIBLE_DEVICES=2,3

或者在代码里加入

import os
os.environ['CUDA_VISIBLE_DEVICES'] = "2,3"

(三)控制每个(单)GPU的显存使用

方法一:动态申请显存tf.config.experimental.set_memory_growth

仅在需要时申请显存空间(程序初始运行时消耗很少的显存,随着程序的运行而动态申请显存)

单GPU

# 设置按需申请
tf.config.experimental.set_memory_growth(gpu[0], True)

多GPU

# 设置按需申请
for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)

方法二:直接限制显存占用(通过tf.config.experimental.set_virtual_device_configuration配置虚拟 GPU 设备)

程序不会超出限定的显存大小,若超出则报错

# 对需要进行限制的GPU进行设置
tf.config.experimental.set_virtual_device_configuration(gpus[0],                                                 [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=8192)])

该方法还可以在只有单GPU的环境模拟多GPU进行调试。

tf.config.experimental.set_virtual_device_configuration(
    gpus[0],
    [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=2048),
     tf.config.experimental.VirtualDeviceConfiguration(memory_limit=2048)])

补充:官方文档对GPU的使用指南(v2.4.0)

获得运算和张量分配到的目标设备

需将tf.debugging.set_log_device_placement(True)放在程序之前:

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)

手动设置运算执行设备

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]])
c = tf.matmul(a, b)
print(c)

使用多GPU系统上的单个GPU

如果系统上有多个 GPU,则默认情况下会选择具有最小 ID 的 GPU

指定需要的 GPU

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)

使用多个GPU(tf.distribute.MirroredStrategy

此程序会在每个 GPU 上运行模型的一个副本,并将输入数据拆分到每个 GPU 上,也就是所谓的“数据并行”。

tf.debugging.set_log_device_placement(True)
strategy = tf.distribute.MirroredStrategy()
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))

通过在每个 GPU 上构建模型来手动实现复制

tf.debugging.set_log_device_placement(True)
gpus = tf.config.experimental.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)

参考

官方文档:TensorFlow Core 使用GPU
【Tensorflow】Tensorflow 2.0 GPU的使用与分配
tensorflow2.0显存设置