I have a generator in producing my TensorFlow data, as triplets (anchor, positive, negative), in batches. Each batch is a list of such triplets, making up the list labels. Using code from Moindrot's blog on triplet loss we get a mask for positives and negatives: With
labels_equal = tf.equal(tf.expand_dims(labels, 0), tf.expand_dims(labels, 1))
mask = tf.logical_not(labels_equal)
we get a mask of negatives (non-equal labels). If I have labels=[1,1,2,3,3,4] the mask will be:
[[ F F T T T T ],
[ F F T T T T ],
[ T T F T T T ],
[ T T T F F T ],
[ T T T F F T ],
[ T T T T T F ]]
The labels are set up such that in groups of 3 they form anchor, positive and negative (triplet).
I'm trying to implement a negative mask for balanced triplets. I.e. a mask where only the negative to its anchor is True for each line. How do I find this negative mask such that only the anchor-negative entries percolate through?
Expected output for labels=[1,1,2,3,3,4]:
[[ F F T F F F ],
[ F F F F F F ],
[ T F F F F F ],
[ F F F F F T ],
[ F F F F F F ],
[ F F F T F F ]]
Notes:
- I have tried to use tfa's triplet loss, but it's not balanced (bad for testing accuracy, recall, etc.).
- Labels have been relabeled, such that the labels of each triplet is batch-unique and won't match other triplets in the batch.
CodePudding user response:
If you really only want anchor-negative True in the negative mask, you can accomplish this with a mix of tf.tile, tf.linalg.band_part and tf.transpose:
labels_equal = tf.equal(tf.expand_dims(labels, 0), tf.expand_dims(labels, 1))
mask = tf.logical_not(labels_equal)
triplet_mask = tf.tile(
[
[False, False, True],
[False, False, False],
[False, False, False],
],
multiples=[d // 3 for d in mask.shape],
)
triplet_mask = tf.linalg.band_part(triplet_mask, 0, 2)
triplet_mask = triplet_mask | tf.transpose(triplet_mask)
I would suggest, however, you rather change the model to accept a list of triplets. It simplifies both training and testing.
