模型执行后清除 TensorFlow GPU 内存

Posted

技术标签:

【中文标题】模型执行后清除 TensorFlow GPU 内存【英文标题】:Clearing Tensorflow GPU memory after model execution 【发布时间】:2017-02-07 01:24:47 【问题描述】:

我已经训练了 3 个模型,现在正在运行的代码依次加载 3 个检查点中的每一个,并使用它们运行预测。我正在使用 GPU。

当第一个模型被加载时,它会预先分配整个 GPU 内存(我想用它来处理第一批数据)。但是完成后它不会卸载内存。当加载第二个模型时,同时使用tf.reset_default_graph()with tf.Graph().as_default(),GPU 内存仍然被第一个模型完全消耗,然后第二个模型内存不足。

除了使用 Python 子进程或多处理来解决这个问题(我通过谷歌搜索找到的唯一解决方案)之外,还有其他方法可以解决这个问题吗?

【问题讨论】:

如果删除会话(del sess)怎么办?这应该与重新启动进程对内存产生相同的影响 不应该 sess.close() (或使用 Session 作为with 的上下文)也可以吗? 我希望,我确实使用with ... sess:,也尝试过sess.close()。 GPU 内存不会被清除,清除默认图形并重建它肯定不会似乎起作用。也就是说,即使我在模型之间暂停 10 秒,我也看不到 GPU 上的内存使用nvidia-smi 清除。这并不一定意味着 tensorflow 没有在幕后正确处理事情,只是保持其内存分配不变。但我在验证这种推理时遇到了麻烦。 nvidia-smi 未正确报告 TensorFlow 可用的内存量。当 TensorFlow 计算释放内存时,它仍然会显示为为外部工具保留,但该内存可供 tensorflow 中的其他计算使用 @YaroslavBulatov 我做了更多的测试,并确认 tensorflow 在重置默认图表后在第二和第三个模型上按预期执行。如果您将其发布为答案,我会认为它是正确的。似乎这个问题是无关紧要的,虽然可能经常被问到,所以值得保持开放。 【参考方案1】:

我使用numba 发布GPU。使用TensorFlow,我找不到有效的方法。

import tensorflow as tf
from numba import cuda

a = tf.constant([1.0,2.0,3.0],shape=[3],name='a')
b = tf.constant([1.0,2.0,3.0],shape=[3],name='b')
with tf.device('/gpu:1'):
    c = a+b

TF_CONFIG = tf.ConfigProto(
gpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.1),
  allow_soft_placement=True)

sess = tf.Session(config=TF_CONFIG)
sess.run(tf.global_variables_initializer())
i=1
while(i<1000):
        i=i+1
        print(sess.run(c))

sess.close() # if don't use numba,the gpu can't be released
cuda.select_device(1)
cuda.close()
with tf.device('/gpu:1'):
    c = a+b

TF_CONFIG = tf.ConfigProto(
gpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.5),
  allow_soft_placement=True)

sess = tf.Session(config=TF_CONFIG)

sess.run(tf.global_variables_initializer())
while(1):
        print(sess.run(c))

【讨论】:

运行这段代码会给我一个tensorflow.python.framework.errors_impl.InternalError: Failed to create session.,前面是Failed precondition: Failed to memcopy into scratch buffer for device 0,当我尝试在cuda.close(1)之后运行tf.Session() 这会使 tensorflow 处于不良状态【参考方案2】:

我正在弄清楚 Jupyter Notebook 中哪个选项更好。即使完成了深度学习应用程序,Jupyter Notebook 也会永久占用 GPU 内存。它通常会导致令人头疼的 GPU Fan ERROR。在这种情况下,我必须重新设置 nvidia_uvm 并定期重启 linux 系统。我总结以下两个选项可以消除 GPU Fan Error 的头痛但想知道哪个更好。

环境:

CUDA 11.0 cuDNN 8.0.1 TensorFlow 2.2 Keras 2.4.3 Jupyter Notebook 6.0.3 Miniconda 4.8.3 Ubuntu 18.04 LTS

第一个选项

将以下代码放在单元格的末尾。内核在应用程序运行时完成后立即结束。但它并不优雅。 Juputer 将弹出一条消息,提示已终止的内核。

import os
 
pid = os.getpid()
!kill -9 $pid

部分选项

以下代码也可以用 Jupyter Notebook 结束内核。我不知道 numba 是否安全。 Nvidia 更喜欢“0” GPU,这是个人开发人员(不是服务器人员)最常用的 GPU。然而,Neil G 和 mradul dubey 都得到了回应:这让 GPU 处于糟糕的状态。

from numba import cuda

cuda.select_device(0)
cuda.close()

似乎第二个选项更优雅。有人可以确认哪个是最佳选择吗?

注意事项:

在Anaconda环境下直接执行“$python abc.py”自动释放GPU内存就不是这个问题了。但是,我有时需要使用 Jyputer Notebook 来处理 .ipynb 应用程序。

【讨论】:

我的测试表明 numba 是更好的选择。但是,用户需要使用 pip install numba 而不是 conda install -c numba mumba 或 sudo apt-get install python-3 numba。 conda install... 存在内部冲突,sudo apt-get install.. 无法使用。 如果在执行cuda.close() 后运行任何代码会导致Could not synchronize CUDA stream: CUDA_ERROR_INVALID_HANDLE: invalid resource handle 错误,有没有办法清除CUDA 内存而不出现此错误【参考方案3】:

你可以使用 numba 库来释放所有的 gpu 内存

pip install numba 
from numba import cuda 
device = cuda.get_current_device()
device.reset()

这将释放所有内存

【讨论】:

这会使 GPU 处于错误状态。 您介意解释一下“使 GPU 处于不良状态”的含义吗?这并没有告诉我们使用这种方法的后果。 我猜他的意思是这会导致“.\tensorflow/core/kernels/random_op_gpu.h:232] 非正常状态:GpuLaunchKernel(FillPhiloxRandomKernelLaunch, num_blocks, block_size , 0, d.stream(), gen, data, size, dist) 状态:内部:资源句柄无效”错误。至少,对我来说是这样。 也许是坏状态,他的意思是这会杀死内核。这不能在漫长的过程中完成【参考方案4】:

现在似乎有两种方法可以解决迭代训练模型,或者如果您使用未来的多进程池来服务模型训练,如果未来完成,池中的进程不会被杀死。您可以在训练过程中应用两种方法来释放 GPU 内存,同时您希望保留主进程。

    调用子流程来运行模型训练。当一阶段训练完成时,子进程将退出并释放内存。很容易得到返回值。 调用multiprocessing.Process(p)运行模型训练(p.start),p.join将指示进程退出并释放内存。

这是一个使用 multiprocess.Process 的辅助函数,它可以打开一个新进程来运行你的 python 编写的函数并返回值,而不是使用 Subprocess,

# open a new process to run function
def process_run(func, *args):
    def wrapper_func(queue, *args):
        try:
            logger.info('run with process id: '.format(os.getpid()))
            result = func(*args)
            error = None
        except Exception:
            result = None
            ex_type, ex_value, tb = sys.exc_info()
            error = ex_type, ex_value,''.join(traceback.format_tb(tb))
        queue.put((result, error))

    def process(*args):
        queue = Queue()
        p = Process(target = wrapper_func, args = [queue] + list(args))
        p.start()
        result, error = queue.get()
        p.join()
        return result, error  

    result, error = process(*args)
    return result, error

【讨论】:

我不确定我是否理解您所说的两种方法之间的区别。在我看来,它们都只是“使用多处理”。并且已经有一个很好的更详细的answer。 在我的意义上,“多处理”和“子进程”,它们都产生了新进程来处理 GPU 运行和释放,但以不同的方式运行 要注意的是multiprocessing.Queue而不是queue.Queue【参考方案5】:

一旦不再需要张量(在 .run 调用终止之前),张量分配的 GPU 内存就会被释放(回到 TensorFlow 内存池)。当变量容器被销毁时,分配给变量的 GPU 内存被释放。对于 DirectSession(即 sess=tf.Session("")),它是在会话关闭或显式重置时(在 62c159ff 中添加)

【讨论】:

就目前而言,tensorflow 仍然不会使用 sess.Close() 或使用 tf.Session() as sess: 释放 GPU 内存,您能否考虑上述 cmets 更新您的答案?跨度> @yaroslav-bulatov 您在您的 cmets 上提到 nvidia-smi 没有在 gpu 上显示正确的内存。我尝试了tf.reset_default_graph(),然后重建了之前的图表,但我有一个OOM 错误,这表明nvidia-smi 正在正确显示内存。有什么想法吗? @DiegoAgher 我的意思是 nvidia-smi 可能显示 0 可用内存,但仍有大量内存可供 TensorFlow 使用。原因是TensorFlow接管了内存管理 @yaroslavBulatov 那么,如果 tensorflow 池仍然存在并且再次构建图形时,我会收到 OOM 错误,您将如何释放 gpu 中的空间? 我也有OOM错误,这似乎是由于变量没有被释放。例如,模型将运行和训练多次,但在重新分配变量(不改变总大小)后,可能会出现 OOM 错误。关闭 Spyder 并重新打开是我唯一的办法..【参考方案6】:

2016 年 6 月的一个 git issue (https://github.com/tensorflow/tensorflow/issues/1727) 表明存在以下问题:

目前GPUDevice中的Allocator属于ProcessState, 这本质上是一个全局单例。第一次使用 GPU 初始化它,并在进程关闭时释放自己。

因此,唯一的解决方法是使用进程并在计算后关闭它们。

示例代码:

import tensorflow as tf
import multiprocessing
import numpy as np

def run_tensorflow():

    n_input = 10000
    n_classes = 1000

    # Create model
    def multilayer_perceptron(x, weight):
        # Hidden layer with RELU activation
        layer_1 = tf.matmul(x, weight)
        return layer_1

    # Store layers weight & bias
    weights = tf.Variable(tf.random_normal([n_input, n_classes]))


    x = tf.placeholder("float", [None, n_input])
    y = tf.placeholder("float", [None, n_classes])
    pred = multilayer_perceptron(x, weights)

    cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
    optimizer = tf.train.AdamOptimizer(learning_rate=0.001).minimize(cost)

    init = tf.global_variables_initializer()

    with tf.Session() as sess:
        sess.run(init)

        for i in range(100):
            batch_x = np.random.rand(10, 10000)
            batch_y = np.random.rand(10, 1000)
            sess.run([optimizer, cost], feed_dict=x: batch_x, y: batch_y)

    print "finished doing stuff with tensorflow!"


if __name__ == "__main__":

    # option 1: execute code with extra process
    p = multiprocessing.Process(target=run_tensorflow)
    p.start()
    p.join()

    # wait until user presses enter key
    raw_input()

    # option 2: just execute the function
    run_tensorflow()

    # wait until user presses enter key
    raw_input()

因此,如果您要在您创建的进程中调用函数run_tensorflow() 并关闭该进程(选项 1),则内存将被释放。如果您只是运行 run_tensorflow()(选项 2),则函数调用后不会释放内存。

【讨论】:

我写了一个small reusable wrapper,它使用了与这个答案相同的技巧。但是,性能下降很严重,这对于小型计算(即在小型数据集上进行推理)是可以的,但在任何其他情况下都不实用。我相信这一定是由于进程间通信和来回传递大型 numpy 对象。 我可以在加载 Keras 模型后使用它来释放 GPU 内存吗? 相关且重要的是,multiprocessing.Process 在 Windows 上使用 spawn 作为默认值,但在 *nix 系统上使用 fork。如果您发现自己处于在单独进程中运行的模型无法使用 GPU 的情况,即tf.test.is_gpu_availableFalse,同时检查跨平台兼容性,您可以使用multiprocessing.get_context('spawn') 强制选择状态。 spawn 适用于 Windows、Linux 和 MacOS。更多关于上下文here 这是一个很好的答案。 我无法让它工作,也无法使用 Ben Usman 列出的小型可重复使用的包装器。问题是如果使用'spawn',则parallel_wrapper 不可提取,如果使用'fork',则进程挂起。很难相信在将近 4 年后,这仍然是 TF 的一个问题。有人知道一个好的分辨率吗?

以上是关于模型执行后清除 TensorFlow GPU 内存的主要内容,如果未能解决你的问题,请参考以下文章

如何清除 Colab Tensorflow TPU 内存

tensorflow小记2

为啥 tensorflow 模块会占用所有 GPU 内存? [复制]

TensorFlow GPU内存

Tensorflow GPU训练模型时假卡死

为啥清除对象后GPU中的内存仍在使用中?