模型执行后清除 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现在似乎有两种方法可以解决迭代训练模型,或者如果您使用未来的多进程池来服务模型训练,如果未来完成,池中的进程不会被杀死。您可以在训练过程中应用两种方法来释放 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_available
是False
,同时检查跨平台兼容性,您可以使用multiprocessing.get_context('spawn')
强制选择状态。 spawn
适用于 Windows、Linux 和 MacOS。更多关于上下文here
这是一个很好的答案。
我无法让它工作,也无法使用 Ben Usman 列出的小型可重复使用的包装器。问题是如果使用'spawn',则parallel_wrapper 不可提取,如果使用'fork',则进程挂起。很难相信在将近 4 年后,这仍然是 TF 的一个问题。有人知道一个好的分辨率吗?以上是关于模型执行后清除 TensorFlow GPU 内存的主要内容,如果未能解决你的问题,请参考以下文章