Пользовательский генератор данных изображений (керас) работает очень медленно из-за преобразования списка массивов numpy в один массив - PullRequest
0 голосов
/ 09 марта 2020

У меня есть узкое место в обучении моей нейронной модели. Я написал собственный генератор, который загружает изображения и маски на лету, чтобы передать их моей модели для обучения. Я обнаружил, что даже когда я использовал графический процессор, он был очень медленным и сузил это до того момента, когда я преобразую свою группу масок из списка массивов в массив с размерами (партия, высота, ширина, классы). Кажется, это занимает много времени, однако я не могу понять, почему? Каждая маска имеет размер 224x224x4 (4 класса, так как она была закодирована в горячем виде), и я использую только пакет из 16. Если я сгенерирую список случайного набора (224x224x4) массивов и приму np.array (массивы) Операция им очень быстрая. Любые идеи будут оценены?

import cv2
import os
import numpy as np
from tensorflow import keras
from skimage import img_as_bool
from skimage.transform import resize


class DataGenerator(keras.utils.Sequence):
    def __init__(self, imgIds, maskIds, imagePath, maskPath, weights=[1,1,1,1],
                        batchSize=16, shuffle=False):
        self.imgIds = imgIds
        self.maskIds = maskIds
        self.imagePath = imagePath
        self.maskPath = maskPath
        self.weights = np.array(weights)
        self.batchSize = batchSize
        self.shuffle = shuffle


    '''
    for each image id load the patch and corresponding mask
    '''
    def __load__(self, imgName, maskName):

        img = cv2.imread(os.path.join(self.imagePath,imgName))
        img = img/255.0

        mask = np.load(os.path.join(self.maskPath,maskName))
        mask = np.multiply(mask, self.weights)
        mask = tf.cast(mask, tf.float32)

        return (img, mask)


    '''
    get the files for each batch (override __getitem__ method)
    '''
    def __getitem__(self, index):

        if(index+1)*self.batchSize > len(self.imgIds):
            self.batchSize = len(self.imgIds) - index*self.batchSize

        batchImgs = self.imgIds[self.batchSize*index:self.batchSize*(index+1)]
        batchMasks = self.maskIds[self.batchSize*index:self.batchSize*(index+1)]
        batchfiles = [self.__load__(imgFile, maskFile) for imgFile, maskFile in zip(batchImgs, batchMasks)]
        images, masks = zip(*batchfiles)

        return np.array(list(images)), np.array(list(masks))


    '''
    Return number of steps per batch that are needed (override __len__ method)
    '''
    def __len__(self):
        return int(np.ceil(len(self.imgIds)/self.batchSize))

1 Ответ

1 голос
/ 09 марта 2020

Попробуйте сделать как можно больше в tensorflow (я использую tensorflow 2.1.0):

def __load__(self, imgName, maskName):
  path = os.path.join(self.imagePath,imgName)
  img = tf.image.decode_jpeg(tf.io.read_file(path)))
  img = img/255.0
  mask = np.load(os.path.join(self.maskPath,maskName))
  mask = tf.cast(mask, tf.float32)
  mask = tf.multiply(mask, self.weights)

np.multiply определенно плохая идея, наверняка, пользователь tf.multiply. Я беспокоюсь о np.load и о том, что оно может быть медленным, так как ваши данные вообще не сжимаются (что означает больше операций ввода-вывода).

Вы можете исправить проблему np.load, используя tf.data.TFRecord для сохранения ваших данных, и использовать tf.data.Dataset в качестве генератора данных. Это способ повышения производительности при загрузке данных в tensorflow.

...