every blog every motto: You can do more than you think.
0. 前言
本节主要讲解有关GPU的设置。具体包括如何节约GPU资源。话不多说,下面进入正题:
1. 正文
1.1 引子
- tensorflow会默认将GPU(尽可能的)占满,这样会导致其他程序无法使用GPU,造成GPU内存和计算资源的浪费,通常有以下方法:
- 设置内存自增长
- 虚拟设备机制(类似window的磁盘)
- 多GPU的使用
- 虚拟GPU和实际GPU
- 手工设置和分布式机制
- 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
# 自动分配变量到某个设备上
- 监控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个GPU,所以程序在服务器上执行,因此,nvidia信息略有不同。
- 如代码所示,我们指定最后一块GPU可见,即,我们使用最后一块GPU。
- 物理GPU仍然是2个,现在逻辑GPU只有1个。
- 其中,我们依然设置了内存自增长。
1.4 GPU切分
说明:
- 类似对一块物理磁盘切分成几个区,即我们平常使用电脑时,可能电脑上只有一个物理磁盘,但我们会分成好几个区,如C盘,D盘,等。
- 物理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.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