Как реализовать настраиваемые генераторы данных для включения динамического потока данных в модели Keras
Генераторы данных - одна из самых полезных функций Keras API. Рассмотрим сценарий, в котором у вас много данных, так много, что вы не можете хранить их все сразу в ОЗУ. Вид? Очевидно, что покупка дополнительной оперативной памяти - не вариант.
Что ж, решением этой проблемы может быть загрузка мини-пакетов, загружаемых в модель динамически. Именно этим и занимаются генераторы данных. Они могут динамически генерировать входные данные модели, таким образом формируя конвейер от хранилища к ОЗУ для загрузки данных по мере необходимости. Еще одним преимуществом этого конвейера является то, что можно легко применять процедуры предварительной обработки к этим минипакетам данных, поскольку они подготовлены для подачи в модель.
В этой статье мы увидим, как создать подкласс от класса tf.keras.utils.Sequence для реализации настраиваемых генераторов данных.
ImageDataGenerator
Перво-наперво, теперь мы увидим, как использовать API ImageDataGenerator для конвейерной обработки динамических изображений и, следовательно, решить необходимость реализации пользовательских.
datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True )
API ImageDataGenerator предоставляет функции конвейерной обработки данных изображений из каталогов, а также из путей, упомянутых в кадре данных. Один из них может включать в себя этапы предварительной обработки, такие как масштабирование, увеличение изображений, которые будут непосредственно применяться к изображениям в реальном времени.
Итак, почему именно кастомные?
Обучение модели не ограничивается одним типом входных данных и целей. Бывают случаи, когда модель загружается сразу несколькими типами входных данных. Например, предположим, что вы работаете над проблемой мультимодальной классификации, когда вам необходимо одновременно обрабатывать текст и данные изображения. Здесь явно нельзя использовать ImageDataGenerator. К тому же загрузка всех данных сразу недоступна. Следовательно, мы решаем эту проблему, реализуя собственный генератор данных.
Реализация настраиваемого генератора данных
Наконец-то начнем с реализации.
Это будет очень общая реализация, и поэтому ее можно будет напрямую скопировать. Вам просто нужно заполнить пробелы / заменить определенные переменные своей собственной логикой.
Как упоминалось ранее, мы создадим подкласс API tf.keras.utils.Sequence.
def __init__( self, df, x_col, y_col=None, batch_size=32, num_classes=None, shuffle=True ): self.batch_size = batch_size self.df = dataframe self.indices = self.df.index.tolist() self.num_classes = num_classes self.shuffle = shuffle self.x_col = x_col self.y_col = y_col self.on_epoch_end()
Сначала мы определяем конструктор для инициализации конфигурации генератора. Обратите внимание, что здесь мы предполагаем, что путь к данным находится в столбце фрейма данных. Следовательно, мы определяем параметры x_col и y_col. Это также может быть имя каталога, из которого вы можете загрузить данные.
Метод on_epoch_end - это метод, который вызывается после каждой эпохи. Здесь мы можем добавить такие процедуры, как перемешивание.
def on_epoch_end(self): self.index = np.arange(len(self.indices)) if self.shuffle == True: np.random.shuffle(self.index)
По сути, мы изменили порядок строк фрейма данных в этом фрагменте.
Еще один служебный метод, который у нас есть, - это __len__. По сути, он возвращает количество шагов в эпоху, используя образцы и размер пакета.
def __len__(self): # Denotes the number of batches per epoch return len(self.indices) // self.batch_size
Затем следует метод __getitem__, который вызывается с номером пакета в качестве аргумента для получения заданного пакета данных.
def __getitem__(self, index): # Generate one batch of data # Generate indices of the batch index = self.index[index * self.batch_size:(index + 1) * self.batch_size] # Find list of IDs batch = [self.indices[k] for k in index] # Generate data X, y = self.__get_data(batch) return X, y
По сути, мы просто получили перетасованные индексы, вызвали набор данных из другого метода и вернули его вызывающей стороне. Здесь может быть реализована сама логика генерации набора данных. Однако рекомендуется перенести его в другое место.
Наконец, мы пишем логику генерации данных в методе __ get_data. Поскольку этот метод будет вызван нами, мы можем назвать его как угодно. Более того, нет причин для того, чтобы этот метод был общедоступным, поэтому мы определяем его как частный.
def __get_data(self, batch): # X.shape : (batch_size, *dim) # We can have multiple Xs and can return them as a list X = # logic to load the data from storage y = # logic for the target variables # Generate data for i, id in enumerate(batch): # Store sample X[i,] = # logic # Store class y[i] = # labels return X, y
Кроме того, мы можем добавить процедуры предварительной обработки / дополнения, чтобы включить их в режиме реального времени. В приведенном выше фрагменте кода X и y загружаются из источников данных в соответствии с аргументом индексов пакета, переданным в метод. Это может быть что угодно, от загрузки изображений до загрузки текстов или и того, и другого одновременно, или любых других данных.
После включения всех методов полный генератор выглядит так:
Заключение
В этой статье мы увидели полезность генераторов данных при обучении моделей с огромным объемом данных. Мы заглянули в API ImageDataGenerator, чтобы узнать, что это такое, и удовлетворить потребность в пользовательских. Затем мы наконец узнали, как реализовать собственный генератор данных, создав подкласс API tf.keras.utils.Sequence.
Не стесняйтесь копировать этот код и добавлять к нему свою собственную логику генератора.