Keras использует слишком много памяти GPU при вызове train_on_batch, fit и т. Д.

Я возился с Керасом, и мне это нравится. При работе с довольно глубокими сетями у меня возникла одна большая проблема: при вызове model.train_on_batch или model.fit и т. Д. Keras выделяет значительно больше памяти GPU, чем сама модель должна быть нужна. Это не вызвано попыткой обучиться на некоторых действительно больших изображениях, а сама сетевая модель, кажется, требует много памяти GPU. Я создал этот игрушечный пример, чтобы показать, что я имею в виду. Вот по сути, что происходит:

Сначала я создаю довольно глубокую сеть и использую model.summary(), чтобы получить общее количество параметров, необходимых для сети (в данном случае 206538153, что соответствует примерно 826 МБ). Затем я использую nvidia-smi, чтобы увидеть, сколько памяти GPU выделил Keras, и я вижу, что это имеет смысл (849 МБ).

Затем я компилирую сеть и могу подтвердить, что это не увеличивает использование памяти графическим процессором. И, как мы видим в этом случае, на данный момент у меня есть почти 1 ГБ видеопамяти.

Затем я пытаюсь передать простое изображение 16x16 и основную правду 1x1 в сеть, и затем все взрывается, потому что Keras снова начинает выделять много памяти, без какой-либо очевидной для меня причины. Кажется, что-то в обучении сети требует гораздо больше памяти, чем просто наличие модели, что не имеет смысла для меня. Я обучил значительно более глубокие сети на этом графическом процессоре в других средах, поэтому я думаю, что я использую Keras неправильно (или что-то не так в моей настройке или в Keras, но, конечно, это трудно понять наверняка).

Вот код:

from scipy import misc
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Convolution2D, MaxPooling2D, Reshape, Flatten, ZeroPadding2D, Dropout
import os

model = Sequential()

model.add(Convolution2D(256, 3, 3, border_mode='same', input_shape=(16,16,1)))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))
model.add(Convolution2D(512, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(Convolution2D(1024, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))
model.add(Convolution2D(256, 3, 3, border_mode='same'))
model.add(Convolution2D(32, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(4))
model.add(Dense(1))

model.summary()

os.system("nvidia-smi")
raw_input("Press Enter to continue...")    

model.compile(optimizer='sgd',
              loss='mse', 
              metrics=['accuracy'])

os.system("nvidia-smi")              
raw_input("Compiled model. Press Enter to continue...")

n_batches = 1
batch_size = 1
for ibatch in range(n_batches):
    x = np.random.rand(batch_size, 16,16,1)
    y = np.random.rand(batch_size, 1)

    os.system("nvidia-smi")
    raw_input("About to train one iteration. Press Enter to continue...")

    model.train_on_batch(x, y)         
    print("Trained one iteration")

Который дает следующий вывод для меня:

Using Theano backend.
Using gpu device 0: GeForce GTX 960 (CNMeM is disabled, cuDNN 5103)
/usr/local/lib/python2.7/dist-packages/theano/sandbox/cuda/__init__.py:600: UserWarning: Your cuDNN version is more recent than the one Theano officially supports. If you see any problems, try updating Theano or downgrading cuDNN to version 5.
  warnings.warn(warn)
____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
convolution2d_1 (Convolution2D)  (None, 16, 16, 256)   2560        convolution2d_input_1[0][0]      
____________________________________________________________________________________________________
maxpooling2d_1 (MaxPooling2D)    (None, 8, 8, 256)     0           convolution2d_1[0][0]            
____________________________________________________________________________________________________
convolution2d_2 (Convolution2D)  (None, 8, 8, 512)     1180160     maxpooling2d_1[0][0]             
____________________________________________________________________________________________________
maxpooling2d_2 (MaxPooling2D)    (None, 4, 4, 512)     0           convolution2d_2[0][0]            
____________________________________________________________________________________________________
convolution2d_3 (Convolution2D)  (None, 4, 4, 1024)    4719616     maxpooling2d_2[0][0]             
____________________________________________________________________________________________________
convolution2d_4 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_3[0][0]            
____________________________________________________________________________________________________
convolution2d_5 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_4[0][0]            
____________________________________________________________________________________________________
convolution2d_6 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_5[0][0]            
____________________________________________________________________________________________________
convolution2d_7 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_6[0][0]            
____________________________________________________________________________________________________
convolution2d_8 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_7[0][0]            
____________________________________________________________________________________________________
convolution2d_9 (Convolution2D)  (None, 4, 4, 1024)    9438208     convolution2d_8[0][0]            
____________________________________________________________________________________________________
convolution2d_10 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_9[0][0]            
____________________________________________________________________________________________________
convolution2d_11 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_10[0][0]           
____________________________________________________________________________________________________
convolution2d_12 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_11[0][0]           
____________________________________________________________________________________________________
convolution2d_13 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_12[0][0]           
____________________________________________________________________________________________________
convolution2d_14 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_13[0][0]           
____________________________________________________________________________________________________
convolution2d_15 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_14[0][0]           
____________________________________________________________________________________________________
convolution2d_16 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_15[0][0]           
____________________________________________________________________________________________________
convolution2d_17 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_16[0][0]           
____________________________________________________________________________________________________
convolution2d_18 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_17[0][0]           
____________________________________________________________________________________________________
convolution2d_19 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_18[0][0]           
____________________________________________________________________________________________________
convolution2d_20 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_19[0][0]           
____________________________________________________________________________________________________
convolution2d_21 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_20[0][0]           
____________________________________________________________________________________________________
convolution2d_22 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_21[0][0]           
____________________________________________________________________________________________________
convolution2d_23 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_22[0][0]           
____________________________________________________________________________________________________
convolution2d_24 (Convolution2D) (None, 4, 4, 1024)    9438208     convolution2d_23[0][0]           
____________________________________________________________________________________________________
maxpooling2d_3 (MaxPooling2D)    (None, 2, 2, 1024)    0           convolution2d_24[0][0]           
____________________________________________________________________________________________________
convolution2d_25 (Convolution2D) (None, 2, 2, 256)     2359552     maxpooling2d_3[0][0]             
____________________________________________________________________________________________________
convolution2d_26 (Convolution2D) (None, 2, 2, 32)      73760       convolution2d_25[0][0]           
____________________________________________________________________________________________________
maxpooling2d_4 (MaxPooling2D)    (None, 1, 1, 32)      0           convolution2d_26[0][0]           
____________________________________________________________________________________________________
flatten_1 (Flatten)              (None, 32)            0           maxpooling2d_4[0][0]             
____________________________________________________________________________________________________
dense_1 (Dense)                  (None, 4)             132         flatten_1[0][0]                  
____________________________________________________________________________________________________
dense_2 (Dense)                  (None, 1)             5           dense_1[0][0]                    
====================================================================================================
Total params: 206538153
____________________________________________________________________________________________________
None
Thu Oct  6 09:05:42 2016       
+------------------------------------------------------+                       
| NVIDIA-SMI 352.63     Driver Version: 352.63         |                       
|-------------------------------+----------------------+----------------------+
| 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 960     Off  | 0000:01:00.0      On |                  N/A |
| 30%   37C    P2    28W / 120W |   1082MiB /  2044MiB |      9%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1796    G   /usr/bin/X                                     155MiB |
|    0      2597    G   compiz                                          65MiB |
|    0      5966    C   python                                         849MiB |
+-----------------------------------------------------------------------------+
Press Enter to continue...
Thu Oct  6 09:05:44 2016       
+------------------------------------------------------+                       
| NVIDIA-SMI 352.63     Driver Version: 352.63         |                       
|-------------------------------+----------------------+----------------------+
| 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 960     Off  | 0000:01:00.0      On |                  N/A |
| 30%   38C    P2    28W / 120W |   1082MiB /  2044MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1796    G   /usr/bin/X                                     155MiB |
|    0      2597    G   compiz                                          65MiB |
|    0      5966    C   python                                         849MiB |
+-----------------------------------------------------------------------------+
Compiled model. Press Enter to continue...
Thu Oct  6 09:05:44 2016       
+------------------------------------------------------+                       
| NVIDIA-SMI 352.63     Driver Version: 352.63         |                       
|-------------------------------+----------------------+----------------------+
| 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 960     Off  | 0000:01:00.0      On |                  N/A |
| 30%   38C    P2    28W / 120W |   1082MiB /  2044MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      1796    G   /usr/bin/X                                     155MiB |
|    0      2597    G   compiz                                          65MiB |
|    0      5966    C   python                                         849MiB |
+-----------------------------------------------------------------------------+
About to train one iteration. Press Enter to continue...
Error allocating 37748736 bytes of device memory (out of memory). Driver report 34205696 bytes free and 2144010240 bytes total 
Traceback (most recent call last):
  File "memtest.py", line 65, in <module>
    model.train_on_batch(x, y)         
  File "/usr/local/lib/python2.7/dist-packages/keras/models.py", line 712, in train_on_batch
    class_weight=class_weight)
  File "/usr/local/lib/python2.7/dist-packages/keras/engine/training.py", line 1221, in train_on_batch
    outputs = self.train_function(ins)
  File "/usr/local/lib/python2.7/dist-packages/keras/backend/theano_backend.py", line 717, in __call__
    return self.function(*inputs)
  File "/usr/local/lib/python2.7/dist-packages/theano/compile/function_module.py", line 871, in __call__
    storage_map=getattr(self.fn, 'storage_map', None))
  File "/usr/local/lib/python2.7/dist-packages/theano/gof/link.py", line 314, in raise_with_op
    reraise(exc_type, exc_value, exc_trace)
  File "/usr/local/lib/python2.7/dist-packages/theano/compile/function_module.py", line 859, in __call__
    outputs = self.fn()
MemoryError: Error allocating 37748736 bytes of device memory (out of memory).
Apply node that caused the error: GpuContiguous(GpuDimShuffle{3,2,0,1}.0)
Toposort index: 338
Inputs types: [CudaNdarrayType(float32, 4D)]
Inputs shapes: [(1024, 1024, 3, 3)]
Inputs strides: [(1, 1024, 3145728, 1048576)]
Inputs values: ['not shown']
Outputs clients: [[GpuDnnConv{algo='small', inplace=True}(GpuContiguous.0, GpuContiguous.0, GpuAllocEmpty.0, GpuDnnConvDesc{border_mode='half', subsample=(1, 1), conv_mode='conv', precision='float32'}.0, Constant{1.0}, Constant{0.0}), GpuDnnConvGradI{algo='none', inplace=True}(GpuContiguous.0, GpuContiguous.0, GpuAllocEmpty.0, GpuDnnConvDesc{border_mode='half', subsample=(1, 1), conv_mode='conv', precision='float32'}.0, Constant{1.0}, Constant{0.0})]]

HINT: Re-running with most Theano optimization disabled could give you a back-trace of when this node was created. This can be done with by setting the Theano flag 'optimizer=fast_compile'. If that does not work, Theano optimizations can be disabled with 'optimizer=None'.
HINT: Use the Theano flag 'exception_verbosity=high' for a debugprint and storage map footprint of this apply node.

Несколько вещей, на которые стоит обратить внимание:

  • Я пробовал как Theano, так и TensorFlow. Оба имеют одинаковые проблемы, и не хватает памяти в одной строке. В TensorFlow кажется, что Keras предварительно выделяет много памяти (около 1,5 ГБ), поэтому nvidia-smi не помогает нам отслеживать, что там происходит, но я получаю те же исключения из нехватки памяти. Опять же, это указывает на ошибку (мое использование) Keras (хотя трудно быть уверенным в таких вещах, это может быть что-то с моей настройкой).
  • Я попытался использовать CNMEM в Theano, который ведет себя как TensorFlow: он предварительно выделяет большой объем памяти (около 1,5 ГБ), но вылетает в том же месте.
  • Есть несколько предупреждений о CudNN-версии. Я попытался запустить бэкэнд Theano с CUDA, но не с CudNN, и я получил те же ошибки, так что это не является источником проблемы.
  • Если вы хотите проверить это на своем собственном графическом процессоре, вы можете захотеть сделать сеть глубже / меньше в зависимости от того, сколько памяти GPU вам нужно для этого.
  • Моя конфигурация следующая: Ubuntu 14.04, GeForce GTX 960, CUDA 7.5.18, CudNN 5.1.3, Python 2.7, Keras 1.1.0 (устанавливается через pip)
  • Я пытался изменить компиляцию модели, чтобы использовать разные оптимизаторы и потери, но, похоже, это ничего не меняет.
  • Я попытался изменить функцию train_on_batch, чтобы использовать вместо нее fit, но у нее та же проблема.
  • Я видел один подобный вопрос здесь, на Stackru - Почему этой модели Keras требуется более 6 ГБ памяти? - но, насколько я могу судить, у меня нет этих проблем в моей конфигурации. У меня никогда не было нескольких версий CUDA, и я дважды проверил мои переменные PATH, LD_LIBRARY_PATH и CUDA_ROOT больше, чем могу сосчитать.
  • Юлиус предположил, что параметры активации сами занимают память GPU. Если это правда, может кто-нибудь объяснить это более четко? Насколько я могу судить, я попытался изменить функцию активации моих слоев свертки на функции, которые четко жестко запрограммированы без доступных для изучения параметров, и это ничего не меняет. Кроме того, кажется маловероятным, что эти параметры будут занимать почти столько же памяти, как и вся остальная сеть.
  • После тщательного тестирования самая большая сеть, которую я могу обучить, имеет около 453 МБ параметров из моих ~2 ГБ ОЗУ GPU. Это нормально?
  • После тестирования Keras на некоторых меньших CNN, которые вписываются в мой GPU, я вижу, что использование RAM-памяти GPU происходит очень внезапно. Если я использую сеть с параметрами около 100 МБ, то в 99% случаев во время обучения будет использоваться менее 200 МБ ОЗУ графического процессора. Но время от времени использование памяти увеличивается до 1,3 ГБ. Кажется безопасным предположить, что именно эти шипы вызывают мои проблемы. Я никогда не видел эти пики в других рамках, но они могут быть там по уважительной причине? Если кто-нибудь знает, что их вызывает, и если есть способ избежать их, пожалуйста, присоединяйтесь!

3 ответа

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

Итак, в начале, когда создается сеть, выделяются только параметры. Однако, когда начинается обучение, распределяются активации (по разам в каждой мини-партии), а также вычисления backprop, что увеличивает использование памяти.

И Theano, и Tensorflow дополняют созданный символический граф, хотя оба по-разному.

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

Вот фрагмент кода для увеличения batch_size на основе вашего исходного кода:

from scipy import misc
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Convolution2D, MaxPooling2D, Reshape, Flatten, ZeroPadding2D, Dropout
import os
import matplotlib.pyplot as plt


def gpu_memory():
    out = os.popen("nvidia-smi").read()
    ret = '0MiB'
    for item in out.split("\n"):
        if str(os.getpid()) in item and 'python' in item:
            ret = item.strip().split(' ')[-2]
    return float(ret[:-3])

gpu_mem = []
gpu_mem.append(gpu_memory())

model = Sequential()
model.add(Convolution2D(100, 3, 3, border_mode='same', input_shape=(16,16,1)))
model.add(Convolution2D(256, 3, 3, border_mode='same'))
model.add(Convolution2D(32, 3, 3, border_mode='same'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(4))
model.add(Dense(1))

model.summary()
gpu_mem.append(gpu_memory())

model.compile(optimizer='sgd',
              loss='mse', 
              metrics=['accuracy'])
gpu_mem.append(gpu_memory())


batches = []
n_batches = 20
batch_size = 1
for ibatch in range(n_batches):
    batch_size = (ibatch+1)*10
    batches.append(batch_size)
    x = np.random.rand(batch_size, 16,16,1)
    y = np.random.rand(batch_size, 1)
    print y.shape

    model.train_on_batch(x, y)         
    print("Trained one iteration")

    gpu_mem.append(gpu_memory())

fig = plt.figure()
plt.plot([-100, -50, 0]+batches, gpu_mem)
plt.show()

Кроме того, для повышения скорости Tensorflow использует всю доступную память графического процессора. Чтобы остановить это, и вам нужно добавить config.gpu_options.allow_growth = True в get_session()

# keras/backend/tensorflow_backend.py
def get_session():
    global _SESSION
    if tf.get_default_session() is not None:
        session = tf.get_default_session()
    else:
        if _SESSION is None:
            if not os.environ.get('OMP_NUM_THREADS'):
                config = tf.ConfigProto(allow_soft_placement=True,
                    )
            else:
                nb_thread = int(os.environ.get('OMP_NUM_THREADS'))
                config = tf.ConfigProto(intra_op_parallelism_threads=nb_thread,
                                        allow_soft_placement=True)
            config.gpu_options.allow_growth = True
            _SESSION = tf.Session(config=config)
        session = _SESSION
    if not _MANUAL_VAR_INIT:
        _initialize_variables()
    return session

Теперь, если вы запустите предыдущий фрагмент, вы получите такие графики:

Theano: Theano Tensorflow: Tenserflow

Теано: После model.compile() сколько бы памяти ни понадобилось, в начале тренировки она почти удваивается. Это связано с тем, что Theano увеличивает символьный граф для обратного распространения, и каждому тензору нужен соответствующий тензор для достижения обратного потока градиентов. Потребность в памяти, кажется, не растет с batch_size и это неожиданно для меня, так как размер заполнителя должен увеличиться, чтобы учесть приток данных из CPU-> GPU.

Tensorflow: память GPU не выделяется даже после model.compile() Керас не зовет get_session() до того времени, которое на самом деле вызывает _initialize_variables(), Кажется, что Tensorflow увеличивает скорость памяти, поэтому память не растет линейно с batch_size,

Сказав все это, Tensorflow кажется жадным до памяти, но для больших графиков это очень быстро. С другой стороны, Theano очень эффективно использует память GPU, но требует очень много времени для инициализации графика в начале обучения. После этого это также довольно быстро.

200M params для 2 Гб GPU - это слишком много. Также ваша архитектура неэффективна, использование локальных узких мест будет более эффективным. Также вы должны перейти от маленькой модели к большой, а не назад, прямо сейчас у вас есть ввод 16x16, с этой архитектурой, которая означает, что в конце большая часть вашей сети будет "заполнена нулями" и не будет основана на функциях ввода. Слои вашей модели зависят от вашего ввода, поэтому вы не можете просто установить произвольное количество слоев и размеров, вам нужно посчитать, сколько данных будет передано каждому из них, с пониманием, почему это происходит. Я бы порекомендовал вам посмотреть этот бесплатный курс http://cs231n.github.io/

Другие вопросы по тегам