自定义数据生成器

时间:2019-06-28 11:09:02

标签: python keras

我有一个自定义文件,其中包含我所有图像的路径及其标签,这些文件的加载方式如下:

MyIndex=pd.read_table('./MySet.txt')

MyIndex具有两个感兴趣的列,分别为 ImagePath ClassName

接下来,我将进行一些训练测试拆分并将输出标签编码为:

images=[]
for index, row in MyIndex.iterrows():
    img_path=basePath+row['ImageName']
    img = image.load_img(img_path, target_size=(299, 299))
    img_path=None
    img_data = image.img_to_array(img)
    img=None
    images.append(img_data)
    img_data=None


images[0].shape

Classes=Sample['ClassName']
OutputClasses=Classes.unique().tolist()

labels=Sample['ClassName']
images=np.array(images, dtype="float") / 255.0
(trainX, testX, trainY, testY) = train_test_split(images,labels, test_size=0.10, random_state=42)
trainX, valX, trainY, valY = train_test_split(trainX, trainY, test_size=0.10, random_state=41)

images=None
labels=None

encoder = LabelEncoder()
encoder=encoder.fit(OutputClasses)
encoded_Y = encoder.transform(trainY)
# convert integers to dummy variables (i.e. one hot encoded)
trainY = to_categorical(encoded_Y, num_classes=len(OutputClasses))

encoded_Y = encoder.transform(valY)
# convert integers to dummy variables (i.e. one hot encoded)
valY = to_categorical(encoded_Y, num_classes=len(OutputClasses))

encoded_Y = encoder.transform(testY)
# convert integers to dummy variables (i.e. one hot encoded)
testY = to_categorical(encoded_Y, num_classes=len(OutputClasses))

datagen=ImageDataGenerator(rotation_range=90,horizontal_flip=True,vertical_flip=True,width_shift_range=0.25,height_shift_range=0.25)
datagen.fit(trainX,augment=True)

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])


batch_size=128
model.fit_generator(datagen.flow(trainX,trainY,batch_size=batch_size), epochs=500, 
                    steps_per_epoch=trainX.shape[0]//batch_size,validation_data=(valX,valY))

我面临的问题是,一次加载的数据太大而无法容纳当前的计算机内存,因此我无法使用完整的数据集。

我尝试使用数据生成器,但不希望遵循其遵循的目录约定,而且也无法消除扩展部分。

问题是,有一种方法可以从磁盘上加载批处理,以确保满足上述两个条件。

3 个答案:

答案 0 :(得分:2)

如果要从磁盘加载,可以方便地使用所使用的ImageDataGenerator。

有两种方法可以做到这一点。通过使用flow_from_directory声明数据目录。或者,您可以将flow_from_dataframe与Pandas数据框一起使用

如果要获取路径列表,则不应使用会生成大量图像的自定义生成器。这是一个存根:

def load_image_from_path(path):
    "Loading and preprocessing"
    ...

def my_generator():
    length = df.shape[0]
    for i in range(0, length, batch_size)
        batch = df.loc[i:min(i+batch_size, length-1)]
        x, y = map(load_image_from_path, batch['ImageName']), batch['ClassName']
        yield x, y

注意:在fit_generator中还有一个名为validation_data的附加生成器,您很可能猜到了-验证。 一种选择是使生成器通过索引以进行选择,以分割训练和测试(假设数据经过了改组,如果未选中此out,则为该代码)。

答案 1 :(得分:2)

我相信您应该看看这个post

您要查找的是K eras flow_from_dataframe ,可通过在数据帧中提供文件名及其标签,并提供顶部目录路径,从磁盘加载批次包含您所有的图片

在代码中进行一些修饰,并从共享的链接中借用一些修饰:

MyIndex=pd.read_table('./MySet.txt')

Classes=MyIndex['ClassName']
OutputClasses=Classes.unique().tolist()

trainDf=MyIndex[['ImageName','ClassName']]
train, test = train_test_split(trainDf, test_size=0.10, random_state=1)


#creating a data generator to load the files on runtime
traindatagen=ImageDataGenerator(rotation_range=90,horizontal_flip=True,vertical_flip=True,width_shift_range=0.25,height_shift_range=0.25,
    validation_split=0.1)
train_generator=traindatagen.flow_from_dataframe(
    dataframe=train,
    directory=basePath,#the directory containing all your images
    x_col='ImageName',
    y_col='ClassName',
    class_mode='categorical',
    target_size=(299, 299),
    batch_size=batch_size,
    subset='training'
)
#Also a generator for the validation data
val_generator=traindatagen.flow_from_dataframe(
    dataframe=train,
    directory=basePath,#the directory containing all your images
    x_col='ImageName',
    y_col='ClassName',
    class_mode='categorical',
    target_size=(299, 299),
    batch_size=batch_size,
    subset='validation'
)


STEP_SIZE_TRAIN=train_generator.n//train_generator.batch_size
STEP_SIZE_VALID=val_generator.n//val_generator.batch_size
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit_generator(generator=train_generator, steps_per_epoch=STEP_SIZE_TRAIN,
                    validation_data=val_generator,
                    validation_steps=STEP_SIZE_VALID,
                    epochs=500)

还要注意,现在您不需要像原始代码中那样对标签进行编码,并且也不需要图像加载代码。

我还没有尝试过这段代码本身,所以请尝试修复您可能遇到的任何错误,因为主要的目的是为您提供基本的想法。

针对您的comment: 如果所有文件都位于不同目录中,那么一种解决方案是使您的ImagesName将相对路径(包括中间目录)存储在类似“ ./Dir/File.jpg”的路径中,然后将所有目录移动到一个文件夹中并使用一个作为基本路径,其他所有内容保持不变。 同样查看加载了文件的代码段,看起来您已经在ImageName列中存储了文件路径,因此建议的方法应该对您有用。

images=[]
for index, row in MyIndex.iterrows():
    img_path=basePath+row['ImageName']
    img = image.load_img(img_path, target_size=(299, 299))
    img_path=None
    img_data = image.img_to_array(img)
    img=None
    images.append(img_data)
    img_data=None

如果仍然存在歧义,请再次询问。

答案 2 :(得分:2)

我认为最简单的方法是每个生成器仅加载部分图像,并以较小的批次重复调用.fit_generator()

这个例子使用`random.random()`选择要加载的图像–您可以使用更复杂的东西。

以前的版本使用random.random(),但是我们也可以像在此修订版中一样使用起始索引和页面大小来永久地循环浏览图像列表。

import itertools


def load_images(start_index, page_size):
    images = []
    for index in range(page_size):
        # Generate index using modulo to loop over the list forever
        index = (start_index + index) % len(rows)
        row = MyIndex[index]
        img_path = basePath + row["ImageName"]
        img = image.load_img(img_path, target_size=(299, 299))
        img_data = image.img_to_array(img)
        images.append(img_data)
    return images


def generate_datagen(batch_size, start_index, page_size):
    images = load_images(start_index, page_size)

    # ... everything else you need to get from images to trainX and trainY, etc. here ...

    datagen = ImageDataGenerator(
        rotation_range=90,
        horizontal_flip=True,
        vertical_flip=True,
        width_shift_range=0.25,
        height_shift_range=0.25,
    )
    datagen.fit(trainX, augment=True)
    return (
        trainX,
        trainY,
        valX,
        valY,
        datagen.flow(trainX, trainY, batch_size=batch_size),
    )


model.compile(
    loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"]
)

page_size = (
    500
)  # load 500 images at a time; change this as suitable for your memory condition

for page in itertools.count():  # Count from zero to forever.
    batch_size = 128
    trainX, trainY, valX, valY, generator = generate_datagen(
        128, page * page_size, page_size
    )
    model.fit_generator(
        generator,
        epochs=5,
        steps_per_epoch=trainX.shape[0] // batch_size,
        validation_data=(valX, valY),
    )
    # TODO: add a `break` clause with a suitable condition