自定义数据生成器

12 浏览
0 Comments

自定义数据生成器

我有一个自定义文件,其中包含所有图像的路径和标签,我使用以下方式将其加载到数据框中:

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

MyIndex有两列感兴趣的列ImagePathClassName

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

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))

我的问题是,一次性加载的数据过大,无法适应当前计算机内存,因此我无法使用完整的数据集。

我试图使用数据生成器进行操作,但不想遵循其遵循的目录约定,也无法淘汰增强部分。

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

admin 更改状态以发布 2023年5月24日
0
0 Comments

我认为最简单的方法是每个生成器加载部分图像,并重复调用.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

0
0 Comments

我认为你应该看看这篇文章

你要找的是Keras flow_from_dataframe功能,它可以通过在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)

还要注意,现在你不需要在原始代码中编码标签,并省略图像加载代码。

我没有使用过这段代码,所以请尝试修复您可能遇到的任何错误,因为主要重点是向您提供基本思路。

回复你的评论

如果您的所有文件都在不同的目录中,则一种解决方法是将ImageName存储相对路径以包括中间目录作为路径的文件名,例如'./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

如果仍然存在一些模糊,可以随时再次询问。

0