Понимание ResourceExhaustedError: OOM при распределении тензора с формой

Я пытаюсь реализовать модель мыслей с пропуском с использованием тензорного потока, и текущая версия помещена здесь. enter image description here

В настоящее время я использую один графический процессор моей машины (всего 2 графических процессора), и информация о графическом процессоре

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())

    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, примерно около 240Mb, если все использовали tf.float32. Выходной сигнал tf.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:
        ...

Тогда я получил ООМ. Если я закомментирую все тело try (нет для подачи данных), сценарий будет работать нормально.

Я понятия не имею, почему я получил OOM в таком маленьком масштабе данных. Используя nvidia-smi, я всегда получал

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 |
+-----------------------------------------------------------------------------+

Я не вижу фактического использования моего скрипта в графическом процессоре, так как тензор потока всегда крадет всю память в начале. И настоящая проблема здесь в том, что я не знаю, как это отладить.

Я прочитал несколько постов о OOM на Qaru. Большинство из них произошло при подаче данных большого набора тестов в модель, а подача данных небольшими партиями может избежать этой проблемы. Но я не вижу, почему такая маленькая комбинация данных и параметров сосет в моем 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:

Любая помощь будет оценена. Заранее спасибо.

Ответ 1

Давайте разделим вопросы по одному:

Что касается тензорного потока для предварительного выделения всей памяти, вы можете использовать следующий фрагмент кода, чтобы тензорный поток выделял память всякий раз, когда это необходимо. Чтобы вы могли понять, как идут дела.

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

Это работает одинаково с tf.Session() вместо tf.InteractiveSession(), если вы предпочитаете.

Вторая вещь о размерах, Поскольку информация о размере вашей сети отсутствует, мы не можем оценить, что происходит не так. Тем не менее, вы можете поочередно отладить всю сеть. Например, создайте сеть только с одним слоем, получите его выходные данные, создайте значения сеанса и канала один раз и визуализируйте, сколько памяти вы используете. Повторяйте этот сеанс отладки, пока не увидите точку, в которой у вас заканчивается память.

Имейте в виду, что выход 3840 x 155229 действительно, действительно большой выход. Это означает ~ 600M нейронов и ~ 2.22GB только на один слой. Если у вас есть слои одинакового размера, все они будут складываться, чтобы заполнить вашу память GPU довольно быстро.

Кроме того, это только для прямого направления, если вы используете этот слой для обучения, обратное распространение и слои, добавленные оптимизатором, умножат этот размер на 2. Таким образом, для обучения вы потребляете ~ 5 ГБ только для выходного слоя.

Я предлагаю вам пересмотреть свою сеть и попытаться уменьшить размер пакета/количество параметров, чтобы приспособить вашу модель к GPU

Ответ 2

Технически это может не иметь смысла, но после некоторого эксперимента я выяснил это.

ОКРУЖАЮЩАЯ СРЕДА: Ubuntu 16.04


Когда вы запускаете команду

nvidia-smi

Вы получите общее потребление памяти установленной графической картой Nvidia. Пример, как показано на этом изображении enter image description here

После запуска нейронной сети потребление может измениться на enter image description here

Потребление памяти обычно предоставляется Python. По какой-то странной причине, если этот процесс не завершается успешно, память никогда не освобождается. Если вы попытаетесь запустить другой экземпляр приложения нейронной сети, вы получите сообщение об ошибке выделения памяти. Сложный путь - попытаться найти способ завершить этот процесс, используя идентификатор процесса. Например, с идентификатором процесса 2794 вы можете выполнить

sudo kill -9 2794

Самый простой способ - просто перезагрузить компьютер и повторить попытку. Однако, если это ошибка, связанная с кодом, это не сработает.

Если вышеупомянутый процесс не работает, скорее всего, вы используете размер пакета данных, который не может поместиться в память GPU или CPU.

Что вы можете сделать, это уменьшить размер пакета или пространственные размеры (длина, ширина и глубина) ваших входных данных. Это может работать, но вам может не хватить оперативной памяти.

Самый надежный способ сэкономить ОЗУ - это использовать генератор функций, который сам по себе является предметом обсуждения.

Ответ 3

Вы исчерпываете свою память. Вы можете уменьшить размер партии, что замедлит процесс обучения, но позволит подогнать данные.

Ответ 4

Перезапуск ноутбука Jupyter работал для меня