How to combine a pre-trained KerasLayer from TensorFlow (v. 2) Hub and tfrecords?
I have a tfrecord with 23 classes with 35 images in each class (805 in total). My current tfrecord read function is:
def read_tfrecord(serialized_example):
feature_description = {
'image': tf.io.FixedLenFeature((), tf.string),
'label': tf.io.FixedLenFeature((), tf.int64),
'height': tf.io.FixedLenFeature((), tf.int64),
'width': tf.io.FixedLenFeature((), tf.int64),
'depth': tf.io.FixedLenFeature((), tf.int64)
}
example = tf.io.parse_single_example(serialized_example, feature_description)
image = tf.io.parse_tensor(example['image'], out_type=float)
image_shape = [example['height'], example['width'], example['depth']]
image = tf.reshape(image, image_shape)
label = tf.cast(example["label"], tf.int32)
image = image/255
return image, label
I then have a make_dataset function that looks like this:
def make_dataset(tfrecord, BATCH_SIZE, EPOCHS, cache=True):
files = tf.data.Dataset.list_files(os.path.join(os.getcwd(), tfrecord))
dataset = tf.data.TFRecordDataset(files)
if cache:
if isinstance(cache, str):
dataset = dataset.cache(cache)
else:
dataset = dataset.cache()
dataset = dataset.shuffle(buffer_size=FLAGS.shuffle_buffer_size)
dataset = dataset.map(map_func=read_tfrecord, num_parallel_calls=AUTOTUNE)
dataset = dataset.repeat(EPOCHS)
dataset = dataset.batch(batch_size=BATCH_SIZE)
dataset = dataset.prefetch(buffer_size=AUTOTUNE)
return dataset
This make_dataset function gets passed into
train_ds = make_dataset(tfrecord=FLAGS.tf_record, BATCH_SIZE=BATCH_SIZE, EPOCHS=EPOCH)
image_batch, label_batch = next(iter(train_ds))
feature_extractor_layer = hub.KerasLayer(url, input_shape=IMAGE_SHAPE + (3,))
feature_batch = feature_extractor_layer(image_batch)
feature_extractor_layer.trainable = False
model = tf.keras.Sequential([feature_extractor_layer, layers.Dense(2048, input_shape=(2048,)), layers.Dense(len(CLASS_NAMES), activation='softmax')])
model.summary()
predictions = model(image_batch)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=rate),
loss='categorical_crossentropy',
metrics=['acc'])
batch_stats_callback = CollectBatchStats()
STEPS_PER_EPOCH = np.ceil(image_count / BATCH_SIZE)
history = model.fit(image_batch, label_batch, epochs=EPOCH, batch_size=BATCH_SIZE, steps_per_epoch=STEPS_PER_EPOCH, callbacks=[batch_stats_callback])
This code runs in the sense that it outputs the usual information about how many epochs I have and some training accuracy data (which is 0 with a loss around 100k). The error I get doesn't have any meaning to me, as it says: Function instantiation has undefined input shape at index: 100 in the outer inference context. You can substitute the number to anything below 1000 (not sure if it ever surpasses the number of images I have in my tfrecord).
I'm at a complete loss with this one.
EDIT:
It seems this "error" I was getting was nothing but a warning message. I suspect it is related to the use of TensorFlow Hub and potentially eager execution. I added
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
at the beginning of the file and the warning has vanished.
Answer
It looks like you've made great progress with your code. The issue you mentioned, particularly the warning about "undefined input shape," seems to be related to the input shape of your feature_extractor_layer
(which uses TensorFlow Hub) not being fully defined at the point of instantiation.
Here are a few things you could try to address this:
1. Verify the input shape of the feature extractor:
Make sure that the input shape you pass to the KerasLayer
corresponds to the shape of your input images. In your code, you define the input shape as IMAGE_SHAPE + (3,)
, but it’s unclear if IMAGE_SHAPE
is set correctly. Ensure that IMAGE_SHAPE
is a tuple like (height, width)
that matches the dimensions of the images in your TFRecord (without including the channel dimension 3
for RGB). For example, if your images are 128x128, set:
IMAGE_SHAPE = (128, 128) # Set the correct height and width
and update:
feature_extractor_layer = hub.KerasLayer(url, input_shape=IMAGE_SHAPE + (3,))
2. Check for Eager Execution Issues:
TensorFlow Hub layers sometimes have trouble with static input shapes, especially in the context of eager execution. You can try setting the model input shape explicitly to ensure that the feature extractor knows its shape:
feature_extractor_layer = hub.KerasLayer(url, input_shape=(None, IMAGE_SHAPE[0], IMAGE_SHAPE[1], 3))
This forces TensorFlow to expect images with the specified dimensions but allows for dynamic batch sizes (None
).
3. Set up the correct labels format:
Since you're using categorical_crossentropy
as the loss function, make sure your labels are in one-hot encoded format. If they aren't, you can convert them like this:
label_batch = tf.one_hot(label_batch, len(CLASS_NAMES))
If your labels are integers (which they seem to be in the TFRecord file), you should one-hot encode them to match the output of the final Dense
layer with softmax
activation.
4. Ensure your STEPS_PER_EPOCH
is set correctly:
In the code snippet you provided, you compute STEPS_PER_EPOCH
using image_count
:
STEPS_PER_EPOCH = np.ceil(image_count / BATCH_SIZE)
Make sure image_count
correctly represents the total number of images in your dataset. You can compute it by reading the total number of records in your TFRecord:
image_count = sum(1 for _ in tf.data.TFRecordDataset(files))
This ensures that the number of steps per epoch is correct.
5. Debugging Tips:
- To debug, print out the shapes of
image_batch
andlabel_batch
before passing them tomodel.fit()
:print(image_batch.shape) print(label_batch.shape)
This can help verify that the input shapes align with what the model expects.
Let me know if you run into further issues or need more clarification!