Error de memoria ‘Recurso agotado’ al intentar entrenar un modelo de Keras

Estoy tratando de entrenar un modelo VGG19 para un problema de clasificación de imágenes binarias. Mi conjunto de datos no encaja en la memoria, así que uso lotes y la función .fit_generator del model .

Sin embargo, incluso cuando trato de entrenar con lotes, recibo el siguiente error:

W tensorflow / core / common_runtime / bfc_allocator.cc: 275] Se agotó la memoria al intentar asignar 392.00MiB. Ver los registros para el estado de la memoria.

W tensorflow / core / framework / op_kernel.cc: 975] Recurso agotado: OOM al asignar el tensor con forma

Aquí está la salida de la consola sobre mi GPU al iniciar el script de entrenamiento:

 Using TensorFlow backend. I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcublas.so locally I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcudnn.so locally I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcufft.so locally I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcuda.so.1 locally I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcurand.so locally Found 20000 images belonging to 2 classes. Found 5000 images belonging to 2 classes. I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties: name: GeForce GT 750M major: 3 minor: 0 memoryClockRate (GHz) 1.085 pciBusID 0000:01:00.0 Total memory: 1.95GiB Free memory: 1.74GiB I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0 I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0: YI tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GT 750M, pci bus id: 0000:01:00.0) 

No lo sé, pero creo que más de 1.5 GB debería ser suficiente para entrenar en lotes pequeños, ¿verdad?

La salida completa de la secuencia de comandos es bastante grande y pegaré una parte de ella en este pastebin .

A continuación se muestra el código de mi modelo:

 from keras.models import Sequential from keras.layers.core import Flatten, Dense, Dropout from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D from keras.preprocessing.image import ImageDataGenerator from keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau class VGG19(object): def __init__(self, weights_path=None, train_folder='data/train', validation_folder='data/val'): self.weights_path = weights_path self.model = self._init_model() if weights_path: self.model.load_weights(weights_path) else: self.datagen = self._datagen() self.train_folder = train_folder self.validation_folder = validation_folder self.model.compile( loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'] ) def fit(self, batch_size=32, nb_epoch=10): train_generator = self.datagen.flow_from_directory( self.train_folder, target_size=(224, 224), color_mode='rgb', class_mode='binary', batch_size=2 ) validation_generator = self.datagen.flow_from_directory( self.validation_folder, target_size=(224, 224), color_mode='rgb', class_mode='binary', batch_size=2 ) self.model.fit_generator( train_generator, samples_per_epoch=16, nb_epoch=1, verbose=1, validation_data=validation_generator, callbacks=[ TensorBoard(log_dir='./logs', write_images=True), ModelCheckpoint(filepath='weights.{epoch:02d}-{val_loss:.2f}.hdf5', monitor='val_loss'), ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=5, min_lr=0.001) ], nb_val_samples=8 ) def evaluate(self, X, y, batch_size=32): return self.model.evaluate( X, y, batch_size=batch_size, verbose=1 ) def predict(self, X, batch_size=4, verbose=1): return self.model.predict(X, batch_size=batch_size, verbose=verbose) def predict_proba(self, X, batch_size=4, verbose=1): return self.model.predict_proba(X, batch_size=batch_size, verbose=verbose) def _init_model(self): model = Sequential() model.add(ZeroPadding2D((1, 1), input_shape=(224, 224, 3))) model.add(Convolution2D(64, 3, 3, activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(64, 3, 3, activation='relu')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(128, 3, 3, activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(128, 3, 3, activation='relu')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(256, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(256, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(256, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(256, 3, 3, activation='relu')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(Flatten()) model.add(Dense(4096, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(4096, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1, activation='softmax')) return model def _datagen(self): return ImageDataGenerator( featurewise_center=True, samplewise_center=False, featurewise_std_normalization=True, samplewise_std_normalization=False, zca_whitening=False, rotation_range=20, width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True, vertical_flip=True ) 

Ejecuto el modelo de la siguiente manera:

 vgg19 = VGG19(train_folder='data/train/train', validation_folder='data/val/val') vgg19.fit(nb_epoch=1) 

y mis carpetas data/train/train y data/val/val consisten en dos directorios cada uno: cats y dogs , de modo que la función ImageDataGenerator.flow_from_directory() podría separar mis clases correctamente.


¿Qué estoy haciendo mal aquí? ¿Es solo que VGG19 es demasiado grande para mi máquina o hay algún problema con los tamaños de lote?

¿Qué puedo hacer para entrenar el modelo en mi máquina?


PD: si no interruto la secuencia de comandos de entrenamiento (aunque produce muchos errores similares como uno en el pastebin anterior), las últimas líneas de la salida son las siguientes:

 W tensorflow/core/common_runtime/bfc_allocator.cc:274] *****************************************************************************************xxxxxxxxxxx W tensorflow/core/common_runtime/bfc_allocator.cc:275] Ran out of memory trying to allocate 392.00MiB. See logs for memory state. W tensorflow/core/framework/op_kernel.cc:975] Resource exhausted: OOM when allocating tensor with shape[25088,4096] Traceback (most recent call last): File "train.py", line 6, in  vgg19.fit(nb_epoch=1) File "/home/denis/WEB/DeepLearning/CatsVsDogs/model/vgg19.py", line 84, in fit nb_val_samples=8 File "/usr/local/lib/python2.7/dist-packages/keras/models.py", line 907, in fit_generator pickle_safe=pickle_safe) File "/usr/local/lib/python2.7/dist-packages/keras/engine/training.py", line 1378, in fit_generator callbacks._set_model(callback_model) File "/usr/local/lib/python2.7/dist-packages/keras/callbacks.py", line 32, in _set_model callback._set_model(model) File "/usr/local/lib/python2.7/dist-packages/keras/callbacks.py", line 493, in _set_model self.sess = KTF.get_session() File "/usr/local/lib/python2.7/dist-packages/keras/backend/tensorflow_backend.py", line 111, in get_session _initialize_variables() File "/usr/local/lib/python2.7/dist-packages/keras/backend/tensorflow_backend.py", line 200, in _initialize_variables sess.run(tf.variables_initializer(uninitialized_variables)) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/client/session.py", line 766, in run run_metadata_ptr) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/client/session.py", line 964, in _run feed_dict_string, options, run_metadata) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/client/session.py", line 1014, in _do_run target_list, options, run_metadata) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/client/session.py", line 1034, in _do_call raise type(e)(node_def, op, message) tensorflow.python.framework.errors_impl.ResourceExhaustedError: OOM when allocating tensor with shape[4096] [[Node: Variable_43/Assign = Assign[T=DT_FLOAT, _class=["loc:@Variable_43"], use_locking=true, validate_shape=true, _device="/job:localhost/replica:0/task:0/gpu:0"](Variable_43, Const_59)]] Caused by op u'Variable_43/Assign', defined at: File "train.py", line 6, in  vgg19.fit(nb_epoch=1) File "/home/denis/WEB/DeepLearning/CatsVsDogs/model/vgg19.py", line 84, in fit nb_val_samples=8 File "/usr/local/lib/python2.7/dist-packages/keras/models.py", line 907, in fit_generator pickle_safe=pickle_safe) File "/usr/local/lib/python2.7/dist-packages/keras/engine/training.py", line 1351, in fit_generator self._make_train_function() File "/usr/local/lib/python2.7/dist-packages/keras/engine/training.py", line 696, in _make_train_function self.total_loss) File "/usr/local/lib/python2.7/dist-packages/keras/optimizers.py", line 387, in get_updates ms = [K.zeros(shape) for shape in shapes] File "/usr/local/lib/python2.7/dist-packages/keras/backend/tensorflow_backend.py", line 278, in zeros dtype, name) File "/usr/local/lib/python2.7/dist-packages/keras/backend/tensorflow_backend.py", line 182, in variable v = tf.Variable(value, dtype=_convert_string_dtype(dtype), name=name) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/ops/variables.py", line 224, in __init__ expected_shape=expected_shape) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/ops/variables.py", line 360, in _init_from_args validate_shape=validate_shape).op File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/ops/gen_state_ops.py", line 47, in assign use_locking=use_locking, name=name) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/framework/op_def_library.py", line 759, in apply_op op_def=op_def) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/framework/ops.py", line 2240, in create_op original_op=self._default_original_op, op_def=op_def) File "/usr/local/lib/python2.7/dist-packages/tensorflow/python/framework/ops.py", line 1128, in __init__ self._traceback = _extract_stack() ResourceExhaustedError (see above for traceback): OOM when allocating tensor with shape[4096] [[Node: Variable_43/Assign = Assign[T=DT_FLOAT, _class=["loc:@Variable_43"], use_locking=true, validate_shape=true, _device="/job:localhost/replica:0/task:0/gpu:0"](Variable_43, Const_59)]] 

Actualización 1

Siguiendo el consejo de @ rmeertens, he reducido las últimas capas densas:

ultimo bloque

 model.add(Flatten()) model.add(Dense(1024, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(256, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1, activation='softmax')) 

y el error cambió un poco. Aunque sigue siendo un error de OOM: pastebin.com/SamkUbJA

En este caso, el error OOM aparece porque su gráfico es demasiado grande. ¿Cuál es la forma del tensor que intentaste asignar cuando todo cae?

De todos modos, lo primero que podría intentar es asignar el modelo sin tener ningún dato en la memoria. Hay algo más en ejecución (otro portátil jupyter, algún otro servicio de modelo en segundo plano).

Además, quizás puedas ahorrar espacio en las últimas capas:

 model.add(Dense(4096, activation='relu')) model.add(Dense(4096, activation='relu')) model.add(Dense(1, activation='sigmoid')) 

Una matriz de 4096×4096 es bastante grande (e inmediatamente volver a 1 es una mala idea de todos modos)

Si me dices que este modelo está funcionando me sorprendería mucho.

Tener una activación de softmax en 1 salida (su última capa) no tiene sentido. El softmax normaliza las salidas de una capa para que sumen 1 … ¡si solo tienes una salida, será 1 todo el tiempo! Entonces, si desea una probabilidad binaria, ¡use sigmoid en 1 salida o use softmax en 2 salidas!

En mi caso, resolví el problema de la siguiente manera:
cambiar Convolution2D -> Conv2D

 from keras.layers import Conv2D model.add(Convolution2D(64, 3, 3, activation='relu')) -> model.add(Conv2D(64, 3, 3, activation='relu'))