开发技巧·TensorFlow&Keras GPU使用技巧
1.问题描述
使用TensorFlow&Keras通过GPU进行加速训练时,有时在训练一个任务的时候需要去测试结果,或者是需要并行训练数据的时候就会显示OOM显存容量不足的错误。以下简称在训练一个任务的时候需要去测试结果,或者是需要并行训练数据为进行新的运算任务。
首先介绍下TensorFlow&Keras GPU使用的机制:TensorFlow&Keras会在有GPU可以使用时,自动将数据与运算放到GPU进行训练(这个不同于MXNet与PyTorch处理方式不同,MXNet与PyTorch需要手动编程去指定数据与运算的Device,这里不讨论这些方法之间的优劣,选择适合自己的就好了),默认充满GPU所有显存。
所以当用户在运行一个运算任务时会占据所有显存,如果再去开启一个新任务就会内存不足,引起OOM显存容量不足的错误。
2.问题分析
通过对上述问题解读,应该可以通过以下的方法解决:
- 当一个训练任务默认占据所有GPU显存的时候,可以使用CPU进行新的任务(这显然不是最优方法,使用CPU进行新的任务速度会很慢)
- 当一个训练任务默认占据所有GPU显存的时候,用户可以设定此任务占用的GPU显存大小,现在再使用GPU进行新的任务时,就可以并行运行了
- 如果有多个GPU可以默认指定任务在不同GPU上。
3.使用教程
1.解决方法一:使用CPU进行新的任务
这不是最优方法,使用CPU进行新的任务速度会很慢,但是也是一种解决方式
代码语言:javascript复制import os
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
# 打印 TF 可用的 GPU
print(os.environ['CUDA_VISIBLE_DEVICES'])
# -1 表示不使用GPU
2.解决方法二:设定任务占用的GPU显存大小
这个是笔者比较推荐的方式,由于TensorFlow&Keras运行一个运算任务时会占据所有显存,其实有时并没有用到那么多。
这样做也会有点小问题就是,单个任务会变慢一点,笔者测试结果是在使用上述方法并行运行两个单个任务速度变为0.8左右,但是换来了可以运行两个任务,还是很值得的。(推测变慢的原因是两个任务并行运算时,对GPU压力更大,每个任务上分配的性能就会降低,类似于在电脑上跑多个任务,电脑会卡顿)
这样做要注意一点,在分配显存空间后,模型训练占据的内存要设置好(这个是指实际占用内存,可以通过修改batch_size来控制),不要超出你所分配的大小,不然会有不期望的结果出现。
代码语言:javascript复制import tensorflow as tf
# 在开启对话session前,先创建一个 tf.ConfigProto() 实例对象
gpuConfig = tf.ConfigProto(allow_soft_placement=True)
# 限制一个进程使用 60% 的显存
gpuConfig.gpu_options.per_process_gpu_memory_fraction = 0.6
# 把你的配置部署到session 变量名 sess 无所谓
sess1 =tf.Session(config=gpuConfig)
#这样,如果你指定的卡的显存是2000M的话,你这个进程只能用1200M。
输出结果(with 1228 MB memory,代表使用1228 MB,这与设置的0.6 * 2000相符)
代码语言:javascript复制Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 1228 MB memory) ->
physical GPU (device: 0, name: GeForce MX150, pci bus id: 0000:01:00.0, compute capability: 6.1)
3.解决方法三:多个GPU指定在不同GPU运行
如果条件允许,拥有多个,就可以把不同任务放置在不同GPU上,要注意如果是和同事共用,要约定好如何分配,免得大家都用了同一个。
设置方法与方法一类似。-1代表不使用,0代表第一个,1代表第二个
以两个GPU举例,第一个任务开头可以使用如下,第二个任务就把0改为1,多个GPU方法类似。注意一点要放置在开头位置。
代码语言:javascript复制import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
# 打印 TF 可用的 GPU
print(os.environ['CUDA_VISIBLE_DEVICES'])
# -1 表示不使用GPU 0代表第一个
如果多于两个GPU,想在某个任务设置多个GPU,可以使用下述方法
代码语言:javascript复制import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
# 打印 TF 可用的 GPU
print(os.environ['CUDA_VISIBLE_DEVICES'])
# -1 表示不使用GPU 0代表第一个
最后留个大家一个思考问题,os.environ'CUDA_VISIBLE_DEVICES' = '-1,0' 时会怎么样调用?
欢迎大家在评论区留言发布自己看法和解读。。
4.如何在多张GPU卡上使用Keras
我们建议有多张GPU卡可用时,使用TnesorFlow后端。
有两种方法可以在多张GPU上运行一个模型:数据并行/设备并行
大多数情况下,你需要的很可能是“数据并行”
数据并行
数据并行将目标模型在多个设备上各复制一份,并使用每个设备上的复制品处理整个数据集的不同部分数据。Keras在 keras.utils.multi_gpu_model 中提供有内置函数,该函数可以产生任意模型的数据并行版本,最高支持在8片GPU上并行。 请参考utils中的multi_gpu_model文档。 下面是一个例子:
代码语言:javascript复制from keras.utils import multi_gpu_model
# Replicates `model` on 8 GPUs.
# This assumes that your machine has 8 available GPUs.
parallel_model = multi_gpu_model(model, gpus=8)
parallel_model.compile(loss='categorical_crossentropy',
optimizer='rmsprop')
# This `fit` call will be distributed on 8 GPUs.
# Since the batch size is 256, each GPU will process 32 samples.
parallel_model.fit(x, y, epochs=20, batch_size=256)
数据并行利用多块GPU同时训练多个batch数据,运行在每块GPU上的模型为同一个神经网络,网络结构完全一样,并且共享模型参数。
设备并行
设备并行是在不同设备上运行同一个模型的不同部分,当模型含有多个并行结构,例如含有两个分支时,这种方式很适合。
这种并行方法可以通过使用TensorFlow device scopes实现,下面是一个例子:
代码语言:javascript复制# Model where a shared LSTM is used to encode two different sequences in parallel
input_a = keras.Input(shape=(140, 256))
input_b = keras.Input(shape=(140, 256))
shared_lstm = keras.layers.LSTM(64)
# Process the first sequence on one GPU
with tf.device_scope('/gpu:0'):
encoded_a = shared_lstm(tweet_a)
# Process the next sequence on another GPU
with tf.device_scope('/gpu:1'):
encoded_b = shared_lstm(tweet_b)
# Concatenate results on CPU
with tf.device_scope('/cpu:0'):
merged_vector = keras.layers.concatenate([encoded_a, encoded_b],
axis=-1)
多任务输出数据并行
在Keras版的Faster-RCNN,它由多个输出支路,也就是多个loss,在网络定义的时候一般会给命名,然后编译的时候找到不同支路layer的名字即可,就像这样:
代码语言:javascript复制model.compile(optimizer=optimizer,
loss={'main_output': jaccard_distance_loss, 'aux_output': 'binary_crossentropy'},
metrics={'main_output': jaccard_distance_loss, 'aux_output': 'acc'},
loss_weights={'main_output': 1., 'aux_output': 0.5})
其中main_output和aux_output就是认为定义的layer name,但是如果用了keras.utils.training_utils.multi_gpu_model()以后,名字就自动换掉了,变成默认的concatenate_1, concatenate_2等等,因此你需要先model.summary()一下,打印出来网络结构,然后弄明白哪个输出代表哪个支路,然后重新编译网络,如下:
代码语言:javascript复制from keras.optimizers import Adam, RMSprop, SGD
model.compile(optimizer=RMSprop(lr=0.045, rho=0.9, epsilon=1.0),
loss={'concatenate_1': jaccard_distance_loss, 'concatenate_2': 'binary_crossentropy'},
metrics={'concatenate_1': jaccard_distance_loss, 'concatenate_2': 'acc'},
loss_weights={'concatenate_1': 1., 'concatenate_2': 0.5})
而且在在Keras版的Faster-RCNN中,每个batch里,对RPN进行训练,测试后的结果作为检测网络的输入,来训练,最后把2个模型对参数的训练结果作为一个模型保存下来。
分布式
keras的分布式是利用TensorFlow实现的,要想完成分布式的训练,你需要将Keras注册在连接一个集群的TensorFlow会话上:
代码语言:javascript复制server = tf.train.Server.create_local_server()
sess = tf.Session(server.target)
from keras import backend as K
K.set_session(sess)
5.参考
1.https://www.cnblogs.com/tectal/p/9048184.html
2.https://blog.csdn.net/qq_34564612/article/details/79209965