Недостаток памяти при обучении модели Keras LSTM для бинарной классификации на последовательностях изображений.

Вопрос или проблема

Я пытаюсь создать модель Keras на основе LSTM слоев, которая будет выполнять бинарную классификацию последовательностей изображений.

Входные данные имеют следующую форму: (sample_number, timesteps, width, height, channels), где один пример будет (1200, 100, 100, 100, 3).
Это 5D тензор, эквивалентный видеоданным.

timesteps равно 100 -> каждый образец (последовательность изображений) содержит 100 кадров
channels равно 3 -> RGB данные

Вот минимума рабочий пример:

import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras import models, layers, optimizers

class TestingStuff():

    def __sequence_image_generator(self, x, y, batch_size, generator, seq_len):
        new_y = np.repeat(y, seq_len)
        helper_flow = generator.flow(x.reshape(x.shape[0] * seq_len,
                                                x.shape[2],
                                                x.shape[3],
                                                x.shape[4]),
                                    new_y,
                                    batch_size=seq_len * batch_size)
        for x_temp, y_temp in helper_flow:
            yield x_temp.reshape(x_temp.shape[0] // seq_len, 
                                seq_len, 
                                x.shape[2] * x.shape[3] * x.shape[4]), y_temp[::seq_len]    

    def testStuff(self):

        batch_size = 50
        training_epochs = 60

        # Случайно сгенерированные, аналогичные фактическому набору данных
        train_samples_num = 50
        valid_samples_num = 50
        data_train = np.random.randint(0, 65536, size=(train_samples_num, 100, 100, 100, 3), dtype="uint16")
        data_valid = np.random.randint(0, 65536, size=(valid_samples_num, 100, 100, 100, 3), dtype="uint16")
        labels_train = np.random.randint(0, 2, size=(train_samples_num), dtype="uint8")
        labels_valid = np.random.randint(0, 2, size=(valid_samples_num), dtype="uint8")

        train_data_generator = ImageDataGenerator() 
        valid_data_generator = ImageDataGenerator()

        num_frames_per_sample = data_train.shape[1]
        data_dimension = data_train.shape[2] * data_train.shape[3] * data_train.shape[4] # высота * ширина * каналы
        data_train_num_samples = data_train.shape[0]
        data_valid_num_samples = data_valid.shape[0]

        train_generator = self.__sequence_image_generator(x = data_train, 
                                                          y = labels_train, 
                                                          batch_size = batch_size, 
                                                          generator = train_data_generator, 
                                                          seq_len = num_frames_per_sample)
        valid_generator = self.__sequence_image_generator(x = data_valid, 
                                                          y = labels_valid, 
                                                          batch_size = batch_size, 
                                                          generator = valid_data_generator, 
                                                          seq_len = num_frames_per_sample)

        num_units = 100

        model = models.Sequential()
        model.add(layers.LSTM(num_units, input_shape=(num_frames_per_sample, data_dimension)))
        model.add(layers.Dense(1, activation='sigmoid'))

        model.compile(optimizer=optimizers.Adam(), loss="binary_crossentropy", metrics=['acc'])
        model.summary()

        model.fit_generator(train_generator,
                            steps_per_epoch = data_train_num_samples // batch_size,
                            epochs = training_epochs,
                            validation_data = valid_generator,
                            validation_steps = data_valid_num_samples // batch_size,
                            verbose = 1)

my_class = TestingStuff()
my_class.testStuff()

Этот пример был протестирован с использованием следующих версий:

python     3.6.8
keras      2.2.4
tensorflow 1.13.1

Объяснение кода:

  • data_train имеет форму (50, 100, 100, 100, 3) и представляет собой 50 образцов из 100 кадров изображений размером 100×100 с 3 каналами. Изображения 16 бит. То же самое относится к data_valid.
  • labels_train и labels_valid являются 1D тензорами с возможными значениями 1 и 0.
  • ImageDataGenerator() используется для увеличения данных, но в этом примере никакие преобразования не упоминаются.
  • __sequence_image_generator() адаптирован отсюда здесь и предназначен для преобразования начальных входных данных (5D тензора) в ожидаемую форму ввода (4D тензор) для метода flow() класса ImageDataGenerator и далее в ожидаемую форму ввода для слоя LSTM (3D тензор с формой (batch_size, timesteps, input_dim)).
  • Архитектура модели является отправной точкой (для улучшения), с только 1 LSTM слоем и 1 Dense слоем.

Проблема:

Я заметил, что код работает нормально, когда train_samples_num и valid_samples_num имеют значения до 50. Если эти переменные имеют более крупные значения (такие как 1000), то использование памяти становится чрезмерным, и кажется, что все обучение блокируется. Обучение не проходит дальше первой эпохи.
Я подозреваю, что проблема возможно заключается где-то в __sequence_image_generator(), где генерация данных может быть неэффективной. Но я могу ошибаться.
Изменение num_units или batch_size на более мелкие значения не решает проблему. Чрезмерное использование памяти все равно присутствует даже с num_units = 1 и batch_size = 1.

Вывод при train_samples_num и valid_samples_num равных 50:

Using TensorFlow backend.
Epoch 1/60
1/1 [==============================] - 16s 16s/step - loss: 0.7258 - acc: 0.5400 - val_loss: 0.7119 - val_acc: 0.6200
Epoch 2/60
1/1 [==============================] - 18s 18s/step - loss: 0.7301 - acc: 0.4800 - val_loss: 0.7445 - val_acc: 0.4000
Epoch 3/60
1/1 [==============================] - 21s 21s/step - loss: 0.7312 - acc: 0.4200 - val_loss: 0.7411 - val_acc: 0.4200
(...обучение продолжается...)

Вывод при train_samples_num и valid_samples_num равных 1000:

Using TensorFlow backend.
Epoch 1/60
(...никогда не заканчивает обучение первой эпохи и использование памяти растет, пока не возникает MemoryError...)

Вопрос:

Как я могу изменить свой код, чтобы предотвратить чрезмерное использование памяти, когда я использую большее количество образцов?
Мои данные имеют около 5000 образцов для обучающего набора и меньше для проверочного и тестового наборов.

В Keras вы можете сохранить свою модель, используя model.save(). Затем вы можете либо загрузить сохраненную модель для обучения с новыми данными, либо продолжить обучение вашей модели.

Мое предложение: сначала перемешайте свои входные изображения, если они следуют определенному шаблону на основе их меток. Затем загрузите партию, скажем, 100 изображений, продолжите обучение вашей модели и save() ее после завершения итерации. Вся суть сохранения вашей модели после каждой итерации заключается в том, чтобы вы могли продолжить обучение, если что-то произойдет в одной итерации.

У меня есть схожая проблема: память в моей фазе обучения была исчерпана. Играя с гиперпараметрами, я обнаружил, что размер партии должен быть уменьшен, чтобы поддержать обработку, если вы не используете GPU или TPU, поэтому я попробовал уменьшить размер партии и в конечном итоге пришел к меньшему, чем я хотел, но достаточно для завершения обучения на моей модели. Надеюсь, это поможет.

Ответ или решение

Проблема исчерпания памяти при обучении модели LSTM на Keras для бинарной классификации последовательностей изображений может иметь несколько причин и возможных решений. Рассмотрим причины возникновения данной проблемы и предложим конкретные методы решения для оптимизации использования памяти.

Причины исчерпания памяти

  1. Объем данных: Ваши данные имеют форму (число образцов, временные кадры, ширина, высота, каналы). Пример (1200, 100, 100, 100, 3) – это 600 миллионов элементов данных, особенно когда вы используете 16-битное представление. При увеличении количества образцов (например, до 1000) объем данных, который требуется загрузить в модель, значительно увеличивается.

  2. Параметры модели: Сложная модель с большим числом параметров (например, num_units в LSTM) может потреблять много памяти, особенно при работе с большими входными тензорами.

  3. Батч-сайз: Если размер батча большой, это также может привести к высокой нагрузке на память, так как в один момент времени обрабатывается много данных.

Решения для оптимизации

  1. Уменьшение батч-сайза: Один из первых шагов, который стоит попробовать – это уменьшение размера батча. Это уменьшит объем данных, обрабатываемых одновременно. Например, можно начать с батч-сайза 8 или 16 и протестировать, как это влияет на использование памяти.

  2. Применение генераторов: Ваша текущая реализация генератора данных может быть оптимизирована:

    • Вместо того чтобы хранить все данные в памяти, можно генерировать только необходимую партию данных на лету.
    • Убедитесь, что в методе __sequence_image_generator() используете только необходимый объем данных.
  3. Использование tf.data API: Для более эффективной работы с данными рассмотрите использование TensorFlow tf.data API. Это позволит вам конвейерно обрабатывать данные, что улучшит производительность и уменьшит использование памяти. Правда, это потребует значительных изменений в коде.

  4. Изменение разрешения изображений: Если возможно, попытайтесь уменьшить разрешение ваших изображений. Например, вместо 100×100 используйте 50×50, что значительно уменьшит размер входных данных.

  5. Кэширование и экономия памяти: Убедитесь, что используете правильные типы данных. Например, если ваш набор данных может быть представлен в менее объемном формате (например, использовать uint8 вместо uint16, если это применимо), это поможет существенно сэкономить память.

  6. Использование GPU: Если у вас есть доступ к графическому процессору (GPU), убедитесь, что ваша среда настроена для обучения на GPU. GPU лучше справляются с большими объемами данных и могут значительно ускорить обучение.

  7. Код для мониторинга: Добавьте код для мониторинга использования памяти (например, с помощью библиотеки memory_profiler) в вашем скрипте, чтобы внимательно следить за тем, где возникают утечки памяти.

Заключение

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

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

Оцените материал
Добавить комментарий

Капча загружается...