[开发技巧]·TensorFlow&Keras GPU使用技巧

2019-06-27 14:16:01 浏览数 (1)

开发技巧·TensorFlow&Keras GPU使用技巧

1.问题描述

使用TensorFlow&Keras通过GPU进行加速训练时,有时在训练一个任务的时候需要去测试结果,或者是需要并行训练数据的时候就会显示OOM显存容量不足的错误。以下简称在训练一个任务的时候需要去测试结果,或者是需要并行训练数据为进行新的运算任务。

首先介绍下TensorFlow&Keras GPU使用的机制:TensorFlow&Keras会在有GPU可以使用时,自动将数据与运算放到GPU进行训练(这个不同于MXNet与PyTorch处理方式不同,MXNet与PyTorch需要手动编程去指定数据与运算的Device,这里不讨论这些方法之间的优劣,选择适合自己的就好了),默认充满GPU所有显存。

所以当用户在运行一个运算任务时会占据所有显存,如果再去开启一个新任务就会内存不足,引起OOM显存容量不足的错误。

2.问题分析

通过对上述问题解读,应该可以通过以下的方法解决:

  1. 当一个训练任务默认占据所有GPU显存的时候,可以使用CPU进行新的任务(这显然不是最优方法,使用CPU进行新的任务速度会很慢)
  2. 当一个训练任务默认占据所有GPU显存的时候,用户可以设定此任务占用的GPU显存大小,现在再使用GPU进行新的任务时,就可以并行运行了
  3. 如果有多个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

0 人点赞