Convert Keras generator to Tensorflow Dataset to train Resnet50

Viewed 772

I am converting python code from keras namespace to tf.keras. It trains Resnet50. New Model.fit() method can't find adapter for my simple generator and validation_data does not even support generators anymore. So I am trying to convert it to Dataset using tensorflow.data.Dataset.from_generator method.

Images are grayscale and stored in raw bytes - one byte for one pixel. Generator has such rows

        def __next__( self ):
            return self.next()

        def __call__( self ):
            return self.next()

        def next( self ):
            #reading files
            ...

            resultLabels = numpy.zeros( ( count, len( classes ) ), "float32" )
            resultImages = numpy.zeros( ( count, patchSize, patchSize, 3 ), "float32" )

            #filling result with images and labels
                ...
                fileBytes = numpy.reshape( numpy.fromfile( self.ImageLabelsAndPaths[i][1], "uint8" ), (patchSize, patchSize), "F" ).astype( "float32" )

                imageWithChannels = numpy.zeros( ( patchSize, patchSize, 3 ), "float32" )
                # Because Resnet50 requires RGB images and we have grayscale
                imageWithChannels[:,:,0] = fileBytes
                imageWithChannels[:,:,1] = fileBytes
                imageWithChannels[:,:,2] = fileBytes

                resultImages[i - cursor] = imageWithChannels

            return ( resultImages, resultLabels )

So resultImages is an array with length of batch_size=16, that contains arrays of image pixels. Numpy.shape is (16, 256, 256, 3) and resultLabels shape is (16, 3) - 3 classes for now.

Next I am converting it to Dataset

            trainGenerator = FileIterator( "train" )
            trainDataset = tf.data.Dataset.from_generator( trainGenerator, (tf.float32, tf.float32), (tf.TensorShape([batchSize, patchSize, patchSize, 3]), tf.TensorShape([batchSize, len(classes)]) ) )
            validationGenerator = FileIterator( "validate" )
            validationDataset = tf.data.Dataset.from_generator( validationGenerator, (tf.float32, tf.float32), (tf.TensorShape([batchSize, patchSize, patchSize, 3]), tf.TensorShape([batchSize, len(classes)]) ) )

but I am getting error

TypeError: `generator` yielded an element that did not match the expected structure. The expected structure was (tf.float32, tf.float32), but the yielded element was [[[[185. 185. 185.]
   [158. 158. 158.]
   [145. 145. 145.]
   ...

And code example for Dataset.from_generator has an array as a second item in tuple and similar output_types=(tf.int64, tf.int64). And I guess it works there.

Attempt to add arrays to type results in another error

TypeError: unhashable type: 'list'

What should I change to make it work?

1 Answers

Ok, after spending two more days, trying to fix some really misleading errors, and getting python.exe to crash when it was finally working, I was able to convert my generator to tensorflow dataset.

I was unable to make it work with batches, and numpy.array is not accepted by Dataset because it is not a sequential in Dataset's world, and it is important to return a tuple, don't know how examples with "yield" and "return data, labels" works.

Generator

        def __iter__(self):
            return self

        def __call__( self ):
            return self

        def __len__(self):
            return self.TotalCount

        def __next__( self ):
            ...
            resultLabel = numpy.zeros( len( classes ), "float32" )
            resultImage = numpy.zeros( ( patchSize, patchSize, 3 ), "float32" )
            # fill those two
            ...

            return (resultImage.tolist(), resultLabel.tolist())

And dataset + model.fit

            trainGenerator = FileIterator( "train" )
            validationGenerator = FileIterator( "validate" )

            trainDataset = tf.data.Dataset.from_generator( trainGenerator, output_types=(tf.float32, tf.float32), output_shapes=(tf.TensorShape([patchSize, patchSize, 3]), tf.TensorShape([len(classes)]) ) )
            trainDataset = trainDataset.batch( batchSize )
            validationDataset = tf.data.Dataset.from_generator( validationGenerator, output_types=(tf.float32, tf.float32), output_shapes=(tf.TensorShape([patchSize, patchSize, 3]), tf.TensorShape([len(classes)]) ) )
            validationDataset = validationDataset.batch( batchSize )


            trainResult = model.fit( x = trainDataset,
                                     epochs = epochsForDenseLayer,
                                     steps_per_epoch = trainGenerator.StepsPerEpoch,
                                     verbose = 2,
                                     validation_data = validationDataset,
                                     validation_steps = validationGenerator.StepsPerEpoch,
                                     validation_freq = 1,
                                     shuffle = False, # already shuffled by generator
                                     workers = cpuCoresCount,
                                     use_multiprocessing = False
                                    )
Related