Python Keras自定义数据生成器,用于不适合内存的大型hdf5文件

Python Keras自定义数据生成器,用于不适合内存的大型hdf5文件,python,keras,hdf5,Python,Keras,Hdf5,我正在尝试使用预训练的InceptionV3模型来分类,它包含101个类别的食物图像,每个类别1000个。到目前为止,我已经将该数据集预处理为一个hdf5文件(我认为这比训练时在路上加载图像有益),其中包含以下表格: 数据分割是标准的70%序列、20%验证、10%测试,因此,例如,有效的img大小为20200*299*299*3。标签为Keras的onehotencoded,因此有效的_标签的大小为20200*101 此hdf5文件的大小为27.1 GB,因此无法放入我的内存。(有8GB的内存

我正在尝试使用预训练的InceptionV3模型来分类,它包含101个类别的食物图像,每个类别1000个。到目前为止,我已经将该数据集预处理为一个hdf5文件(我认为这比训练时在路上加载图像有益),其中包含以下表格:

数据分割是标准的70%序列、20%验证、10%测试,因此,例如,有效的img大小为20200*299*299*3。标签为Keras的onehotencoded,因此有效的_标签的大小为20200*101

此hdf5文件的大小为27.1 GB,因此无法放入我的内存。(有8GB的内存,尽管在运行Ubuntu时,实际上可能只有4-5G可用。另外,我的GPU是GTX 960,带有2GB的VRAM,到目前为止,当我尝试启动培训脚本时,python似乎有1.5GB可用)。我正在使用Tensorflow后端

我的第一个想法是使用
model.train\u on\u batch()
和双嵌套for循环,如下所示:

#Loading InceptionV3, adding my fully connected layers, compiling model...    

dataset = h5py.File('/home/uzoltan/PycharmProjects/food-101/food-101_299x299.hdf5', 'r')
    epoch = 50
    for i in range(epoch):
        for i in range(100): #1000 images can fit in the memory easily, this could probably be range(10) too
            train_images = dataset["train_img"][i * 706:(i + 1) * 706, ...]
            train_labels = dataset["train_labels"][i * 706:(i + 1) * 706, ...]
            val_images = dataset["valid_img"][i * 202:(i + 1) * 202, ...]
            val_labels = dataset["valid_labels"][i * 202:(i + 1) * 202, ...]
            model.train_on_batch(x=train_images, y=train_labels, class_weight=None,
                                 sample_weight=None, )
这种方法的问题是
train\u on\u batch
为验证或批洗牌提供了0支持,因此每个历元的批顺序都不相同

因此,我研究了
model.fit_generator()
,它具有提供与
fit()
相同功能的优良特性,再加上内置的
ImageDataGenerator
,您可以在使用CPU的同时进行图像增强(旋转、水平翻转等),因此您的模型可以更加健壮。我这里的问题是,如果我理解正确,
ImageDataGenerator.flow(x,y)
方法一次需要所有的样本和标签,但是我的培训/验证数据无法放入我的RAM中


我认为自定义数据生成器就是在这里出现的,但在仔细查看了Keras GitHub/Issues页面上的一些示例后,我仍然不知道如何实现自定义生成器,这将从我的hdf5文件中读取成批数据。有人能给我提供一个好的示例或指针吗?如何将自定义批处理生成器与图像增强相结合?或者,对于
train\u on\u batch()
,实现某种手动验证和批量洗牌是否更容易?如果是这样,我也可以在那里使用一些指针。

您想编写一个函数,从HDF5加载图像,然后
生成
s(而不是
返回
s)作为numpy数组。下面是一个使用OpenCV直接从给定目录中的.png/.jpg文件加载图像的简单示例:

def generate_data(directory, batch_size):
    """Replaces Keras' native ImageDataGenerator."""
    i = 0
    file_list = os.listdir(directory)
    while True:
        image_batch = []
        for b in range(batch_size):
            if i == len(file_list):
                i = 0
                random.shuffle(file_list)
            sample = file_list[i]
            i += 1
            image = cv2.resize(cv2.imread(sample[0]), INPUT_SHAPE)
            image_batch.append((image.astype(float) - 128) / 128)

        yield np.array(image_batch)
显然,您将不得不修改它以从HDF5读取

编写函数后,用法很简单:

model.fit_generator(
generate_data('~/my_data', batch_size),
steps_per_epoch=len(os.listdir('~/my_data')) // batch_size)

再次修改以反映您从HDF5而不是目录读取数据的事实。

这是我使用h5文件对每个历元的数据进行无序排列的解决方案。 索引指列车或val索引列表

def generator(h5path, indices, batchSize=128, is_train=True, aug=None):

    db = h5py.File(h5path, "r")
    with open("mean.json") as f:
        mean = json.load(f)
    meanV = np.array([mean["R"], mean["G"], mean["B"]])

    while True:

        np.random.shuffle(indices)
        for i in range(0, len(indices), batchSize):
            t0 = time()
            batch_indices = indices[i:i+batchSize]
            batch_indices.sort()

            by = db["labels"][batch_indices,:]
            bx = db["images"][batch_indices,:,:,:]

            bx[:,:,:,0] -= meanV[0]
            bx[:,:,:,1] -= meanV[1]
            bx[:,:,:,2] -= meanV[2]
            t1=time()

            if is_train:

                #bx = random_crop(bx, (224,224))
                if aug is not None:
                    bx,by = next(aug.flow(bx,by,batchSize))

            yield (bx,by)


h5path='all_224.hdf5'   
model.fit_generator(generator(h5path, train_indices, batchSize=batchSize, is_train=True, aug=aug),
                steps_per_epoch = 20000//batchSize,
                validation_data= generator(h5path, test_indices, is_train=False, batchSize=batchSize), 
                validation_steps = 2424//batchSize,
                epochs=args.epoch, 
                max_queue_size=100,
                callbacks=[checkpoint, early_stop])

如果我理解正确,您希望使用HDF5中的数据(不适合内存),同时在其上使用数据增强

我的处境和你一样,我发现这段代码可能会有帮助,只需稍作修改:


对于仍在寻找答案的人,我对ImageDataGeneator的方法做了以下“粗略包装”

来自numpy.random导入统一,randint
从tensorflow.python.keras.preprocessing.image导入ImageDataGenerator
将numpy作为np导入
类CustomImageGenerator:
定义初始(自、x、缩放范围、剪切范围、重缩放、水平翻转、批量大小):
self.x=x
self.zoom\u range=缩放范围
自剪切范围=剪切范围
self.rescale=重新缩放
自水平翻转=水平翻转
self.batch\u size=批次大小
self.\uu img\u gen=ImageDataGenerator()
self.\u批量\u索引=0
定义(自我):
#如果未指定,每个历元的步数将使用len(生成器)作为一系列步数。
#因此
返回np.floor(self.x.shape[0]/self.batch\u size)
def next(自我):
返回自我。下一个
定义下一个(自我):
开始=self.\u批次索引*self.batch\u大小
停止=开始+自批大小
自。\批量\索引+=1
如果停止>len(self.x):
提出停止迭代
transformed=np.array(self.x[start:stop])#从hdf5加载
对于范围内的i(len(转换)):
缩放=均匀(自缩放范围[0],自缩放范围[1])
转换={
“zx”:缩放,
“zy”:缩放,
“剪切”:均匀(-自剪切范围,自剪切范围),
“水平翻转”:self.horizontal\u翻转和bool(randint(0,2))
}
转换的[i]=自我。应用转换(转换的[i],转换)
返回已转换*自重缩放
可以这样称呼:

导入h5py
f=h5py.File(“my_heavy_dataset_File.hdf5”,“r”)
images=f['mydataset/images']
my_gen=自定义图像生成器(
图像,
缩放范围=[0.8,1],
剪切范围=6,
重新缩放=1./255,
水平翻转=真,
批量大小=64
)
型号安装发电机(my\U gen)

为什么您不能简单地将所有文件提取到单独的目录并使用函数?在
keras
中有一个指定函数。是的,但OP询问如何为该函数未涵盖的用例编写自定义数据生成器的示例。这就回答了这个问题。你是正确的,他们可能会更好地简单地从HDF5中取出图像并使用来自目录的
flow\u
。不-他甚至一次也没有提到来自目录的
flow\u。他提到从
h5
加载图像,然后使用
flow
@Jeff Alan和任何关于如何添加图像增强的指针