我正在尝试使用预训练的InceptionV3模型对food-101 dataset进行分类,其中包含101个类别的食物图像,每个类别1000个。我已经将这个数据集预处理成一个单独的hdf5文件(我认为这与在训练时加载图像相比有益)到目前为止,其中包含以下表格:
数据拆分是标准的70%列车,20%验证,10%测试,因此例如valid_img的大小为20200 * 299 * 299 * 3。这些标签是针对Keras的一个编码,因此valid_labels的大小为20200 * 101.
此hdf5文件的大小为27.1 GB ,因此它不适合我的内存。 (有8 GB,虽然在运行Ubuntu时实际上只有4-5演出可用。而且我的GPU是带有2 GB VRAM的GTX 960,到目前为止,当我尝试启动时,它似乎有1.5 GB可用于python训练脚本)。我正在使用Tensorflow后端。
我的第一个想法是使用model.train_on_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_on_batch
为验证或批量混洗提供了0支持,因此每个时期的批次不是相同的顺序。
所以我看向model.fit_generator()
,它具有提供与fit()
相同功能的优良特性,再加上内置ImageDataGenerator
你可以做图像增强(旋转,水平翻转)等)与CPU同时使您的模型更加健壮。我的问题是,如果我理解正确,ImageDataGenerator.flow(x,y)
方法一次需要所有样本和标签,但我的训练/验证数据不适合我的RAM。
以下是我认为自定义数据生成器出现在图片中的内容,但在广泛查看了我可以在Keras GitHub / Issues页面上找到的一些示例后,我仍然不知道如何实现自定义生成器,这将从我的hdf5文件批量读取数据。有人能为我提供一个好的例子或指针吗?如何将自定义批处理生成器与图像扩充相结合?或者可能更容易为train_on_batch()
实施某种手动验证和批量改组?如果是这样,我也可以在那里使用一些指针。
答案 0 :(得分:2)
如果我对您的理解正确,那么您想使用HDF5中的数据(不适合内存),同时在其上使用数据扩充。
我和您的处境相同,并且我发现这段代码可能会对一些修改有所帮助:
https://gist.github.com/wassname/74f02bc9134897e3fe4e60784f5aaa15
答案 1 :(得分:1)
对于仍在寻找答案的任何人,我围绕着ImageDataGeneator的apply_transform
方法进行了以下“粗包装”。
from numpy.random import uniform, randint
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
import numpy as np
class CustomImagesGenerator:
def __init__(self, x, zoom_range, shear_range, rescale, horizontal_flip, batch_size):
self.x = x
self.zoom_range = zoom_range
self.shear_range = shear_range
self.rescale = rescale
self.horizontal_flip = horizontal_flip
self.batch_size = batch_size
self.__img_gen = ImageDataGenerator()
self.__batch_index = 0
def __len__(self):
# steps_per_epoch, if unspecified, will use the len(generator) as a number of steps.
# hence this
return np.floor(self.x.shape[0]/self.batch_size)
def next(self):
return self.__next__()
def __next__(self):
start = self.__batch_index*self.batch_size
stop = start + self.batch_size
self.__batch_index += 1
if stop > len(self.x):
raise StopIteration
transformed = np.array(self.x[start:stop]) # loads from hdf5
for i in range(len(transformed)):
zoom = uniform(self.zoom_range[0], self.zoom_range[1])
transformations = {
'zx': zoom,
'zy': zoom,
'shear': uniform(-self.shear_range, self.shear_range),
'flip_horizontal': self.horizontal_flip and bool(randint(0,2))
}
transformed[i] = self.__img_gen.apply_transform(transformed[i], transformations)
return transformed * self.rescale
可以这样称呼:
import h5py
f = h5py.File("my_heavy_dataset_file.hdf5", 'r')
images = f['mydatasets/images']
my_gen = CustomImagesGenerator(
images,
zoom_range=[0.8, 1],
shear_range=6,
rescale=1./255,
horizontal_flip=True,
batch_size=64
)
model.fit_generator(my_gen)
答案 2 :(得分:0)
这是我的h *文件每个时代的随机数据解决方案。 指数是指火车或谷物指数清单。
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])
答案 3 :(得分:-1)
你想编写一个函数来加载来自HDF5的图像,然后将yield
s(而不是return
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而不是目录中读取的事实。