Tensorflow2.0 GPU管理与分布式

0 / 1202

Nvidia命令

nvidia-smi 	# 查看GPU占用情况
watch -n 0.1  -x nvidia-smi 	# 动态实时0.1秒间隔,查看GPU占用情况。

GPU管理

为什么需要管理GPU?

默认TF程序运行会沾满耗尽GPU

如何管理GPU

使用内存增长式 API
内存增长解释: 按需分配

# 查看物理GPU信息
gpus = tf.config.experimental.list_physical_devices('GPU')  # 获取所有物理GPU信息

tf.config.experimental.set_visible_devices(gpus[2])      # 只使用 第3个GPU, 默认不设置就是使用所有GPU

for gpu in gpus:             
	tf.config.experimental.set_memory_growth(gpu, True)   # 这行代码必须放在前面
    
print(len(gpus))

GPU 逻辑切分 (分配大小)

就像划分CDE盘符一样,实际上还是一个整体的GPU

tf.config.experimental.set_virtual_device_configuration(
	gpus[2],    # 拿第3块物理 GPU来切分
	[
		tf.config.experimental.VirtualDeviceConfiguration(memory_limit=2048), # 实例化第一个蛋糕,并分为2G
		tf.config.experimental.VirtualDeviceConfiguration(memory_limit=2048), # 实例化第二个蛋糕,并分为2G
	]
)

使用逻辑切分好的GPU

# 查看逻辑GPU
logical_gpus = tf.config.experimental.list_logical_devices('GPU')  # 获取所有物理GPU信息
print(len(logical_gpus))

c = []
for gpu in logical_gpus:
	with tf.device(gpu.name):	# 指定GPU名字作为上下文环境,(将此GPU应用到上下文变量当中)
		a = xxx
		b = xxx
		c.append( a @ b )   		# 矩阵乘法,应用到了CPU,遍历一次,切换一个GPU
with tf.device('/CPU:0'): 		# 指定CPU名字作为上下文环境,(将此CPU应用到上下文变量当中)
	tf.add_n(c)

分布式策略

MirroredStrategy

镜像式策略:
0.一台机器, 多GPU
1.参数同步式,分布式训练
2.同步的意思是, 每个GPU的不同的参数会 按次序同步处理。
主要机制:
数据集 分发 给 不同的 GPU处理 (参考多线程)

CentralStorageStrategy

MirroredStrategy 变种
注意这里:  
	参数转变为 存储在一个设备上集中管理(可CPU,可GPU)
	而计算,依然是在所有GPU上运行

MultiWorkerMirroredStrategy

同 MirroredStrategy,只不过 可扩展为  在多台机器
参数同步式

ParameterServerStrategy

  1. 参数,异步分布
  2. 机器分为 Parameter Server 和 Worker两类 (可理解为,生产者,消费者)
    Parameter Serve: 负责管理,更新 参数和梯度
     Worker:  负责计算,训练网络
    
  3. 将输入数据转发给 多个 Worker
    Woker流程:
    3.1 Worker们, 训练后 将参数 push 回给 Server
     3.2 Worker们, 训练后 将 Server的参数 pull 拉过来
    

Server流程:

3.1 梯度聚合
3.2 梯度更新

总结同步式 vs 异步式

同步式:

就像分布式爬虫一样, 同步式,相当于在 Server中 加了一个缓冲管道。
等所有GPU齐了,再全部聚合更新。
缺点:
	有的机器计算快,有的机器计算慢,浪费时间  (短板效应,,拖后腿)
适用于:
	一台机器,多个GPU, 避免过多网络IO通信

异步式:

缺点+ 也算是优点:
	就像多线程对全局变量的不可控一样。可能参数错乱。
	但是这样训练出的模型,更有泛化能力(更能容忍错误, 所以这也算是个小优点)
    
适用于:
	多机器,多GPU

分布式实例(以MirroredStrategy为例,其他也一样)

keras中使用分布式:

strategy = tf.distribute.MirroredStrategy()
with strategy.scope():               # 同样制造一个上下文
	model = keras.models.Sequential([...])  # 模型定义部分放在上下文意味着,参数需要分布式管理
	model.add()
	...
	...
	model.add()
	model.compile(...)						# compile 放在上下文中,意味着,训练环节,同样也需要分布式训练
	# 至此结束

estimator使用分布式

原始,没有添加分布式的 estimator 代码

model = keras.Sequential([])
estimator = keras.estimator.model_to_estimator(model)
# 偷个懒,直接用Keras转过来的, 其实estimator 有很多内置模型(比如线性回归,逻辑回归)。和SKlearn用法差不多

# 所以在训练时,把 model.fit替换为 estimator.train
from sklearn.linear_model.base import make_dataset
estimator.train(
	input_fn= lambda: 自己处理数据的函数(参数),  # 此函数返回结果通常是 tf.data.Dataset对象()
	max_steps=1000      # 最多训练1000步数
)

# 训练结果的精度值等指标,通常输出到一个文件中,  可通过 tensorflow_board 打开查看

使用 分布式的 estimator 代码: 只需按照上面便跟几行即可:

strategy = tf.distribute.MirroredStrategy()         
config = tf.estimator.RunConfig(              # 主要加了这里
	train_distribute=strategy
)
model = keras.Sequential([])
estimator = keras.estimator.model_to_estimator(model, config=config)
下面同上

自定义模型+训练 的 分布式改造

分布式效果不好??

  1. 可以考虑 把 batch_size 增大
  2. 可以尝试数据分布式
    数据分布式代码:
    strategy = tf.distribute.MirroredStrategy()
    with strategy.scope():
    ...
    train_dataset = strategy.experimental_distribute_dataset(train_dataset)
    test_dataset = strategy.experimental_distribute_dataset(test_dataset)

原文出处