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
- 参数,异步分布
- 机器分为 Parameter Server 和 Worker两类 (可理解为,生产者,消费者)
Parameter Serve: 负责管理,更新 参数和梯度 Worker: 负责计算,训练网络
- 将输入数据转发给 多个 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)
下面同上
自定义模型+训练 的 分布式改造
分布式效果不好??
- 可以考虑 把 batch_size 增大
- 可以尝试数据分布式
数据分布式代码:
strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
...
train_dataset = strategy.experimental_distribute_dataset(train_dataset)
test_dataset = strategy.experimental_distribute_dataset(test_dataset)