【问题标题】:Custom Datagenerator自定义数据生成器
【发布时间】:2019-11-10 08:23:27
【问题描述】:

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

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

我面临的问题是一次性加载的数据太大而无法放入当前的机器内存中,因此我无法使用完整的数据集。

我尝试使用数据生成器,但不想遵循它遵循的目录约定,也无法消除扩充部分。

问题是有没有办法从磁盘加载批次以确保两个规定的条件。

【问题讨论】:

  • 您的示例缺少Sample 的来源
  • 示例只是包含一些信息以及图像的类名
  • 您可以只将 Classes 视为 @AKX 的类列表

标签: python keras


【解决方案1】:

我相信你应该看看这个post

您正在寻找的是 Keras 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

如果仍有歧义,请随时再次询问。

【讨论】:

  • 帖子建议所有文件必须在同一个目录中,但我在单独的文件夹中有文件,所以我相信这可能不适合我。
  • @pure_virtual 请查看新的编辑以回应您的评论。
  • 是的,我有包含路径的图像名称。比如 Dodge_Charger 类的 basePath/Dodge_Charger/1985/ref1.jpg。
  • 是的,这正是我的观点,如果采用所描述的格式,代码应该适合你。
【解决方案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

【讨论】:

  • 我不明白截断的目的是什么?
  • 它在那里,因此您可以选择足够小的批量大小。例如,如果您估计内存中可以容纳 20% 的图像,请将其设置为 0.2,并且每次都会随机选择 20%。
  • 如果我想训练所有图像,我认为这不是很有用,对吧?
  • 它最终可能会在所有图像上进行训练。无论如何,当然,我可以让事情变得更有状态,所以它会使用所有图像。
  • 已编辑 - 现在它在 MyIndex 列表中分页。
【解决方案3】:

如果您想从磁盘加载,使用您使用的 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)。

【讨论】:

    猜你喜欢
    • 2020-12-10
    • 2019-03-16
    • 1970-01-01
    • 1970-01-01
    • 2014-01-23
    • 2012-03-28
    • 1970-01-01
    • 2017-07-17
    相关资源
    最近更新 更多