web-dev-qa-db-ja.com

メモリに収まらない大きなhdf5ファイル用のKerasカスタムデータジェネレータ

事前にトレーニングされたInceptionV3モデルを使用して、 food-101データセット を分類しようとしています。これには、101のカテゴリ、カテゴリごとに1000の食品画像が含まれています。これまで、このデータセットを単一のhdf5ファイルに前処理しました(トレーニング時に外出先で画像を読み込むよりも有益だと思いました)。このファイルには次の表が含まれています。

Food-101 hdf5 file structure

データ分割は、標準の70%トレイン、20%検証、10%テストであるため、たとえば、valid_imgのサイズは20200 * 299 * 299 * 3です。ラベルはKeras用にonehotencodedされているため、valid_labelsのサイズは20200 * 101です。

このhdf5ファイルのサイズは27.1 GBなので、私のメモリには収まりません。 (8 GBを持っていますが、Ubuntuの実行中にはおそらく4〜5ギガしか使用できません。また、私のGPUは2GBのVRAMを備えたGTX960であり、これまでのところ1.5GBがpythonトレーニングスクリプトを開始しようとすると)Tensorflowバックエンドを使用しています。

私が持っていた最初のアイデアは、次のように二重にネストされたforループでmodel.train_on_batch()を使用することです。

_#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()と同じ機能をすべて提供するNiceプロパティを備えたfit()に目を向けました。さらに、組み込みのImageDataGeneratorを使用すると、画像の拡張(回転、 CPUと同時に水平反転など)を行うことで、モデルをより堅牢にすることができます。ここでの私の問題は、正しく理解していれば、ImageDataGenerator.flow(x,y)メソッドはすべてのサンプルとラベルを一度に必要としますが、トレーニング/検証データがRAMに収まらないことです。

ここでカスタムデータジェネレーターが登場すると思いますが、Keras GitHub/Issuesページで見つけたいくつかの例を詳しく調べた後、どのように実装すればよいのかまだわかりません。私のhdf5ファイルからデータのバッチを読み取るカスタムジェネレーター。誰かが私に良い例やポインターを提供できますか?カスタムバッチジェネレーターを画像拡張とどのように組み合わせることができますか?それとも、train_on_batch()のある種の手動検証とバッチシャッフルを実装する方が簡単ですか?もしそうなら、私もそこにいくつかのポインタを使用することができます。

7
Zoltán Umlauf

まだ答えを探している人のために、私は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
Samuel Prevost

私があなたを正しく理解していれば、HDF5からのデータ(メモリに収まらない)を使用すると同時に、データ拡張を使用する必要があります。

私はあなたと同じ状況にあり、いくつかの変更を加えることで役立つ可能性のあるこのコードを見つけました。

https://Gist.github.com/wassname/74f02bc9134897e3fe4e60784f5aaa15

2
Helder

これは、h5ファイルを使用したエポックごとのデータのシャッフルに対する私のソリューションです。 indexは、trainまたは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])
0
chunhui