作为GPU实施的一部分,我该怎么做才能在CPU上运行特定的TensorFlow计算?

oro*_*ome 3 memory-management gpu out-of-memory tensorflow

我对如何有效地将TensorFlow操作和变量分配给设备感到困惑。显然,至少对于我的基本卷积神经网络的实现而言,在GPU上放置尽可能多的操作是可取的。但是我目前可以访问的GPU的内存有限,并导致出现许多形式的警告

Ran out of memory trying to allocate 2.60GiB. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory is available.

某些特定操作偶尔会崩溃,例如

Ran out of memory trying to allocate 83.74MiB.  See logs for memory state.
Resource exhausted: OOM when allocating tensor with shape[28000,1,28,28]
Run Code Online (Sandbox Code Playgroud)

可以通过在CPU上放置变量来避免这些情况,但是在我的实现中,这导致训练时期需要10倍的计算时间。

显然,理想的策略是识别出会产生错误的特定代码块,并尝试仅将这些代码放置在CPU上。但是我不清楚如何执行此操作,因为这些计算无法与需要GPU放置才能实现效率的计算隔离开来。

例如,只需使用以下内容在测试集上生成预测

evals = sess.run(tf.argmax(y, 1), feed_dict={x: use_x_all})
Run Code Online (Sandbox Code Playgroud)

xtf.placeholder我的模型的输入,并且y是我的网络的输出激活时,当use_x_all数组很大时(在此处带有28000示例)会产生上述错误。尝试将这种计算单独放在CPU上失败,这可能是因为网络评估产生y在GPU上。

因此,我(似乎)需要诉诸诸如

use_x_all, _ = data_loader.stack_data(use_data, as_cols=False)
use_x_split = np.split(use_x_all, splits)
for use_x in use_x_split:
    # ... (full example below)
    evals_part = sess.run(tf.argmax(y, 1), feed_dict={x: use_x})
    # accumulate evals
Run Code Online (Sandbox Code Playgroud)

这显然无法扩展。

有没有更好的办法?特别:

  • 有没有一种方法可以将上述计算结果放在CPU上,并且仍然在GPU上运行相同图形的计算结果(例如训练)?

或者

  • 是否有一种习惯用法(例如批处理)可以在这种情况下更轻松地应用,以减少此类计算的内存需求?

实际上,令我惊讶的是后者不是TensorFlow API的一部分。不需要上面的代码就可以自动分解不适合设备的计算吗?


我的代码中的完整示例:

f = open('{0:s}/{1:s}_{2:3.0f}.csv'.format(FLAGS.pred_dir, FLAGS.session_name,
                                                       10000*float(sess.run(accuracy, feed_dict=valid_feed))), 'w')
f.write('ImageId,Label\n')
use_x_all, _ = data_loader.stack_data(use_data, as_cols=False)
            use_x_split = np.split(use_x_all, splits)
last = 0
buff = ''
for use_x in use_x_split:
    evals = sess.run(tf.argmax(y, 1), feed_dict={x: use_x})
    f.write('\n'.join('{0},{1}'.format(r[0]+ last, r[1]) for r in enumerate(evals, start=1)))
    last += int(len(use_x_all)/splits)
    if last < len(use_x_all):
        f.write('\n')
f.close()
Run Code Online (Sandbox Code Playgroud)

rda*_*olf 5

简短的答案: 您可以拆分计算,但是您需要考虑正确的方法。同样,小批量生产是在这里考虑的合理习惯。

长答案:

可以进行异构放置,但是我认为这比您所允许的要复杂得多。考虑以下(抽象,简化)设置:

设定

这里的问题是,评估该网络所需的所有张量都无法适合我们的G​​PU。不幸的是,由于内存分配问题通常是一个总的问题,所以它并不像“识别产生错误的特定代码块”那样简单。IE,这些错误只是总和过大的一种征兆,而不是表明特定分配过大的征兆-可以这么说,您只是看到一根破草的骆驼。

正如您所指出的那样,我们希望在GPU上运行计算以提高吞吐量,但是随意分配会在PCI接口上移动大量数据,从而浪费了我们将获得的任何性能提升:

隔断

在左侧的分区方案中,我们在接口上移动的数据比右侧的少得多。

除了可以通过多种其他方式划分计算图以实现不同的余额外...

其他分区

而且每一项在性能方面都会有所不同。这就是为什么TF不会自动对图形进行分区的原因。在一般情况下,这实际上不是一个简单的问题。

分区计算技巧

回到具体的问题。您的方法(小批量)是一种可行的方法。如果有效地缩小了所有分配请求的大小。正如其他人建议的那样,对它进行分区可能也是可行的方法,但是您应该明智地进行此操作。尝试最小化设备间的内存移动,并尝试将计算密集的图形(如大卷积层)放置在同一设备上。

一种有用的策略可能是(手动或使用张量板)计算图形中不同部分的张量大小。这应该使您感觉到网络之间有多大相对关系。反过来,这为如何对其进行分区提供了合理的指导。

最后,如果您仅进行推理(不进行任何训练),则另一种解决方案是一次仅评估某些网络。这是使用较小批处理的一种双重方法:您可以使用一些具有几个大批处理的小型网络,而不是使用具有许多小批处理的大型网络。