【Tensorflow2.x】设置GPU(内存自增长、指定GPU)

every blog every motto: You can do more than you think.

0. 前言

本节主要讲解有关GPU的设置。具体包括如何节约GPU资源。话不多说,下面进入正题:

1. 正文

1.1 引子

  1. tensorflow会默认将GPU(尽可能的)占满,这样会导致其他程序无法使用GPU,造成GPU内存和计算资源的浪费,通常有以下方法:
  • 设置内存自增长
  • 虚拟设备机制(类似window的磁盘)
  1. 多GPU的使用
  • 虚拟GPU和实际GPU
  • 手工设置和分布式机制
  1. API列表
    说明: 注释在相应代码的下面
tf.debugging.set_log_device_placement  
# 打印变量在哪个设备上
tf.config.experimental.set_visible_devices  
# 设置对本进程可见的设备
tf.config.experimental.list_logical_devices  
# 获取所有的逻辑设备
tf.config.experimental.list_physical_devices  
# 获取物理设备的列表
tf.config.experimental.set_memory_growth  
# 设置内存自增长
tf.config.experimental.VirtualDeviceConfiguration  
# 建立逻辑分区
tf.config.set_soft_device_placement 
# 自动分配变量到某个设备上
  1. 监控GPU情况
  • 常规
    在window控制台(win+R -> cmd)进入或是linux终端
nvidia-smi
  • 实时监控
    window
nvidi-smi -l

linux(ubuntu):

watch -n 0.1 nvidia-smi 

1.2 设置内存自增长

在默认情况下,程序会将GPU内存(尽可能)的占满,尽管其没有用到这么多的内存,如下图所示:
在这里插入图片描述
在程序开始的位置,设置内存自增长,代码如下:
说明: 在程序刚开始时,对GPU设置内存自增长,调用如下函数即可。

def set_GPU():
    """GPU相关设置"""

    # 打印变量在那个设备上
    # tf.debugging.set_log_device_placement(True)
    # 获取物理GPU个数
    gpus = tf.config.experimental.list_physical_devices('GPU')
    print('物理GPU个数为:', len(gpus))
    # 设置内存自增长
    for gpu in gpus:
        tf.config.experimental.set_memory_growth(gpu, True)
    print('-------------已设置完GPU内存自增长--------------')
    # 获取逻辑GPU个数
    logical_gpus = tf.config.experimental.list_logical_devices('GPU')
    print('逻辑GPU个数为:', len(logical_gpus))

在这里插入图片描述
GPU使用情况如下:
在这里插入图片描述

1.3 指定GPU可见

默认情况下,程序会使用第1个GPU,我们可以对其进行指定,即让程序在我们指定的GPU上运行。代码如下:

def set_GPU():
    """GPU相关设置"""

    # 打印变量在那个设备上
    # tf.debugging.set_log_device_placement(True)
    # 获取物理GPU个数
    gpus = tf.config.experimental.list_physical_devices('GPU')
    print('物理GPU个数为:', len(gpus))
    # 设置内存自增长
    for gpu in gpus:
        tf.config.experimental.set_memory_growth(gpu, True)
    print('-------------已设置完GPU内存自增长--------------')

    # 设置哪个GPU对设备可见,即指定用哪个GPU
    tf.config.experimental.set_visible_devices(gpus[-1], 'GPU')
    # 获取逻辑GPU个数
    logical_gpus = tf.config.experimental.list_logical_devices('GPU')
    print('逻辑GPU个数为:', len(logical_gpus))

在这里插入图片描述
说明:

  1. 本机只有1个GPU,所以程序在服务器上执行,因此,nvidia信息略有不同。
  2. 如代码所示,我们指定最后一块GPU可见,即,我们使用最后一块GPU。
  3. 物理GPU仍然是2个,现在逻辑GPU只有1个。
  4. 其中,我们依然设置了内存自增长。
    在这里插入图片描述在这里插入图片描述

1.4 GPU切分

说明:

  1. 类似对一块物理磁盘切分成几个区,即我们平常使用电脑时,可能电脑上只有一个物理磁盘,但我们会分成好几个区,如C盘,D盘,等。
  2. 物理GPU,即实实在在的一块GPU;逻辑GPU,即虚拟的GPU(由物理GPU到逻辑GPU的映射)
def set_GPU():
    """GPU相关设置"""

    # 打印变量在那个设备上
    # tf.debugging.set_log_device_placement(True)
    # 获取物理GPU个数
    gpus = tf.config.experimental.list_physical_devices('GPU')
    print('物理GPU个数为:', len(gpus))
    # 设置内存自增长
    # for gpu in gpus:
    #     tf.config.experimental.set_memory_growth(gpu, True)
    # print('-------------已设置完GPU内存自增长--------------')

    # 设置哪个GPU对设备可见,即指定用哪个GPU
    tf.config.experimental.set_visible_devices(gpus[-1], 'GPU')
    # 切分逻辑GPU
    tf.config.experimental.set_virtual_device_configuration(
        gpus[-1],  # 指定要切割的物理GPU
        # 切割的个数,和每块逻辑GPU的大小
        [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=4096),
         tf.config.experimental.VirtualDeviceConfiguration(memory_limit=4096), ]
    )
    # 获取逻辑GPU个数
    logical_gpus = tf.config.experimental.list_logical_devices('GPU')
    print('逻辑GPU个数为:', len(logical_gpus))

在这里插入图片描述
如下图所示,我们讲最后一块物理GPU切分成两块,现在逻辑GPU个数为2,不同于上面(1.2)中的逻辑GPU为1。
在这里插入图片描述
在这里插入图片描述

1.5 多GPU环境的使用

1.5.1 手动指定

代码如下:

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
  
gpus = tf.config.experimental.list_physical_devices('GPU')
print('物理GPU个数为:', len(gpus))
# 设置内存自增长
for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)
print('-------------已设置完GPU内存自增长--------------')

# 获取逻辑GPU个数
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
print('逻辑GPU个数为:', len(logical_gpus))

c = []
# 手动指定多GPU环境
for gpu in logical_gpus:
    print(gpu.name)
    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)

在这里插入图片描述
坏处:

  1. 太多要控制的细节
  2. 有的设备不支持

1.5.2 分布式策略

在这里插入图片描述

1. MirroredStrategy

  • 同步式分布训练
  • 适用于一机多卡情况
  • 每个GPU都有网络结构的所有参数,这些参数会被同步
  • 数据并行
    • Batch数据切为N份给各个GPU
    • 梯度聚合然后更新给各个GPU上的参数

2. CentralStorageStrategy

  • MirroredStrategy 的变种
  • 参数不是在每个GPU上,而是存储在一个设备上
    • CPU或者唯一的GPU上
  • 计算是在所有的GPU上并行的
    • 除了更新参数的计算之外

3. MultiWorkerMirroredStrategy

  • 类似于MirroredStrategy
  • 适用于多机多卡的情况

4. TPUStrategy

  • 与MirroredStrategy类似
  • 使用TPU上的策略

4. ParameterServerStrategy

  • 异步分布式
  • 更加适用于分布式系统
  • 机器分为Parameter Server 和worker两类
    • Parameter server负责整合梯度,更新参数
    • Worker负责计算,训练网络

在这里插入图片描述
在这里插入图片描述
同步与异步的优劣

  • 多机多卡
    • 异步可以避免短板效应
  • 一机多卡
    • 同步可以避免过多的通信
  • 异步的计算会增加模型的范围能力
    • 异步不是严格正确的,所以模型更容易容忍错误

1.6 小结

节约GPU内存资源方式:

  • 内存自增长
  • 逻辑切分

参考文献

[1] https://blog.csdn.net/weixin_39190382/article/details/104739572

猜你喜欢

转载自blog.csdn.net/weixin_39190382/article/details/110533410