【问题标题】:Understanding the ResourceExhaustedError: OOM when allocating tensor with shape理解 ResourceExhaustedError: OOM 时用形状分配张量
【发布时间】:2017-09-06 04:35:47
【问题描述】:

我正在尝试使用 tensorflow 实现跳过思维模型,当前版本位于 here

目前我使用我机器的一个 GPU(总共 2 个 GPU),GPU 信息是

2017-09-06 11:29:32.657299: I tensorflow/core/common_runtime/gpu/gpu_device.cc:940] Found device 0 with properties:
name: GeForce GTX 1080 Ti
major: 6 minor: 1 memoryClockRate (GHz) 1.683
pciBusID 0000:02:00.0
Total memory: 10.91GiB
Free memory: 10.75GiB

但是,当我尝试向模型提供数据时,我得到了 OOM。我尝试调试如下:

我在运行sess.run(tf.global_variables_initializer())后立即使用以下sn-p

    logger.info('Total: {} params'.format(
        np.sum([
            np.prod(v.get_shape().as_list())
            for v in tf.trainable_variables()
        ])))

得到2017-09-06 11:29:51,333 INFO main main.py:127 - Total: 62968629 params,如果全部使用tf.float32,大约是240Mbtf.global_variables 的输出是

[<tf.Variable 'embedding/embedding_matrix:0' shape=(155229, 200) dtype=float32_ref>,
 <tf.Variable 'encoder/rnn/gru_cell/gates/kernel:0' shape=(400, 400) dtype=float32_ref>,
 <tf.Variable 'encoder/rnn/gru_cell/gates/bias:0' shape=(400,) dtype=float32_ref>,
 <tf.Variable 'encoder/rnn/gru_cell/candidate/kernel:0' shape=(400, 200) dtype=float32_ref>,
 <tf.Variable 'encoder/rnn/gru_cell/candidate/bias:0' shape=(200,) dtype=float32_ref>,
 <tf.Variable 'decoder/weights:0' shape=(200, 155229) dtype=float32_ref>,
 <tf.Variable 'decoder/biases:0' shape=(155229,) dtype=float32_ref>,
 <tf.Variable 'decoder/previous_decoder/rnn/gru_cell/gates/kernel:0' shape=(400, 400) dtype=float32_ref>,
 <tf.Variable 'decoder/previous_decoder/rnn/gru_cell/gates/bias:0' shape=(400,) dtype=float32_ref>,
 <tf.Variable 'decoder/previous_decoder/rnn/gru_cell/candidate/kernel:0' shape=(400, 200) dtype=float32_ref>,
 <tf.Variable 'decoder/previous_decoder/rnn/gru_cell/candidate/bias:0' shape=(200,) dtype=float32_ref>,
 <tf.Variable 'decoder/next_decoder/rnn/gru_cell/gates/kernel:0' shape=(400, 400) dtype=float32_ref>,
 <tf.Variable 'decoder/next_decoder/rnn/gru_cell/gates/bias:0' shape=(400,) dtype=float32_ref>,
 <tf.Variable 'decoder/next_decoder/rnn/gru_cell/candidate/kernel:0' shape=(400, 200) dtype=float32_ref>,
 <tf.Variable 'decoder/next_decoder/rnn/gru_cell/candidate/bias:0' shape=(200,) dtype=float32_ref>,
 <tf.Variable 'global_step:0' shape=() dtype=int32_ref>]

在我的训练短语中,我有一个形状为(164652, 3, 30)的数据数组,即sample_size x 3 x time_step,这里的3表示上一句、当前句和下一句。该训练数据的大小约为57Mb,并存储在loader 中。然后我用写一个生成器函数来获取句子,看起来像

def iter_batches(self, batch_size=128, time_major=True, shuffle=True):

    num_samples = len(self._sentences)
    if shuffle:
        samples = self._sentences[np.random.permutation(num_samples)]
    else:
        samples = self._sentences

    batch_start = 0
    while batch_start < num_samples:
        batch = samples[batch_start:batch_start + batch_size]

        lens = (batch != self._vocab[self._vocab.pad_token]).sum(axis=2)
        y, x, z = batch[:, 0, :], batch[:, 1, :], batch[:, 2, :]
        if time_major:
            yield (y.T, lens[:, 0]), (x.T, lens[:, 1]), (z.T, lens[:, 2])
        else:
            yield (y, lens[:, 0]), (x, lens[:, 1]), (z, lens[:, 2])
        batch_start += batch_size

训练循环看起来像

for epoch in num_epochs:
    batches = loader.iter_batches(batch_size=args.batch_size)
    try:
        (y, y_lens), (x, x_lens), (z, z_lens) =  next(batches)
        _, summaries, loss_val = sess.run(
        [train_op, train_summary_op, st.loss],
        feed_dict={
            st.inputs: x,
            st.sequence_length: x_lens,
            st.previous_targets: y,
            st.previous_target_lengths: y_lens,
            st.next_targets: z,
            st.next_target_lengths: z_lens
        })
    except StopIteraton:
        ...

然后我得到了一个OOM。如果我注释掉整个 try 正文(不提供数据),脚本运行得很好。

我不知道为什么我会在如此小的数据规模下得到 OOM。使用nvidia-smi 我总是得到 ​​p>

Wed Sep  6 12:03:37 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 384.59                 Driver Version: 384.59                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 108...  Off  | 00000000:02:00.0 Off |                  N/A |
|  0%   44C    P2    60W / 275W |  10623MiB / 11172MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GeForce GTX 108...  Off  | 00000000:03:00.0 Off |                  N/A |
|  0%   43C    P2    62W / 275W |  10621MiB / 11171MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0     32748    C   python3                                      10613MiB |
|    1     32748    C   python3                                      10611MiB |
+-----------------------------------------------------------------------------+

看不到我的脚本的 实际 GPU 使用情况,因为 tensorflow 总是在开始时窃取所有内存。而这里的实际问题是我不知道如何调试它。

我在 StackOverflow 上阅读了一些关于 OOM 的帖子。其中大部分发生在向模型提供大型测试集数据时,小批量提供数据可以避免该问题。但我不明白为什么在我的 11Gb 1080Ti 中看到如此小的数据和参数组合,因为它只是试图分配一个矩阵大小 [3840 x 155229] 的错误。 (解码器的输出矩阵,3840 = 30(time_steps) x 128(batch_size)155229是vocab_size)。

2017-09-06 12:14:45.787566: W tensorflow/core/common_runtime/bfc_allocator.cc:277] ********************************************************************************************xxxxxxxx
2017-09-06 12:14:45.787597: W tensorflow/core/framework/op_kernel.cc:1158] Resource exhausted: OOM when allocating tensor with shape[3840,155229]
2017-09-06 12:14:45.788735: W tensorflow/core/framework/op_kernel.cc:1158] Resource exhausted: OOM when allocating tensor with shape[3840,155229]
     [[Node: decoder/previous_decoder/Add = Add[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/gpu:0"](decoder/previous_decoder/MatMul, decoder/biases/read)]]
2017-09-06 12:14:45.790453: I tensorflow/core/common_runtime/gpu/pool_allocator.cc:247] PoolAllocator: After 2857 get requests, put_count=2078 evicted_count=1000 eviction_rate=0.481232 and unsatisfied allocation rate=0.657683
2017-09-06 12:14:45.790482: I tensorflow/core/common_runtime/gpu/pool_allocator.cc:259] Raising pool_size_limit_ from 100 to 110
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/client/session.py", line 1139, in _do_call
    return fn(*args)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/client/session.py", line 1121, in _run_fn
    status, run_metadata)
  File "/usr/lib/python3.6/contextlib.py", line 88, in __exit__
    next(self.gen)
  File "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/errors_impl.py", line 466, in raise_exception_on_not_ok_status
    pywrap_tensorflow.TF_GetCode(status))
tensorflow.python.framework.errors_impl.ResourceExhaustedError: OOM when allocating tensor with shape[3840,155229]
     [[Node: decoder/previous_decoder/Add = Add[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/gpu:0"](decoder/previous_decoder/MatMul, decoder/biases/read)]]
     [[Node: GradientDescent/update/_146 = _Recv[client_terminated=false, recv_device="/job:localhost/replica:0/task:0/cpu:0", send_device="/job:localhost/replica:0/task:0/gpu:0", send_device_incarnation=1, tensor_name="edge_2166_GradientDescent/update", tensor_type=DT_FLOAT, _device="/job:localhost/replica:0/task:0/cpu:0"]()]]

During handling of the above exception, another exception occurred:

任何帮助将不胜感激。提前致谢。

【问题讨论】:

  • “跳过思考”!很酷的模型名称。

标签: tensorflow tensorflow-gpu


【解决方案1】:

让我们一一划分问题:

关于tensorflow提前分配所有内存,可以使用下面的代码sn-p让tensorflow在需要的时候分配内存。以便您了解事情的进展情况。

gpu_options = tf.GPUOptions(allow_growth=True)
session = tf.InteractiveSession(config=tf.ConfigProto(gpu_options=gpu_options))

如果您愿意,这同样适用于 tf.Session() 而不是 tf.InteractiveSession()

关于尺寸的第二件事, 由于没有关于您的网络规模的信息,我们无法估计出了什么问题。但是,您也可以逐步调试所有网络。例如,创建一个只有一层的网络,获取它的输出,创建一次会话和馈送值,并可视化您消耗了多少内存。迭代此调试会话,直到您看到内存不足的点。

请注意,3840 x 155229 输出确实非常非常大。这意味着约 6 亿个神经元,每层仅约 2.22GB。如果您有任何类似大小的层,所有这些层加起来会很快填满您的 GPU 内存。

另外,这仅用于前向,如果您使用此层进行训练,则优化器添加的反向传播和层会将此大小乘以 2。因此,对于训练,您仅消耗约 5 GB 用于输出层。

我建议您修改网络并尝试减少批量大小/参数计数以使您的模型适合 GPU

【讨论】:

  • 感谢您的回答!我会尽快尝试gpu_options。关于网络规模,sn -p np.sum([np.prod(v.get_shape().as_list()) for v in tf.trainable_variables()]) 不是得到网络的整数(62968629) 参数吗?梯度加倍,总计2 * 62968629 * 4 / 1024/1024/1024 -&gt; 0.47G。而且,我的编码器中只有1 层,2 解码器中只有1 层。 3840 x 155229 是解码器输出,与参数无关,所以我认为反向传播时它不会翻倍?
  • 这个计算对于推理是正确的。我以为你做了一个全连接层,我的错。但是,对于训练,您需要使用 tf.global_variables() 而不是 trainable_variables() 作为优化器,并且您实现的所有其他附录将添加更多不可见参数。
  • 再次感谢。我打印了tf.global_variables()tf.trainable_variables() 的结果并更新了问题。在我的情况下,与前者相比,后者仅缺少 global_step 张量。
  • 新的 TF 2.0 内存分配方式如下:gpus = tf.config.experimental.list_physical_devices('GPU') for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True)
  • TF2 摆脱了 tf.config AttributeError: module 'tensorflow' has no attribute 'config'
【解决方案2】:

这在技术上可能没有意义,但经过一段时间的实验,这是我发现的。

环境:Ubuntu 16.04


当你运行命令时

nvidia-smi

您将获得已安装的 Nvidia 显卡的总内存消耗。示例如下图所示

当你运行你的神经网络时,你的消费可能会变成

内存消耗通常交给python。出于某种奇怪的原因,如果此进程未能成功终止,则永远不会释放内存。如果您尝试运行神经网络应用程序的另一个实例,您将收到内存分配错误。困难的方法是尝试找出一种使用进程 ID 终止此进程的方法。例如,使用进程 ID 2794,您可以这样做

sudo kill -9 2794

简单的方法是重新启动计算机并重试。但是,如果它是与代码相关的错误,则这将不起作用。

如果上述过程不起作用,则很可能您使用的数据批量大小不适合 GPU 或 CPU 内存。

您可以做的是减少输入数据的批量大小或空间维度(长度、宽度和深度)。这可能有效,但您可能会用完 RAM。

节省 RAM 的最可靠方法是使用函数生成器,这本身就是主题。

【讨论】:

  • 使用 'kill' 终止进程对我有用 - 所以这是简单的方法。
  • 自从发布此答案以来,当我的 ML 模型正在训练时,我一直在终端中执行很多“上箭头 + 输入”...谢谢!
  • @Acy 试试watch nvidia-smi :)
  • 该死的你刚刚救了我的培根!
  • 非常感谢,很好的解释,对我来说就是这种情况。
【解决方案3】:

你正在耗尽你的记忆,你可以减少批量大小,这会减慢训练过程但让你适应数据。

【讨论】:

    【解决方案4】:

    我知道您的问题是关于 tensorflow。无论如何,使用 Kerastensorflow-backend 是我的用例,并导致相同的 OOM-错误。

    我使用 Keras 和 tf-backend 的解决方案是使用 Keras 的 fit_generator() 方法。在此之前我只使用了fit()-方法(导致OOM-错误)。

    fit_generator() 通常在您无法将数据放入主内存或必须访问与 GPU 训练并行的 CPU 压力时很有用。例如,请参阅文档中的以下摘录:

    生成器与模型并行运行,以提高效率。例如,这允许您在 CPU 上对图像进行实时数据增强,同时在 GPU 上训练您的模型。

    显然,这也有助于防止显卡内存溢出。



    编辑:如果您需要一些关于如何开发自己的(线程安全)生成器来扩展 Keras 的 Sequence-class 的灵感,然后可以将其用于 fit_generator(),您可以查看我提供的一些信息在this Q&A

    【讨论】:

    • 未来的我:我降低了批量大小、输入大小(不需要),并增加了steps_per_epoch
    • @NelsonGon:听起来是正确的,因为批处理是并行的,因此批处理大小对应于您的内存消耗。
    【解决方案5】:

    我在使用 EfficientNet 模型应用迁移学习时发生了这个错误。我可以通过冻结 Conv 层来解决这个问题:

    base_model = EfficientNetB0(include_top=False, input_tensor=...)
    for layer in base_model.layers:
        layer.trainable=False
    

    【讨论】:

      【解决方案6】:

      我遇到了内存问题。就我而言,我只是减少批量大小。 ?

      【讨论】:

        【解决方案7】:

        重启 jupyter notebook 对我有用

        【讨论】:

        • 这对我也有用。即使我关闭了浏览器,nvidia-smi 也会保留内存使用情况。我也不得不关闭 Jupyter Notebook 控制台。然后内存被释放,我可以再次启动 Jupyter 并运行完全相同的代码而没有 OOM 错误!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-08
        • 2021-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多