Search code examples
pythontensorflowkerasconv-neural-networktensorflow-datasets

Using my own Images for TripletSemiHardLoss from Keras addons


My image folder is set up as one main folder with 130 separate folders, each folder with its own images folder_with_130_folders-
folder1_class1-
img_in_class1_folder.jpg
img_in_class1_folder.jpg
...
folder130_class130-
img_in_class130_folder.jpg
img_in_class130_folder.jpg


train_dataset =  prod_images.flow_from_directory(directory, target_size=(225, 225), class_mode='categorical', subset='training', save_format='jpg')
validation_set =  prod_images.flow_from_directory(directory, target_size=(225, 225), class_mode='categorical', subset='validation', save_format='jpg')

(x_train, y_train), (x_test, y_test) = train_dataset.next(), validation_set.next()

model = models.Sequential()
model.add(layers.Conv2D(filters=128, kernel_size=2, padding='same', activation='relu', input_shape=(225, 225, 3)))
model.add(layers.MaxPooling2D(pool_size=2))
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu'))
model.add(layers.MaxPooling2D(pool_size=2))
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
model.add(layers.MaxPooling2D(pool_size=2))
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(filters=16, kernel_size=2, padding='same', activation='relu'))
model.add(layers.MaxPooling2D(pool_size=2))
model.add(layers.Dropout(0.3))
model.add(layers.Flatten())
model.add(layers.Dense(256, activation=None)) # No activation on final dense layer
model.add(layers.Lambda(lambda x: tf.math.l2_normalize(x, axis=1))) # L2 normalize embeddings

model.summary()

model.compile(optimizer = tf.keras.optimizers.Adam(learning_rate=0.001), loss=tfa.losses.TripletSemiHardLoss())

model_fit = model.fit(train_dataset, steps_per_epoch=4, epochs=20, verbose=1, validation_data = validation_set)```

Solution

  • As stated in the docs regarding the tfa.losses.TripletSemiHardLoss:

    We expect labels y_true to be provided as 1-D integer Tensor with shape [batch_size] of multi-class integer labels. And embeddings y_pred must be 2-D float Tensor of l2 normalized embedding vectors

    You should, therefore, use sparse integer labels (sparse_categorical) instead of one-hot encoded labels (categorical). Here is a working example:

    import tensorflow as tf
    import tensorflow_addons as tfa
    import pathlib
    
    dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
    data_dir = tf.keras.utils.get_file('flower_photos', origin=dataset_url, untar=True)
    data_dir = pathlib.Path(data_dir)
    
    batch_size = 32
    
    train_ds = tf.keras.utils.image_dataset_from_directory(
      data_dir,
      validation_split=0.2,
      label_mode='int', # sparse categorical
      subset="training",
      seed=123,
      image_size=(225, 225),
      batch_size=batch_size)
    
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Conv2D(filters=128, kernel_size=2, padding='same', activation='relu', input_shape=(225, 225, 3)))
    model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.Conv2D(filters=16, kernel_size=2, padding='same', activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
    model.add(tf.keras.layers.Dropout(0.3))
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(256, activation=None)) # No activation on final dense layer
    model.add(tf.keras.layers.Lambda(lambda x: tf.math.l2_normalize(x, axis=-1))) # L2 normalize embeddings
    
    model.summary()
    
    model.compile(optimizer = tf.keras.optimizers.Adam(learning_rate=0.001), loss=tfa.losses.TripletSemiHardLoss())
    
    model_fit = model.fit(train_ds, epochs=5, verbose=1)
    

    In your case you have to set the parameter class_mode to sparse:

    flow_from_directory(directory, target_size=(225, 225), class_mode='sparse', subset='training', save_format='jpg')